diff --git a/actions/auth/auth.ts b/actions/auth/auth.ts index 4973e3f8e2..d5ec199b06 100644 --- a/actions/auth/auth.ts +++ b/actions/auth/auth.ts @@ -62,7 +62,7 @@ export const createNewUser = async ( const bodyData = { data: { - type: "User", + type: "users", attributes: { name: formData.name, email: formData.email, @@ -99,7 +99,7 @@ export const getToken = async (formData: z.infer) => { const bodyData = { data: { - type: "Token", + type: "tokens", attributes: { email: formData.email, password: formData.password, diff --git a/actions/compliance.ts b/actions/compliance.ts deleted file mode 100644 index 304b826077..0000000000 --- a/actions/compliance.ts +++ /dev/null @@ -1,39 +0,0 @@ -"use server"; - -import { revalidatePath } from "next/cache"; -import { redirect } from "next/navigation"; - -import { parseStringify } from "@/lib"; - -export const getCompliance = async ({ page = 1 }) => { - if (isNaN(Number(page)) || page < 1) redirect("/compliance"); - const keyServer = process.env.SITE_URL; - - try { - const compliance = await fetch( - `${keyServer}/api/compliance?page%5Bnumber%5D=${page}`, - ); - const data = await compliance.json(); - const parsedData = parseStringify(data); - revalidatePath("/compliance"); - return parsedData; - } catch (error) { - console.error("Error fetching Compliance:", error); - return undefined; - } -}; - -export const getErrorMessage = (error: unknown): string => { - let message: string; - - if (error instanceof Error) { - message = error.message; - } else if (error && typeof error === "object" && "message" in error) { - message = String(error.message); - } else if (typeof error === "string") { - message = error; - } else { - message = "Oops! Something went wrong."; - } - return message; -}; diff --git a/actions/compliances/compliances.ts b/actions/compliances/compliances.ts new file mode 100644 index 0000000000..e9c8814c31 --- /dev/null +++ b/actions/compliances/compliances.ts @@ -0,0 +1,43 @@ +"use server"; +import { revalidatePath } from "next/cache"; + +import { auth } from "@/auth.config"; +import { parseStringify } from "@/lib"; + +export const getCompliancesOverview = async ({ + scanId, + region, +}: { + scanId: string; + region?: string | string[]; +}) => { + const session = await auth(); + + const keyServer = process.env.API_BASE_URL; + const url = new URL(`${keyServer}/compliance-overviews`); + + if (scanId) url.searchParams.append("filter[scan_id]", scanId); + + if (region) { + const regionValue = Array.isArray(region) ? region.join(",") : region; + url.searchParams.append("filter[region__in]", regionValue); + } + + try { + const compliances = await fetch(url.toString(), { + headers: { + Accept: "application/vnd.api+json", + Authorization: `Bearer ${session?.accessToken}`, + }, + }); + const data = await compliances.json(); + const parsedData = parseStringify(data); + + revalidatePath("/compliance"); + return parsedData; + } catch (error) { + // eslint-disable-next-line no-console + console.error("Error fetching providers:", error); + return undefined; + } +}; diff --git a/actions/compliances/index.ts b/actions/compliances/index.ts new file mode 100644 index 0000000000..ba9df7e860 --- /dev/null +++ b/actions/compliances/index.ts @@ -0,0 +1 @@ +export * from "./compliances"; diff --git a/actions/invitations/index.ts b/actions/invitations/index.ts new file mode 100644 index 0000000000..addd041884 --- /dev/null +++ b/actions/invitations/index.ts @@ -0,0 +1 @@ +export * from "./invitation"; diff --git a/actions/invitations/invitation.ts b/actions/invitations/invitation.ts index 7fc99ce33c..56aefc74e1 100644 --- a/actions/invitations/invitation.ts +++ b/actions/invitations/invitation.ts @@ -42,6 +42,7 @@ export const getInvitations = async ({ revalidatePath("/invitations"); return parsedData; } catch (error) { + // eslint-disable-next-line no-console console.error("Error fetching invitations:", error); return undefined; } @@ -56,7 +57,7 @@ export const sendInvite = async (formData: FormData) => { const body = JSON.stringify({ data: { - type: "Invitation", + type: "invitations", attributes: { email, }, @@ -104,7 +105,7 @@ export const updateInvite = async (formData: FormData) => { }, body: JSON.stringify({ data: { - type: "Invitation", + type: "invitations", id: invitationId, attributes: { email: invitationEmail, @@ -117,6 +118,7 @@ export const updateInvite = async (formData: FormData) => { revalidatePath("/invitations"); return parseStringify(data); } catch (error) { + // eslint-disable-next-line no-console console.error("Error updating invitation:", error); return { error: getErrorMessage(error), diff --git a/actions/providers/providers.ts b/actions/providers/providers.ts index 29a8a114c7..fc5cd97bb7 100644 --- a/actions/providers/providers.ts +++ b/actions/providers/providers.ts @@ -90,7 +90,7 @@ export const updateProvider = async (formData: FormData) => { }, body: JSON.stringify({ data: { - type: "Provider", + type: "providers", id: providerId, attributes: { alias: providerAlias, @@ -129,7 +129,7 @@ export const addProvider = async (formData: FormData) => { }, body: JSON.stringify({ data: { - type: "Provider", + type: "providers", attributes: { provider: providerType, uid: providerUid, @@ -209,7 +209,7 @@ export const addCredentialsProvider = async (formData: FormData) => { const bodyData = { data: { - type: "ProviderSecret", + type: "provider-secrets", attributes: { secret_type: secretType, secret, @@ -219,7 +219,7 @@ export const addCredentialsProvider = async (formData: FormData) => { provider: { data: { id: providerId, - type: "Provider", + type: "providers", }, }, }, diff --git a/actions/scans/scans.ts b/actions/scans/scans.ts index 866918cf86..367446daaa 100644 --- a/actions/scans/scans.ts +++ b/actions/scans/scans.ts @@ -91,14 +91,14 @@ export const scanOnDemand = async (formData: FormData) => { }, body: JSON.stringify({ data: { - type: "Scan", + type: "scans", attributes: { name: scanName, }, relationships: { provider: { data: { - type: "Provider", + type: "providers", id: providerId, }, }, @@ -136,7 +136,7 @@ export const updateScan = async (formData: FormData) => { }, body: JSON.stringify({ data: { - type: "Scan", + type: "scans", id: scanId, attributes: { name: scanName, diff --git a/actions/users/users.ts b/actions/users/users.ts index 903d0be120..30a5b26d1f 100644 --- a/actions/users/users.ts +++ b/actions/users/users.ts @@ -69,7 +69,7 @@ export const updateUser = async (formData: FormData) => { }, body: JSON.stringify({ data: { - type: "User", + type: "users", id: userId, attributes: { name: userName, diff --git a/app/(prowler)/compliance/page.tsx b/app/(prowler)/compliance/page.tsx index 6cc1c5c11e..08368c69da 100644 --- a/app/(prowler)/compliance/page.tsx +++ b/app/(prowler)/compliance/page.tsx @@ -1,30 +1,55 @@ import { Spacer } from "@nextui-org/react"; -import { redirect } from "next/navigation"; import { Suspense } from "react"; -import { getCompliance } from "@/actions/compliance"; +import { getCompliancesOverview } from "@/actions/compliances"; +import { getScans } from "@/actions/scans"; import { ComplianceCard, ComplianceSkeletonGrid, } from "@/components/compliance"; -import { FilterControls } from "@/components/filters"; +import { DataCompliance } from "@/components/compliance/data-compliance"; import { Header } from "@/components/ui"; -import { SearchParamsProps } from "@/types"; +import { ComplianceOverviewData, SearchParamsProps } from "@/types"; export default async function Compliance({ searchParams, }: { searchParams: SearchParamsProps; }) { - const searchParamsKey = JSON.stringify(searchParams || {}); + const scansData = await getScans({}); + const scanList = scansData?.data.map((scan: any) => ({ + id: scan.id, + name: scan.attributes.name || "Unnamed Scan", + state: scan.attributes.state, + progress: scan.attributes.progress, + })); + + const selectedScanId = searchParams.scanId || scanList[0]?.id; + + // Fetch compliance data for regions + const compliancesData = await getCompliancesOverview({ + scanId: selectedScanId, + }); + + // Extract unique regions + const regions = compliancesData?.data + ? Array.from( + new Set( + compliancesData.data.map( + (compliance: ComplianceOverviewData) => + compliance.attributes.region as string, + ), + ), + ) + : []; return ( <>
- - - }> + + + }> @@ -36,28 +61,56 @@ const SSRComplianceGrid = async ({ }: { searchParams: SearchParamsProps; }) => { - const page = parseInt(searchParams.page?.toString() || "1", 10); - const compliancesData = await getCompliance({ page }); - const [compliances] = await Promise.all([compliancesData]); + const scanId = searchParams.scanId?.toString() || ""; - if (compliances?.errors) redirect("/compliance"); + const regionFilter = searchParams["filter[region__in]"]?.toString() || ""; + + // Fetch compliance data + const compliancesData = await getCompliancesOverview({ + scanId, + region: regionFilter, + }); + + // Check if the response contains no data + if (!compliancesData || compliancesData?.data?.length === 0) { + return ( +
+
+ No compliance data available for the selected scan. +
+
+ ); + } + + // Handle errors returned by the API + if (compliancesData?.errors?.length > 0) { + return ( +
+
Provide a valid scan ID.
+
+ ); + } return ( -
- {compliances.compliance?.data.map((compliance: any) => ( - - ))} +
+ {compliancesData.data.map((compliance: ComplianceOverviewData) => { + const { attributes } = compliance; + const { + framework, + requirements_status: { passed, total }, + } = attributes; + + return ( + + ); + })}
); }; diff --git a/app/api/compliance/route.ts b/app/api/compliance/route.ts deleted file mode 100644 index b2e4a01d9f..0000000000 --- a/app/api/compliance/route.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { NextResponse } from "next/server"; - -import data from "../../../dataCompliance.json"; - -export async function GET() { - // Simulate fetching data with a delay - await new Promise((resolve) => setTimeout(resolve, 2000)); - - return NextResponse.json({ compliance: data }); -} diff --git a/auth.config.ts b/auth.config.ts index 4639f8bf37..56c44a14e5 100644 --- a/auth.config.ts +++ b/auth.config.ts @@ -16,7 +16,7 @@ const refreshAccessToken = async (token: JwtPayload) => { const bodyData = { data: { - type: "TokenRefresh", + type: "tokens-refresh", attributes: { refresh: (token as any).refreshToken, }, diff --git a/components/compliance/ComplianceCard.tsx b/components/compliance/compliance-card.tsx similarity index 100% rename from components/compliance/ComplianceCard.tsx rename to components/compliance/compliance-card.tsx diff --git a/components/compliance/ComplianceSkeletonGrid.tsx b/components/compliance/compliance-skeleton-grid.tsx similarity index 100% rename from components/compliance/ComplianceSkeletonGrid.tsx rename to components/compliance/compliance-skeleton-grid.tsx diff --git a/components/compliance/data-compliance/data-compliance.tsx b/components/compliance/data-compliance/data-compliance.tsx new file mode 100644 index 0000000000..10fcc44f6b --- /dev/null +++ b/components/compliance/data-compliance/data-compliance.tsx @@ -0,0 +1,90 @@ +"use client"; + +import { useRouter, useSearchParams } from "next/navigation"; +import { useCallback, useEffect, useState } from "react"; + +import { SelectScanComplianceData } from "@/components/compliance/data-compliance"; +import { CrossIcon } from "@/components/icons"; +import { CustomButton, CustomDropdownFilter } from "@/components/ui/custom"; + +interface DataComplianceProps { + scans: { id: string; name: string; state: string; progress: number }[]; + regions: string[]; +} + +export const DataCompliance = ({ scans, regions }: DataComplianceProps) => { + const router = useRouter(); + const searchParams = useSearchParams(); + const [showClearButton, setShowClearButton] = useState(false); + const scanIdParam = searchParams.get("scanId"); + const selectedScanId = scanIdParam || scans[0]?.id; + + useEffect(() => { + const hasFilters = Array.from(searchParams.keys()).some( + (key) => key.startsWith("filter[") || key === "sort", + ); + setShowClearButton(hasFilters); + }, [searchParams]); + const handleScanChange = (selectedKey: string) => { + const params = new URLSearchParams(searchParams); + params.set("scanId", selectedKey); + router.push(`?${params.toString()}`); + }; + + const pushDropdownFilter = useCallback( + (key: string, values: string[]) => { + const params = new URLSearchParams(searchParams); + const filterKey = `filter[${key}]`; + + if (values.length === 0) { + params.delete(filterKey); + } else { + params.set(filterKey, values.join(",")); + } + + router.push(`?${params.toString()}`); + }, + [router, searchParams], + ); + const clearAllFilters = useCallback(() => { + const params = new URLSearchParams(searchParams.toString()); + Array.from(params.keys()).forEach((key) => { + if (key.startsWith("filter[") || key === "sort") { + params.delete(key); + } + }); + router.push(`?${params.toString()}`, { scroll: false }); + }, [router, searchParams]); + return ( +
+
+ + + {showClearButton && ( + } + radius="sm" + > + Reset + + )} +
+
+ ); +}; diff --git a/components/compliance/data-compliance/index.ts b/components/compliance/data-compliance/index.ts new file mode 100644 index 0000000000..14f430df04 --- /dev/null +++ b/components/compliance/data-compliance/index.ts @@ -0,0 +1,2 @@ +export * from "./data-compliance"; +export * from "./select-scan-compliance-data"; diff --git a/components/compliance/data-compliance/select-scan-compliance-data.tsx b/components/compliance/data-compliance/select-scan-compliance-data.tsx new file mode 100644 index 0000000000..5ebc36515d --- /dev/null +++ b/components/compliance/data-compliance/select-scan-compliance-data.tsx @@ -0,0 +1,50 @@ +import { Select, SelectItem } from "@nextui-org/react"; + +interface SelectScanComplianceDataProps { + scans: { id: string; name: string; state: string; progress: number }[]; + selectedScanId: string; + onSelectionChange: (selectedKey: string) => void; +} + +export const SelectScanComplianceData = ({ + scans, + selectedScanId, + onSelectionChange, +}: SelectScanComplianceDataProps) => { + return ( + + ); +}; diff --git a/components/compliance/index.ts b/components/compliance/index.ts index 6f025f2ca5..bb1b06d995 100644 --- a/components/compliance/index.ts +++ b/components/compliance/index.ts @@ -1,2 +1,2 @@ -export * from "./ComplianceCard"; -export * from "./ComplianceSkeletonGrid"; +export * from "./compliance-card"; +export * from "./compliance-skeleton-grid"; diff --git a/dataCompliance.json b/dataCompliance.json deleted file mode 100644 index a1bdd62831..0000000000 --- a/dataCompliance.json +++ /dev/null @@ -1,438 +0,0 @@ -{ - "links": { - "first": "http://localhost:8080/api/v1/compliance?page%5Bnumber%5D=1", - "last": "http://localhost:8080/api/v1/compliance?page%5Bnumber%5D=1", - "next": null, - "prev": null - }, - "data": [ - { - "id": "001", - "attributes": { - "title": "AWS Account Security Onboarding", - "passingRequirements": 28, - "totalRequirements": 83, - "regions": ["us-east-1", "us-east-2"] - }, - "lastScan": { - "attributes": { - "passingRequirements": 28, - "totalRequirements": 83 - } - } - }, - { - "id": "002", - "attributes": { - "title": "AWS Audit Manager Control Tower Guardrails", - "passingRequirements": 10, - "totalRequirements": 14, - "regions": ["us-west-1", "us-west-2"] - }, - "lastScan": { - "attributes": { - "passingRequirements": 14, - "totalRequirements": 14 - } - } - }, - { - "id": "003", - "attributes": { - "title": "AWS Foundational Security Best Practices", - "passingRequirements": 22, - "totalRequirements": 37, - "regions": ["eu-west-1", "eu-west-2"] - }, - "lastScan": { - "attributes": { - "passingRequirements": 22, - "totalRequirements": 37 - } - } - }, - { - "id": "004", - "attributes": { - "title": "AWS Foundational Technical Review", - "passingRequirements": 3, - "totalRequirements": 45, - "regions": ["ap-southeast-1", "ap-southeast-2"] - }, - "lastScan": { - "attributes": { - "passingRequirements": 25, - "totalRequirements": 45 - } - } - }, - { - "id": "005", - "attributes": { - "title": "AWS Well Architected Framework Reliability Pillar", - "passingRequirements": 2, - "totalRequirements": 3, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 0, - "totalRequirements": 3 - } - } - }, - { - "id": "006", - "attributes": { - "title": "AWS Well Architected Framework Security Pillar", - "passingRequirements": 19, - "totalRequirements": 57, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 24, - "totalRequirements": 57 - } - } - }, - { - "id": "007", - "attributes": { - "title": "CIS 1.4", - "passingRequirements": 43, - "totalRequirements": 58, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 40, - "totalRequirements": 58 - } - } - }, - { - "id": "008", - "attributes": { - "title": "CIS 1.5", - "passingRequirements": 48, - "totalRequirements": 63, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 50, - "totalRequirements": 63 - } - } - }, - { - "id": "009", - "attributes": { - "title": "CIS 2.0", - "passingRequirements": 47, - "totalRequirements": 64, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 64, - "totalRequirements": 64 - } - } - }, - { - "id": "010", - "attributes": { - "title": "CIS 3.0", - "passingRequirements": 45, - "totalRequirements": 62, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 48, - "totalRequirements": 62 - } - } - }, - { - "id": "011", - "attributes": { - "title": "CISA", - "passingRequirements": 4, - "totalRequirements": 16, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 4, - "totalRequirements": 16 - } - } - }, - { - "id": "012", - "attributes": { - "title": "ENS RD2022 Categoría ALTA", - "passingRequirements": 89, - "totalRequirements": 189, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 82, - "totalRequirements": 189 - } - } - }, - { - "id": "013", - "attributes": { - "title": "FFIEC", - "passingRequirements": 8, - "totalRequirements": 44, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 40, - "totalRequirements": 44 - } - } - }, - { - "id": "014", - "attributes": { - "title": "FedRAMP Low Revision 4", - "passingRequirements": 4, - "totalRequirements": 18, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 4, - "totalRequirements": 18 - } - } - }, - { - "id": "015", - "attributes": { - "title": "FedRamp Moderate Revision 4", - "passingRequirements": 11, - "totalRequirements": 64, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 28, - "totalRequirements": 64 - } - } - }, - { - "id": "016", - "attributes": { - "title": "GDPR", - "passingRequirements": 0, - "totalRequirements": 3, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 0, - "totalRequirements": 3 - } - } - }, - { - "id": "017", - "attributes": { - "title": "GxP 21 CFR Part 11", - "passingRequirements": 1, - "totalRequirements": 11, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 11, - "totalRequirements": 11 - } - } - }, - { - "id": "018", - "attributes": { - "title": "GxP EU Annex 11", - "passingRequirements": 5, - "totalRequirements": 14, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 5, - "totalRequirements": 14 - } - } - }, - { - "id": "019", - "attributes": { - "title": "HIPAA", - "passingRequirements": 2, - "totalRequirements": 32, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 9, - "totalRequirements": 32 - } - } - }, - { - "id": "020", - "attributes": { - "title": "ISO27001 2013", - "passingRequirements": 55, - "totalRequirements": 79, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 66, - "totalRequirements": 79 - } - } - }, - { - "id": "021", - "attributes": { - "title": "MITRE ATTACK", - "passingRequirements": 7, - "totalRequirements": 46, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 19, - "totalRequirements": 46 - } - } - }, - { - "id": "022", - "attributes": { - "title": "NIST 800 171 Revision 2", - "passingRequirements": 3, - "totalRequirements": 50, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 3, - "totalRequirements": 50 - } - } - }, - { - "id": "023", - "attributes": { - "title": "NIST 800 53 Revision 4", - "passingRequirements": 21, - "totalRequirements": 64, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 21, - "totalRequirements": 64 - } - } - }, - { - "id": "024", - "attributes": { - "title": "NIST 800 53 Revision 5", - "passingRequirements": 75, - "totalRequirements": 288, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 75, - "totalRequirements": 288 - } - } - }, - { - "id": "025", - "attributes": { - "title": "NIST CSF 1.1", - "passingRequirements": 15, - "totalRequirements": 56, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 46, - "totalRequirements": 56 - } - } - }, - { - "id": "026", - "attributes": { - "title": "PCI 3.2.1", - "passingRequirements": 11, - "totalRequirements": 19, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 19, - "totalRequirements": 19 - } - } - }, - { - "id": "027", - "attributes": { - "title": "RBI Cyber Security Framework", - "passingRequirements": 3, - "totalRequirements": 9, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 3, - "totalRequirements": 9 - } - } - }, - { - "id": "028", - "attributes": { - "title": "SOC2", - "passingRequirements": 7, - "totalRequirements": 56, - "regions": [] - }, - "lastScan": { - "attributes": { - "passingRequirements": 7, - "totalRequirements": 56 - } - } - } - ], - "meta": { - "pagination": { - "page": 1, - "pages": 1, - "count": 28 - }, - "version": "v1" - } -} diff --git a/types/components.ts b/types/components.ts index 2299d0bffe..7ec383d408 100644 --- a/types/components.ts +++ b/types/components.ts @@ -100,9 +100,56 @@ export interface ApiError { }; code: string; } +export interface CompliancesOverview { + links: { + first: string; + last: string; + next: string | null; + prev: string | null; + }; + data: ComplianceOverviewData[]; + meta: { + pagination: { + page: number; + pages: number; + count: number; + }; + version: string; + }; +} + +export interface ComplianceOverviewData { + type: "compliance-overviews"; + id: string; + attributes: { + inserted_at: string; + compliance_id: string; + framework: string; + version: string; + requirements_status: { + passed: number; + failed: number; + manual: number; + total: number; + }; + region: string; + provider_type: string; + }; + relationships: { + scan: { + data: { + type: "scans"; + id: string; + }; + }; + }; + links: { + self: string; + }; +} export interface InvitationProps { - type: "Invitation"; + type: "invitations"; id: string; attributes: { inserted_at: string; @@ -115,7 +162,7 @@ export interface InvitationProps { relationships: { inviter: { data: { - type: "User"; + type: "users"; id: string; }; }; @@ -126,7 +173,7 @@ export interface InvitationProps { } export interface UserProps { - type: "User"; + type: "users"; id: string; attributes: { name: string; @@ -140,7 +187,7 @@ export interface UserProps { count: number; }; data: Array<{ - type: "Membership"; + type: "memberships"; id: string; }>; }; @@ -175,7 +222,7 @@ export interface ProviderProps { } export interface ScanProps { - type: "Scan"; + type: "scans"; id: string; attributes: { name: string; @@ -203,20 +250,20 @@ export interface ScanProps { provider: { data: { id: string; - type: "Provider"; + type: "providers"; }; }; task: { data: { id: string; - type: "Task"; + type: "tasks"; }; }; }; } export interface FindingProps { - type: "Finding"; + type: "findings"; id: string; attributes: { uid: string; @@ -264,13 +311,13 @@ export interface FindingProps { relationships: { resources: { data: { - type: "Resource"; + type: "resources"; id: string; }[]; }; scan: { data: { - type: "Scan"; + type: "scans"; id: string; }; attributes: { @@ -290,7 +337,7 @@ export interface FindingProps { }; resource: { data: { - type: "Resource"; + type: "resources"; id: string; }[]; id: string; @@ -307,7 +354,7 @@ export interface FindingProps { relationships: { provider: { data: { - type: "Provider"; + type: "providers"; id: string; }; }; @@ -316,7 +363,7 @@ export interface FindingProps { count: number; }; data: { - type: "Finding"; + type: "findings"; id: string; }[]; }; @@ -327,7 +374,7 @@ export interface FindingProps { }; provider: { data: { - type: "Provider"; + type: "providers"; id: string; }; attributes: { @@ -344,7 +391,7 @@ export interface FindingProps { relationships: { secret: { data: { - type: "ProviderSecret"; + type: "provider-secrets"; id: string; }; };