From f3a000e712617e5e29ef3e8d531ee2fdf3354501 Mon Sep 17 00:00:00 2001 From: Stefan Buck Date: Wed, 11 Sep 2024 10:20:03 +0200 Subject: [PATCH] fix: load all issues and not just the first 100 (#204) --- src/api/SnykApiClient.ts | 44 ++++++++---- .../SnykOverviewComponent.tsx | 2 +- .../SnykEntityComponent/SnykTab.tsx | 6 +- .../components/SnykIssuesComponent.tsx | 8 ++- src/types/unifiedIssuesTypes.ts | 68 +------------------ 5 files changed, 41 insertions(+), 87 deletions(-) diff --git a/src/api/SnykApiClient.ts b/src/api/SnykApiClient.ts index e82bc2c..c52f549 100644 --- a/src/api/SnykApiClient.ts +++ b/src/api/SnykApiClient.ts @@ -24,7 +24,7 @@ import { Entity } from "@backstage/catalog-model"; import { mockedDepGraphs } from "../utils/mockedDepGraphs"; import { mockedProjectDetails } from "../utils/mockedProjectDetails"; import { IssuesCount } from "../types/types"; -import { Issue } from "../types/unifiedIssuesTypes"; +import { Issue, UnifiedIssues } from "../types/unifiedIssuesTypes"; const pkg = require('../../package.json'); const DEFAULT_PROXY_PATH_BASE = ""; @@ -42,7 +42,7 @@ export const snykApiRef: ApiRef = createApiRef({ }); export interface SnykApi { - listAllAggregatedIssues(orgName: string, projectId: string): Promise; + listAllAggregatedIssues(orgName: string, projectId: string): Promise; getProjectDetails(orgName: string, projectId: string): Promise; @@ -232,26 +232,40 @@ export class SnykApiClient implements SnykApi { }; }; - async listAllAggregatedIssues(orgId: string, projectId: string) { + async listAllAggregatedIssues(orgId: string, projectId: string): Promise { if (this.isMocked()) { await new Promise((resolve) => setTimeout(resolve, 500)); return mockedIssues[projectId]; } - const backendBaseUrl = await this.getApiUrl(); - const v3Headers = this.headers; + const fetchPage = async (url: string): Promise => { + const backendBaseUrl = await this.getApiUrl(); + const v3Headers = this.headers; + v3Headers["Content-Type"] = "application/vnd.api+json"; + + const response = await this.fetch(`${backendBaseUrl}${url}`, "GET"); + + if (response.status >= 400 && response.status < 600) { + throw new Error(`Error ${response.status} - Failed fetching Vuln Issues snyk data`); + } + + const json = await response.json(); + + const currentIssues = json?.data || []; + + if (json?.links?.next) { + const nextPageIssues = await fetchPage(json.links.next); + return [...currentIssues, ...nextPageIssues]; + } + + return currentIssues; + }; + const version = this.getSnykIssuesApiVersion(); - v3Headers["Content-Type"] = "application/vnd.api+json"; - const apiUrl = `${backendBaseUrl}/rest/orgs/${orgId}/issues?version=${version}&scan_item.id=${projectId}&scan_item.type=project&limit=100`; - const response = await this.fetch(`${apiUrl}`, "GET"); + const initialApiUrl = `/rest/orgs/${orgId}/issues?version=${version}&scan_item.id=${projectId}&scan_item.type=project&limit=100`; - if (response.status >= 400 && response.status < 600) { - throw new Error( - `Error ${response.status} - Failed fetching Vuln Issues snyk data` - ); - } - return response.json(); - } + return await fetchPage(initialApiUrl); + } async getProjectDetails(orgName: string, projectId: string) { if (this.isMocked()) { diff --git a/src/components/SnykEntityComponent/SnykOverviewComponent.tsx b/src/components/SnykEntityComponent/SnykOverviewComponent.tsx index 74e2f66..e8ae578 100644 --- a/src/components/SnykEntityComponent/SnykOverviewComponent.tsx +++ b/src/components/SnykEntityComponent/SnykOverviewComponent.tsx @@ -108,7 +108,7 @@ export const SnykOverviewComponent = ({ entity }: { entity: Entity }) => { ); const currentProjectIssuesCount = snykApi.getIssuesCount( - vulnsIssues.data + vulnsIssues ); aggregatedIssuesCount.critical += currentProjectIssuesCount.critical; aggregatedIssuesCount.high += currentProjectIssuesCount.high; diff --git a/src/components/SnykEntityComponent/SnykTab.tsx b/src/components/SnykEntityComponent/SnykTab.tsx index 07eef6f..0e84787 100644 --- a/src/components/SnykEntityComponent/SnykTab.tsx +++ b/src/components/SnykEntityComponent/SnykTab.tsx @@ -40,13 +40,13 @@ export const generateSnykTabForProject = ( orgId, projectId ); - const genericIssues: Array = allIssues.data.filter((issue) => + const genericIssues: Array = allIssues.filter((issue) => genericIssuesTypeArray.includes(issue.attributes.type) ); - const licenseIssues: Array = allIssues.data.filter( + const licenseIssues: Array = allIssues.filter( (issue) => issue.attributes.type === "license" ); - const ignoredIssues: Array = allIssues.data.filter( + const ignoredIssues: Array = allIssues.filter( (issue) => issue.attributes.ignored === true && issue.attributes.status != IssueAttributesStatusEnum.Resolved diff --git a/src/components/SnykEntityComponent/components/SnykIssuesComponent.tsx b/src/components/SnykEntityComponent/components/SnykIssuesComponent.tsx index a31e427..bce767e 100644 --- a/src/components/SnykEntityComponent/components/SnykIssuesComponent.tsx +++ b/src/components/SnykEntityComponent/components/SnykIssuesComponent.tsx @@ -78,7 +78,13 @@ export const IssuesTable: FC = ({ issues, pageUrl }) => { return ( diff --git a/src/types/unifiedIssuesTypes.ts b/src/types/unifiedIssuesTypes.ts index af3f26a..1430b5f 100644 --- a/src/types/unifiedIssuesTypes.ts +++ b/src/types/unifiedIssuesTypes.ts @@ -709,70 +709,4 @@ export interface Issue { type: IssueType; } -export interface JsonApi { - /** - * Version of the JSON API specification this server supports. - * @type {string} - * @memberof JsonApi - */ - version: string; -} -export interface LinkProperty {} -/** - * - * @export - * @interface PaginatedLinks - */ -export interface PaginatedLinks { - /** - * - * @type {LinkProperty} - * @memberof PaginatedLinks - */ - first?: LinkProperty; - /** - * - * @type {LinkProperty} - * @memberof PaginatedLinks - */ - last?: LinkProperty; - /** - * - * @type {LinkProperty} - * @memberof PaginatedLinks - */ - next?: LinkProperty; - /** - * - * @type {LinkProperty} - * @memberof PaginatedLinks - */ - prev?: LinkProperty; - /** - * - * @type {LinkProperty} - * @memberof PaginatedLinks - */ - self?: LinkProperty; -} - -export interface UnifiedIssues { - /** - * - * @type {Array} - * @memberof InlineResponse2003 - */ - data: Array; - /** - * - * @type {JsonApi} - * @memberof InlineResponse2003 - */ - jsonapi: JsonApi; - /** - * - * @type {PaginatedLinks} - * @memberof InlineResponse2003 - */ - links?: PaginatedLinks; -} +export type UnifiedIssues = Array;