diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e84153b5..7052a146a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Added + +- Added business owner and security owner fields to vendors + +### Changed + +- Improved vendor detail page with organized sections + - Split information into logical sections (Basic Information, Ownership, Risk & Service, Documentation) + - Better visual organization of vendor information + ## [0.8.0] - 2025-04-12 ### Added diff --git a/apps/console/src/pages/organizations/Routes.tsx b/apps/console/src/pages/organizations/Routes.tsx index a0741ca67..81b9cb27b 100644 --- a/apps/console/src/pages/organizations/Routes.tsx +++ b/apps/console/src/pages/organizations/Routes.tsx @@ -26,7 +26,7 @@ import { EditRiskPage } from "./risks/EditRiskPage"; import { NewRiskPage } from "./risks/NewRiskPage"; import { ListRiskPage } from "./risks/ListRiskPage"; import ShowRiskView from "./risks/ShowRiskView"; -import { VendorListPage } from "./vendors/VendorListPage"; +import { ListVendorPage } from "./vendors/ListVendorPage"; import { VendorPage } from "./vendors/VendorPage"; export function OrganizationsRoutes() { @@ -37,7 +37,7 @@ export function OrganizationsRoutes() { } /> } /> } /> - } /> + } /> } /> } /> diff --git a/apps/console/src/pages/organizations/vendors/VendorListPage.tsx b/apps/console/src/pages/organizations/vendors/ListVendorPage.tsx similarity index 86% rename from apps/console/src/pages/organizations/vendors/VendorListPage.tsx rename to apps/console/src/pages/organizations/vendors/ListVendorPage.tsx index 8d9e3bb94..34b6c0d9c 100644 --- a/apps/console/src/pages/organizations/vendors/VendorListPage.tsx +++ b/apps/console/src/pages/organizations/vendors/ListVendorPage.tsx @@ -4,9 +4,9 @@ import { lazy } from "@probo/react-lazy"; import { useLocation } from "react-router"; import { ErrorBoundaryWithLocation } from "../ErrorBoundary"; -const VendorListView = lazy(() => import("./VendorListView")); +const VendorListView = lazy(() => import("./ListVendorView")); -export function VendorListViewSkeleton() { +export function ListVendorViewSkeleton() { return ( }> + }> diff --git a/apps/console/src/pages/organizations/vendors/VendorListView.tsx b/apps/console/src/pages/organizations/vendors/ListVendorView.tsx similarity index 89% rename from apps/console/src/pages/organizations/vendors/VendorListView.tsx rename to apps/console/src/pages/organizations/vendors/ListVendorView.tsx index 4ceaec9d8..57ae99b4e 100644 --- a/apps/console/src/pages/organizations/vendors/VendorListView.tsx +++ b/apps/console/src/pages/organizations/vendors/ListVendorView.tsx @@ -17,12 +17,12 @@ import { Link } from "react-router"; import Fuse from "fuse.js"; import { useToast } from "@/hooks/use-toast"; import { PageTemplate } from "@/components/PageTemplate"; -import { VendorListViewSkeleton } from "./VendorListPage"; -import { VendorListViewQuery as VendorListViewQueryType } from "./__generated__/VendorListViewQuery.graphql"; -import { VendorListViewCreateVendorMutation } from "./__generated__/VendorListViewCreateVendorMutation.graphql"; -import { VendorListViewDeleteVendorMutation } from "./__generated__/VendorListViewDeleteVendorMutation.graphql"; -import { VendorListViewPaginationQuery } from "./__generated__/VendorListViewPaginationQuery.graphql"; -import { VendorListView_vendors$key } from "./__generated__/VendorListView_vendors.graphql"; +import { ListVendorViewSkeleton } from "./ListVendorPage"; +import { ListVendorViewCreateVendorMutation } from "./__generated__/ListVendorViewCreateVendorMutation.graphql"; +import { ListVendorViewPaginationQuery } from "./__generated__/ListVendorViewPaginationQuery.graphql"; +import { ListVendorView_vendors$key } from "./__generated__/ListVendorView_vendors.graphql"; +import { ListVendorViewQuery } from "./__generated__/ListVendorViewQuery.graphql"; +import { ListVendorViewDeleteVendorMutation } from "./__generated__/ListVendorViewDeleteVendorMutation.graphql"; interface VendorData { name: string; @@ -44,8 +44,8 @@ interface VendorData { const ITEMS_PER_PAGE = 25; -const vendorListViewQuery = graphql` - query VendorListViewQuery( +const listVendorViewQuery = graphql` + query ListVendorViewQuery( $organizationId: ID! $first: Int $after: CursorKey @@ -55,15 +55,15 @@ const vendorListViewQuery = graphql` organization: node(id: $organizationId) { id - ...VendorListView_vendors + ...ListVendorView_vendors @arguments(first: $first, after: $after, last: $last, before: $before) } } `; const vendorListFragment = graphql` - fragment VendorListView_vendors on Organization - @refetchable(queryName: "VendorListViewPaginationQuery") + fragment ListVendorView_vendors on Organization + @refetchable(queryName: "ListVendorViewPaginationQuery") @argumentDefinitions( first: { type: "Int" } after: { type: "CursorKey" } @@ -99,7 +99,7 @@ const vendorListFragment = graphql` `; const createVendorMutation = graphql` - mutation VendorListViewCreateVendorMutation( + mutation ListVendorViewCreateVendorMutation( $input: CreateVendorInput! $connections: [ID!]! ) { @@ -119,7 +119,7 @@ const createVendorMutation = graphql` `; const deleteVendorMutation = graphql` - mutation VendorListViewDeleteVendorMutation( + mutation ListVendorViewDeleteVendorMutation( $input: DeleteVendorInput! $connections: [ID!]! ) { @@ -183,14 +183,14 @@ function LoadBelowButton({ ); } -function VendorListContent({ +function ListVendorContent({ queryRef, }: { - queryRef: PreloadedQuery; + queryRef: PreloadedQuery; }) { const { toast } = useToast(); - const data = usePreloadedQuery( - vendorListViewQuery, + const data = usePreloadedQuery( + listVendorViewQuery, queryRef ); const [, setSearchParams] = useSearchParams(); @@ -200,9 +200,9 @@ function VendorListContent({ const [vendorsData, setVendorsData] = useState([]); const [isLoadingVendors, setIsLoadingVendors] = useState(false); const [createVendor] = - useMutation(createVendorMutation); + useMutation(createVendorMutation); const [deleteVendor] = - useMutation(deleteVendorMutation); + useMutation(deleteVendorMutation); const { organizationId } = useParams(); useEffect(() => { @@ -239,8 +239,8 @@ function VendorListContent({ isLoadingNext, isLoadingPrevious, } = usePaginationFragment< - VendorListViewPaginationQuery, - VendorListView_vendors$key + ListVendorViewPaginationQuery, + ListVendorView_vendors$key >(vendorListFragment, data.organization); const vendors = @@ -390,14 +390,6 @@ function VendorListContent({

{vendor?.name}

- {vendor?.description && ( - <> - • -

- {vendor.description} -

- - )}
@@ -484,10 +476,10 @@ function VendorListContent({ ); } -export default function VendorListView() { +export default function ListVendorView() { const [searchParams] = useSearchParams(); const [queryRef, loadQuery] = - useQueryLoader(vendorListViewQuery); + useQueryLoader(listVendorViewQuery); const { organizationId } = useParams(); @@ -505,12 +497,12 @@ export default function VendorListView() { }, [loadQuery, organizationId]); if (!queryRef) { - return ; + return ; } return ( - }> - + }> + ); } diff --git a/apps/console/src/pages/organizations/vendors/VendorView.tsx b/apps/console/src/pages/organizations/vendors/VendorView.tsx index ba808a95d..b63f13ebe 100644 --- a/apps/console/src/pages/organizations/vendors/VendorView.tsx +++ b/apps/console/src/pages/organizations/vendors/VendorView.tsx @@ -5,7 +5,7 @@ import { Label } from "@/components/ui/label"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { useToast } from "@/hooks/use-toast"; -import { HelpCircle, X } from "lucide-react"; +import { HelpCircle, X, User } from "lucide-react"; import { graphql, PreloadedQuery, @@ -23,9 +23,10 @@ import { useParams } from "react-router"; import { cn } from "@/lib/utils"; import { PageTemplate } from "@/components/PageTemplate"; import { VendorViewSkeleton } from "./VendorPage"; +import PeopleSelector from "@/components/PeopleSelector"; const vendorViewQuery = graphql` - query VendorViewQuery($vendorId: ID!) { + query VendorViewQuery($vendorId: ID!, $organizationId: ID!) { node(id: $vendorId) { ... on Vendor { id @@ -46,6 +47,14 @@ const vendorViewQuery = graphql` headquarterAddress legalName websiteUrl + businessOwner { + id + fullName + } + securityOwner { + id + fullName + } createdAt updatedAt complianceReports(first: 100) @@ -64,6 +73,9 @@ const vendorViewQuery = graphql` } } } + organization: node(id: $organizationId) { + ...PeopleSelector_organization + } } `; @@ -89,6 +101,14 @@ const updateVendorMutation = graphql` headquarterAddress legalName websiteUrl + businessOwner { + id + fullName + } + securityOwner { + id + fullName + } updatedAt } } @@ -133,12 +153,14 @@ function EditableField({ onChange, type = "text", helpText, + disabled = false, }: { label: string; value: string; onChange: (value: string) => void; type?: string; helpText?: string; + disabled?: boolean; }) { return (
@@ -151,6 +173,7 @@ function EditableField({ type={type} value={value} onChange={(e) => onChange(e.target.value)} + disabled={disabled} /> {helpText &&

{helpText}

}
@@ -348,6 +371,7 @@ function VendorViewContent({ }: { queryRef: PreloadedQuery; }) { + const { organizationId } = useParams(); const data = usePreloadedQuery(vendorViewQuery, queryRef); const [editedFields, setEditedFields] = useState>(new Set()); const [formData, setFormData] = useState({ @@ -368,6 +392,8 @@ function VendorViewContent({ headquarterAddress: data.node.headquarterAddress || "", legalName: data.node.legalName || "", websiteUrl: data.node.websiteUrl || "", + businessOwnerId: data.node.businessOwner?.id || null, + securityOwnerId: data.node.securityOwner?.id || null, }); const [updateVendor] = useMutation(updateVendorMutation); @@ -391,6 +417,8 @@ function VendorViewContent({ serviceTerminationAt: formData.serviceTerminationAt ? formatDateForAPI(formData.serviceTerminationAt) : null, + businessOwnerId: formData.businessOwnerId || undefined, + securityOwnerId: formData.securityOwnerId || undefined, }; updateVendor({ @@ -407,6 +435,7 @@ function VendorViewContent({ variant: "default", }); setEditedFields(new Set()); + loadQuery({ vendorId: data.node.id!, organizationId: organizationId! }); }, onError: (error) => { if (error.message?.includes("concurrent modification")) { @@ -417,7 +446,10 @@ function VendorViewContent({ variant: "destructive", }); - loadQuery({ vendorId: data.node.id! }); + loadQuery({ + vendorId: data.node.id!, + organizationId: organizationId!, + }); } else { toast({ title: "Error", @@ -427,7 +459,7 @@ function VendorViewContent({ } }, }); - }, [updateVendor, data.node.id, formData, loadQuery, toast]); + }, [updateVendor, data.node.id, formData, loadQuery, toast, organizationId]); const handleFieldChange = (field: keyof typeof formData, value: unknown) => { setFormData((prev) => ({ @@ -456,6 +488,8 @@ function VendorViewContent({ headquarterAddress: data.node.headquarterAddress || "", legalName: data.node.legalName || "", websiteUrl: data.node.websiteUrl || "", + businessOwnerId: data.node.businessOwner?.id || null, + securityOwnerId: data.node.securityOwner?.id || null, }); setEditedFields(new Set()); }; @@ -480,7 +514,10 @@ function VendorViewContent({ description: "Compliance report deleted successfully", variant: "default", }); - loadQuery({ vendorId: data.node.id! }); + loadQuery({ + vendorId: data.node.id!, + organizationId: organizationId!, + }); }, onError: (error) => { toast({ @@ -491,7 +528,13 @@ function VendorViewContent({ }, }); }, - [deleteVendorComplianceReport, data.node.id, loadQuery, toast] + [ + deleteVendorComplianceReport, + data.node.id, + loadQuery, + toast, + organizationId, + ] ); const handleUploadReport = useCallback( @@ -547,7 +590,10 @@ function VendorViewContent({ description: "Compliance report uploaded successfully", variant: "default", }); - loadQuery({ vendorId: data.node.id! }); + loadQuery({ + vendorId: data.node.id!, + organizationId: organizationId!, + }); }, onError: (error) => { toast({ @@ -561,48 +607,121 @@ function VendorViewContent({ }; reader.readAsDataURL(file); }, - [uploadVendorComplianceReport, data.node.id, loadQuery, toast] + [ + uploadVendorComplianceReport, + data.node.id, + loadQuery, + toast, + organizationId, + ] ); return (
- handleFieldChange("name", value)} - /> + +
+
+

Basic Information

+

+ General information about the vendor +

+
- handleFieldChange("description", value)} - /> +
+ handleFieldChange("name", value)} + /> - handleFieldChange("legalName", value)} - /> + handleFieldChange("description", value)} + /> - handleFieldChange("headquarterAddress", value)} - /> + handleFieldChange("legalName", value)} + /> - handleFieldChange("websiteUrl", value)} - /> + + handleFieldChange("headquarterAddress", value) + } + /> + + handleFieldChange("websiteUrl", value)} + /> +
+
+
-

Service Information

+

Ownership Information

- Basic information about the vendor service + Individuals responsible for this vendor +

+
+ +
+
+
+ + +
+ + handleFieldChange("businessOwnerId", value) + } + placeholder="Select business owner (optional)" + /> +

+ The person responsible for business decisions related to this + vendor +

+
+ +
+
+ + +
+ + handleFieldChange("securityOwnerId", value) + } + placeholder="Select security owner (optional)" + /> +

+ The person responsible for security oversight of this vendor +

+
+
+
+
+ + +
+
+

+ Risk & Service Information +

+

+ Information about service criticality and risk

@@ -735,7 +854,7 @@ function VendorViewContent({
-

URLs

+

Documentation & Links

Important URLs related to the vendor

@@ -860,13 +979,13 @@ function VendorViewContent({ } export default function VendorView() { - const { vendorId } = useParams(); + const { vendorId, organizationId } = useParams(); const [queryRef, loadQuery] = useQueryLoader(vendorViewQuery); useEffect(() => { - loadQuery({ vendorId: vendorId! }); - }, [loadQuery, vendorId]); + loadQuery({ vendorId: vendorId!, organizationId: organizationId! }); + }, [loadQuery, vendorId, organizationId]); if (!queryRef) { return ; diff --git a/apps/console/src/pages/organizations/vendors/__generated__/VendorListViewCreateVendorMutation.graphql.ts b/apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewCreateVendorMutation.graphql.ts similarity index 86% rename from apps/console/src/pages/organizations/vendors/__generated__/VendorListViewCreateVendorMutation.graphql.ts rename to apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewCreateVendorMutation.graphql.ts index bb96005a4..56c78d05c 100644 --- a/apps/console/src/pages/organizations/vendors/__generated__/VendorListViewCreateVendorMutation.graphql.ts +++ b/apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewCreateVendorMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<178462391e73ba2adb212005c3813f33>> * @lightSyntaxTransform * @nogrep */ @@ -12,6 +12,7 @@ import { ConcreteRequest } from 'relay-runtime'; export type RiskTier = "CRITICAL" | "GENERAL" | "SIGNIFICANT"; export type ServiceCriticality = "HIGH" | "LOW" | "MEDIUM"; export type CreateVendorInput = { + businessOwnerId?: string | null | undefined; category?: string | null | undefined; certifications?: ReadonlyArray | null | undefined; dataProcessingAgreementUrl?: string | null | undefined; @@ -22,6 +23,7 @@ export type CreateVendorInput = { organizationId: string; privacyPolicyUrl?: string | null | undefined; riskTier: RiskTier; + securityOwnerId?: string | null | undefined; securityPageUrl?: string | null | undefined; serviceCriticality: ServiceCriticality; serviceLevelAgreementUrl?: string | null | undefined; @@ -32,11 +34,11 @@ export type CreateVendorInput = { trustPageUrl?: string | null | undefined; websiteUrl?: string | null | undefined; }; -export type VendorListViewCreateVendorMutation$variables = { +export type ListVendorViewCreateVendorMutation$variables = { connections: ReadonlyArray; input: CreateVendorInput; }; -export type VendorListViewCreateVendorMutation$data = { +export type ListVendorViewCreateVendorMutation$data = { readonly createVendor: { readonly vendorEdge: { readonly node: { @@ -50,9 +52,9 @@ export type VendorListViewCreateVendorMutation$data = { }; }; }; -export type VendorListViewCreateVendorMutation = { - response: VendorListViewCreateVendorMutation$data; - variables: VendorListViewCreateVendorMutation$variables; +export type ListVendorViewCreateVendorMutation = { + response: ListVendorViewCreateVendorMutation$data; + variables: ListVendorViewCreateVendorMutation$variables; }; const node: ConcreteRequest = (function(){ @@ -145,7 +147,7 @@ return { ], "kind": "Fragment", "metadata": null, - "name": "VendorListViewCreateVendorMutation", + "name": "ListVendorViewCreateVendorMutation", "selections": [ { "alias": null, @@ -170,7 +172,7 @@ return { (v0/*: any*/) ], "kind": "Operation", - "name": "VendorListViewCreateVendorMutation", + "name": "ListVendorViewCreateVendorMutation", "selections": [ { "alias": null, @@ -203,16 +205,16 @@ return { ] }, "params": { - "cacheID": "cedd0394889a1b00d329ed9f30dcb3b9", + "cacheID": "6b3c93935ce5b90fead6aadd70992667", "id": null, "metadata": {}, - "name": "VendorListViewCreateVendorMutation", + "name": "ListVendorViewCreateVendorMutation", "operationKind": "mutation", - "text": "mutation VendorListViewCreateVendorMutation(\n $input: CreateVendorInput!\n) {\n createVendor(input: $input) {\n vendorEdge {\n node {\n id\n name\n description\n createdAt\n updatedAt\n riskTier\n }\n }\n }\n}\n" + "text": "mutation ListVendorViewCreateVendorMutation(\n $input: CreateVendorInput!\n) {\n createVendor(input: $input) {\n vendorEdge {\n node {\n id\n name\n description\n createdAt\n updatedAt\n riskTier\n }\n }\n }\n}\n" } }; })(); -(node as any).hash = "9be60a8a66ae0214cf280252be1c6b7b"; +(node as any).hash = "d75423060ceec238c1cc6b38f79be4e1"; export default node; diff --git a/apps/console/src/pages/organizations/vendors/__generated__/VendorListViewDeleteVendorMutation.graphql.ts b/apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewDeleteVendorMutation.graphql.ts similarity index 78% rename from apps/console/src/pages/organizations/vendors/__generated__/VendorListViewDeleteVendorMutation.graphql.ts rename to apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewDeleteVendorMutation.graphql.ts index 7f817d046..c074dad33 100644 --- a/apps/console/src/pages/organizations/vendors/__generated__/VendorListViewDeleteVendorMutation.graphql.ts +++ b/apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewDeleteVendorMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -12,18 +12,18 @@ import { ConcreteRequest } from 'relay-runtime'; export type DeleteVendorInput = { vendorId: string; }; -export type VendorListViewDeleteVendorMutation$variables = { +export type ListVendorViewDeleteVendorMutation$variables = { connections: ReadonlyArray; input: DeleteVendorInput; }; -export type VendorListViewDeleteVendorMutation$data = { +export type ListVendorViewDeleteVendorMutation$data = { readonly deleteVendor: { readonly deletedVendorId: string; }; }; -export type VendorListViewDeleteVendorMutation = { - response: VendorListViewDeleteVendorMutation$data; - variables: VendorListViewDeleteVendorMutation$variables; +export type ListVendorViewDeleteVendorMutation = { + response: ListVendorViewDeleteVendorMutation$data; + variables: ListVendorViewDeleteVendorMutation$variables; }; const node: ConcreteRequest = (function(){ @@ -59,7 +59,7 @@ return { ], "kind": "Fragment", "metadata": null, - "name": "VendorListViewDeleteVendorMutation", + "name": "ListVendorViewDeleteVendorMutation", "selections": [ { "alias": null, @@ -84,7 +84,7 @@ return { (v0/*: any*/) ], "kind": "Operation", - "name": "VendorListViewDeleteVendorMutation", + "name": "ListVendorViewDeleteVendorMutation", "selections": [ { "alias": null, @@ -117,16 +117,16 @@ return { ] }, "params": { - "cacheID": "7edee61297200dec1695f6b2325cd7f0", + "cacheID": "7aa26c31ccb2b601700560ad66e84b6f", "id": null, "metadata": {}, - "name": "VendorListViewDeleteVendorMutation", + "name": "ListVendorViewDeleteVendorMutation", "operationKind": "mutation", - "text": "mutation VendorListViewDeleteVendorMutation(\n $input: DeleteVendorInput!\n) {\n deleteVendor(input: $input) {\n deletedVendorId\n }\n}\n" + "text": "mutation ListVendorViewDeleteVendorMutation(\n $input: DeleteVendorInput!\n) {\n deleteVendor(input: $input) {\n deletedVendorId\n }\n}\n" } }; })(); -(node as any).hash = "85387685fd75c8b99fb9e9195d4a903f"; +(node as any).hash = "9d443dea5c3e78e04dbfc6632b063c8d"; export default node; diff --git a/apps/console/src/pages/organizations/vendors/__generated__/VendorListViewPaginationQuery.graphql.ts b/apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewPaginationQuery.graphql.ts similarity index 91% rename from apps/console/src/pages/organizations/vendors/__generated__/VendorListViewPaginationQuery.graphql.ts rename to apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewPaginationQuery.graphql.ts index de5f26bd6..5589e0910 100644 --- a/apps/console/src/pages/organizations/vendors/__generated__/VendorListViewPaginationQuery.graphql.ts +++ b/apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewPaginationQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<49f537ed24daee25a89fb244341ba874>> * @lightSyntaxTransform * @nogrep */ @@ -10,21 +10,21 @@ import { ConcreteRequest } from 'relay-runtime'; import { FragmentRefs } from "relay-runtime"; -export type VendorListViewPaginationQuery$variables = { +export type ListVendorViewPaginationQuery$variables = { after?: string | null | undefined; before?: string | null | undefined; first?: number | null | undefined; id: string; last?: number | null | undefined; }; -export type VendorListViewPaginationQuery$data = { +export type ListVendorViewPaginationQuery$data = { readonly node: { - readonly " $fragmentSpreads": FragmentRefs<"VendorListView_vendors">; + readonly " $fragmentSpreads": FragmentRefs<"ListVendorView_vendors">; }; }; -export type VendorListViewPaginationQuery = { - response: VendorListViewPaginationQuery$data; - variables: VendorListViewPaginationQuery$variables; +export type ListVendorViewPaginationQuery = { + response: ListVendorViewPaginationQuery$data; + variables: ListVendorViewPaginationQuery$variables; }; const node: ConcreteRequest = (function(){ @@ -119,7 +119,7 @@ return { ], "kind": "Fragment", "metadata": null, - "name": "VendorListViewPaginationQuery", + "name": "ListVendorViewPaginationQuery", "selections": [ { "alias": null, @@ -137,7 +137,7 @@ return { (v9/*: any*/) ], "kind": "FragmentSpread", - "name": "VendorListView_vendors" + "name": "ListVendorView_vendors" } ], "storageKey": null @@ -156,7 +156,7 @@ return { (v3/*: any*/) ], "kind": "Operation", - "name": "VendorListViewPaginationQuery", + "name": "ListVendorViewPaginationQuery", "selections": [ { "alias": null, @@ -320,16 +320,16 @@ return { ] }, "params": { - "cacheID": "f447e5171c495d86e5132c58b2afc8c8", + "cacheID": "f48cd9d88c871edac4c400dc2fa20e9a", "id": null, "metadata": {}, - "name": "VendorListViewPaginationQuery", + "name": "ListVendorViewPaginationQuery", "operationKind": "query", - "text": "query VendorListViewPaginationQuery(\n $after: CursorKey\n $before: CursorKey\n $first: Int\n $last: Int\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...VendorListView_vendors_pbnwq\n id\n }\n}\n\nfragment VendorListView_vendors_pbnwq on Organization {\n vendors(first: $first, after: $after, last: $last, before: $before, orderBy: {direction: ASC, field: NAME}) {\n edges {\n node {\n id\n name\n description\n createdAt\n updatedAt\n riskTier\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n id\n}\n" + "text": "query ListVendorViewPaginationQuery(\n $after: CursorKey\n $before: CursorKey\n $first: Int\n $last: Int\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...ListVendorView_vendors_pbnwq\n id\n }\n}\n\nfragment ListVendorView_vendors_pbnwq on Organization {\n vendors(first: $first, after: $after, last: $last, before: $before, orderBy: {direction: ASC, field: NAME}) {\n edges {\n node {\n id\n name\n description\n createdAt\n updatedAt\n riskTier\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n id\n}\n" } }; })(); -(node as any).hash = "96466253eae17f60ffdf08ad59719e83"; +(node as any).hash = "6e3badec3308017fd318d4bd1dc41b44"; export default node; diff --git a/apps/console/src/pages/organizations/vendors/__generated__/VendorListViewQuery.graphql.ts b/apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewQuery.graphql.ts similarity index 91% rename from apps/console/src/pages/organizations/vendors/__generated__/VendorListViewQuery.graphql.ts rename to apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewQuery.graphql.ts index 500a61c7d..46266cad0 100644 --- a/apps/console/src/pages/organizations/vendors/__generated__/VendorListViewQuery.graphql.ts +++ b/apps/console/src/pages/organizations/vendors/__generated__/ListVendorViewQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<843c11acfe2ed3f07fe2e05237f4cb08>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -10,22 +10,22 @@ import { ConcreteRequest } from 'relay-runtime'; import { FragmentRefs } from "relay-runtime"; -export type VendorListViewQuery$variables = { +export type ListVendorViewQuery$variables = { after?: string | null | undefined; before?: string | null | undefined; first?: number | null | undefined; last?: number | null | undefined; organizationId: string; }; -export type VendorListViewQuery$data = { +export type ListVendorViewQuery$data = { readonly organization: { readonly id: string; - readonly " $fragmentSpreads": FragmentRefs<"VendorListView_vendors">; + readonly " $fragmentSpreads": FragmentRefs<"ListVendorView_vendors">; }; }; -export type VendorListViewQuery = { - response: VendorListViewQuery$data; - variables: VendorListViewQuery$variables; +export type ListVendorViewQuery = { + response: ListVendorViewQuery$data; + variables: ListVendorViewQuery$variables; }; const node: ConcreteRequest = (function(){ @@ -120,7 +120,7 @@ return { ], "kind": "Fragment", "metadata": null, - "name": "VendorListViewQuery", + "name": "ListVendorViewQuery", "selections": [ { "alias": "organization", @@ -139,7 +139,7 @@ return { (v10/*: any*/) ], "kind": "FragmentSpread", - "name": "VendorListView_vendors" + "name": "ListVendorView_vendors" } ], "storageKey": null @@ -158,7 +158,7 @@ return { (v1/*: any*/) ], "kind": "Operation", - "name": "VendorListViewQuery", + "name": "ListVendorViewQuery", "selections": [ { "alias": "organization", @@ -322,16 +322,16 @@ return { ] }, "params": { - "cacheID": "4bef9b4458b57ed70122d2bcbb205788", + "cacheID": "f0f91262680529a635110382be5a2b03", "id": null, "metadata": {}, - "name": "VendorListViewQuery", + "name": "ListVendorViewQuery", "operationKind": "query", - "text": "query VendorListViewQuery(\n $organizationId: ID!\n $first: Int\n $after: CursorKey\n $last: Int\n $before: CursorKey\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ...VendorListView_vendors_pbnwq\n }\n}\n\nfragment VendorListView_vendors_pbnwq on Organization {\n vendors(first: $first, after: $after, last: $last, before: $before, orderBy: {direction: ASC, field: NAME}) {\n edges {\n node {\n id\n name\n description\n createdAt\n updatedAt\n riskTier\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n id\n}\n" + "text": "query ListVendorViewQuery(\n $organizationId: ID!\n $first: Int\n $after: CursorKey\n $last: Int\n $before: CursorKey\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ...ListVendorView_vendors_pbnwq\n }\n}\n\nfragment ListVendorView_vendors_pbnwq on Organization {\n vendors(first: $first, after: $after, last: $last, before: $before, orderBy: {direction: ASC, field: NAME}) {\n edges {\n node {\n id\n name\n description\n createdAt\n updatedAt\n riskTier\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n id\n}\n" } }; })(); -(node as any).hash = "ea4b2e523cecb2f3200afb17b4bd505a"; +(node as any).hash = "184bbebfe8fea44e62091de8165d0683"; export default node; diff --git a/apps/console/src/pages/organizations/vendors/__generated__/VendorListView_vendors.graphql.ts b/apps/console/src/pages/organizations/vendors/__generated__/ListVendorView_vendors.graphql.ts similarity index 92% rename from apps/console/src/pages/organizations/vendors/__generated__/VendorListView_vendors.graphql.ts rename to apps/console/src/pages/organizations/vendors/__generated__/ListVendorView_vendors.graphql.ts index 904a2f30d..4ad44d33b 100644 --- a/apps/console/src/pages/organizations/vendors/__generated__/VendorListView_vendors.graphql.ts +++ b/apps/console/src/pages/organizations/vendors/__generated__/ListVendorView_vendors.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -11,7 +11,7 @@ import { ReaderFragment } from 'relay-runtime'; export type RiskTier = "CRITICAL" | "GENERAL" | "SIGNIFICANT"; import { FragmentRefs } from "relay-runtime"; -export type VendorListView_vendors$data = { +export type ListVendorView_vendors$data = { readonly id: string; readonly vendors: { readonly __id: string; @@ -32,11 +32,11 @@ export type VendorListView_vendors$data = { readonly startCursor: string | null | undefined; }; }; - readonly " $fragmentType": "VendorListView_vendors"; + readonly " $fragmentType": "ListVendorView_vendors"; }; -export type VendorListView_vendors$key = { - readonly " $data"?: VendorListView_vendors$data; - readonly " $fragmentSpreads": FragmentRefs<"VendorListView_vendors">; +export type ListVendorView_vendors$key = { + readonly " $data"?: ListVendorView_vendors$data; + readonly " $fragmentSpreads": FragmentRefs<"ListVendorView_vendors">; }; const node: ReaderFragment = (function(){ @@ -98,14 +98,14 @@ return { "fragmentPathInResult": [ "node" ], - "operation": require('./VendorListViewPaginationQuery.graphql'), + "operation": require('./ListVendorViewPaginationQuery.graphql'), "identifierInfo": { "identifierField": "id", "identifierQueryVariableName": "id" } } }, - "name": "VendorListView_vendors", + "name": "ListVendorView_vendors", "selections": [ { "alias": "vendors", @@ -257,6 +257,6 @@ return { }; })(); -(node as any).hash = "96466253eae17f60ffdf08ad59719e83"; +(node as any).hash = "6e3badec3308017fd318d4bd1dc41b44"; export default node; diff --git a/apps/console/src/pages/organizations/vendors/__generated__/VendorViewQuery.graphql.ts b/apps/console/src/pages/organizations/vendors/__generated__/VendorViewQuery.graphql.ts index 62a0c7a7a..4018957d0 100644 --- a/apps/console/src/pages/organizations/vendors/__generated__/VendorViewQuery.graphql.ts +++ b/apps/console/src/pages/organizations/vendors/__generated__/VendorViewQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<62c3cb3473246b0326ff4cbd4cc4f940>> * @lightSyntaxTransform * @nogrep */ @@ -9,13 +9,19 @@ // @ts-nocheck import { ConcreteRequest } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; export type RiskTier = "CRITICAL" | "GENERAL" | "SIGNIFICANT"; export type ServiceCriticality = "HIGH" | "LOW" | "MEDIUM"; export type VendorViewQuery$variables = { + organizationId: string; vendorId: string; }; export type VendorViewQuery$data = { readonly node: { + readonly businessOwner?: { + readonly fullName: string; + readonly id: string; + } | null | undefined; readonly certifications?: ReadonlyArray; readonly complianceReports?: { readonly edges: ReadonlyArray<{ @@ -39,6 +45,10 @@ export type VendorViewQuery$data = { readonly name?: string; readonly privacyPolicyUrl?: string | null | undefined; readonly riskTier?: RiskTier; + readonly securityOwner?: { + readonly fullName: string; + readonly id: string; + } | null | undefined; readonly securityPageUrl?: string | null | undefined; readonly serviceCriticality?: ServiceCriticality; readonly serviceLevelAgreementUrl?: string | null | undefined; @@ -50,6 +60,9 @@ export type VendorViewQuery$data = { readonly updatedAt?: string; readonly websiteUrl?: string | null | undefined; }; + readonly organization: { + readonly " $fragmentSpreads": FragmentRefs<"PeopleSelector_organization">; + }; }; export type VendorViewQuery = { response: VendorViewQuery$data; @@ -57,168 +70,234 @@ export type VendorViewQuery = { }; const node: ConcreteRequest = (function(){ -var v0 = [ - { - "defaultValue": null, - "kind": "LocalArgument", - "name": "vendorId" - } -], -v1 = [ +var v0 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "organizationId" +}, +v1 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "vendorId" +}, +v2 = [ { "kind": "Variable", "name": "id", "variableName": "vendorId" } ], -v2 = { +v3 = { "alias": null, "args": null, "kind": "ScalarField", "name": "id", "storageKey": null }, -v3 = { +v4 = { "alias": null, "args": null, "kind": "ScalarField", "name": "name", "storageKey": null }, -v4 = { +v5 = { "alias": null, "args": null, "kind": "ScalarField", "name": "description", "storageKey": null }, -v5 = { +v6 = { "alias": null, "args": null, "kind": "ScalarField", "name": "serviceStartAt", "storageKey": null }, -v6 = { +v7 = { "alias": null, "args": null, "kind": "ScalarField", "name": "serviceTerminationAt", "storageKey": null }, -v7 = { +v8 = { "alias": null, "args": null, "kind": "ScalarField", "name": "serviceCriticality", "storageKey": null }, -v8 = { +v9 = { "alias": null, "args": null, "kind": "ScalarField", "name": "riskTier", "storageKey": null }, -v9 = { +v10 = { "alias": null, "args": null, "kind": "ScalarField", "name": "statusPageUrl", "storageKey": null }, -v10 = { +v11 = { "alias": null, "args": null, "kind": "ScalarField", "name": "termsOfServiceUrl", "storageKey": null }, -v11 = { +v12 = { "alias": null, "args": null, "kind": "ScalarField", "name": "privacyPolicyUrl", "storageKey": null }, -v12 = { +v13 = { "alias": null, "args": null, "kind": "ScalarField", "name": "serviceLevelAgreementUrl", "storageKey": null }, -v13 = { +v14 = { "alias": null, "args": null, "kind": "ScalarField", "name": "dataProcessingAgreementUrl", "storageKey": null }, -v14 = { +v15 = { "alias": null, "args": null, "kind": "ScalarField", "name": "securityPageUrl", "storageKey": null }, -v15 = { +v16 = { "alias": null, "args": null, "kind": "ScalarField", "name": "trustPageUrl", "storageKey": null }, -v16 = { +v17 = { "alias": null, "args": null, "kind": "ScalarField", "name": "certifications", "storageKey": null }, -v17 = { +v18 = { "alias": null, "args": null, "kind": "ScalarField", "name": "headquarterAddress", "storageKey": null }, -v18 = { +v19 = { "alias": null, "args": null, "kind": "ScalarField", "name": "legalName", "storageKey": null }, -v19 = { +v20 = { "alias": null, "args": null, "kind": "ScalarField", "name": "websiteUrl", "storageKey": null }, -v20 = { +v21 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "fullName", + "storageKey": null +}, +v22 = [ + (v3/*: any*/), + (v21/*: any*/) +], +v23 = { + "alias": null, + "args": null, + "concreteType": "People", + "kind": "LinkedField", + "name": "businessOwner", + "plural": false, + "selections": (v22/*: any*/), + "storageKey": null +}, +v24 = { + "alias": null, + "args": null, + "concreteType": "People", + "kind": "LinkedField", + "name": "securityOwner", + "plural": false, + "selections": (v22/*: any*/), + "storageKey": null +}, +v25 = { "alias": null, "args": null, "kind": "ScalarField", "name": "createdAt", "storageKey": null }, -v21 = { +v26 = { "alias": null, "args": null, "kind": "ScalarField", "name": "updatedAt", "storageKey": null }, -v22 = { +v27 = { "alias": null, "args": null, "kind": "ScalarField", "name": "__typename", "storageKey": null }, -v23 = [ +v28 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null +}, +v29 = { + "alias": null, + "args": null, + "concreteType": "PageInfo", + "kind": "LinkedField", + "name": "pageInfo", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "endCursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasNextPage", + "storageKey": null + } + ], + "storageKey": null +}, +v30 = [ { "alias": null, "args": null, @@ -235,7 +314,7 @@ v23 = [ "name": "node", "plural": false, "selections": [ - (v2/*: any*/), + (v3/*: any*/), { "alias": null, "args": null, @@ -271,64 +350,56 @@ v23 = [ "name": "fileSize", "storageKey": null }, - (v20/*: any*/), - (v22/*: any*/) + (v25/*: any*/), + (v27/*: any*/) ], "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "cursor", - "storageKey": null - } + (v28/*: any*/) ], "storageKey": null }, + (v29/*: any*/) +], +v31 = [ { - "alias": null, - "args": null, - "concreteType": "PageInfo", - "kind": "LinkedField", - "name": "pageInfo", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "endCursor", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasNextPage", - "storageKey": null - } - ], - "storageKey": null + "kind": "Variable", + "name": "id", + "variableName": "organizationId" } ], -v24 = [ +v32 = { + "kind": "Literal", + "name": "first", + "value": 100 +}, +v33 = [ + (v32/*: any*/) +], +v34 = [ + (v32/*: any*/), { "kind": "Literal", - "name": "first", - "value": 100 + "name": "orderBy", + "value": { + "direction": "ASC", + "field": "FULL_NAME" + } } ]; return { "fragment": { - "argumentDefinitions": (v0/*: any*/), + "argumentDefinitions": [ + (v0/*: any*/), + (v1/*: any*/) + ], "kind": "Fragment", "metadata": null, "name": "VendorViewQuery", "selections": [ { "alias": null, - "args": (v1/*: any*/), + "args": (v2/*: any*/), "concreteType": null, "kind": "LinkedField", "name": "node", @@ -337,7 +408,6 @@ return { { "kind": "InlineFragment", "selections": [ - (v2/*: any*/), (v3/*: any*/), (v4/*: any*/), (v5/*: any*/), @@ -356,7 +426,10 @@ return { (v18/*: any*/), (v19/*: any*/), (v20/*: any*/), - (v21/*: any*/), + (v23/*: any*/), + (v24/*: any*/), + (v25/*: any*/), + (v26/*: any*/), { "alias": "complianceReports", "args": null, @@ -364,7 +437,7 @@ return { "kind": "LinkedField", "name": "__VendorView_complianceReports_connection", "plural": false, - "selections": (v23/*: any*/), + "selections": (v30/*: any*/), "storageKey": null } ], @@ -373,6 +446,22 @@ return { } ], "storageKey": null + }, + { + "alias": "organization", + "args": (v31/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "PeopleSelector_organization" + } + ], + "storageKey": null } ], "type": "Query", @@ -380,24 +469,26 @@ return { }, "kind": "Request", "operation": { - "argumentDefinitions": (v0/*: any*/), + "argumentDefinitions": [ + (v1/*: any*/), + (v0/*: any*/) + ], "kind": "Operation", "name": "VendorViewQuery", "selections": [ { "alias": null, - "args": (v1/*: any*/), + "args": (v2/*: any*/), "concreteType": null, "kind": "LinkedField", "name": "node", "plural": false, "selections": [ - (v22/*: any*/), - (v2/*: any*/), + (v27/*: any*/), + (v3/*: any*/), { "kind": "InlineFragment", "selections": [ - (v3/*: any*/), (v4/*: any*/), (v5/*: any*/), (v6/*: any*/), @@ -415,20 +506,23 @@ return { (v18/*: any*/), (v19/*: any*/), (v20/*: any*/), - (v21/*: any*/), + (v23/*: any*/), + (v24/*: any*/), + (v25/*: any*/), + (v26/*: any*/), { "alias": null, - "args": (v24/*: any*/), + "args": (v33/*: any*/), "concreteType": "VendorComplianceReportConnection", "kind": "LinkedField", "name": "complianceReports", "plural": false, - "selections": (v23/*: any*/), + "selections": (v30/*: any*/), "storageKey": "complianceReports(first:100)" }, { "alias": null, - "args": (v24/*: any*/), + "args": (v33/*: any*/), "filters": null, "handle": "connection", "key": "VendorView_complianceReports", @@ -441,11 +535,87 @@ return { } ], "storageKey": null + }, + { + "alias": "organization", + "args": (v31/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v27/*: any*/), + (v3/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": (v34/*: any*/), + "concreteType": "PeopleConnection", + "kind": "LinkedField", + "name": "peoples", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "PeopleEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "People", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v3/*: any*/), + (v21/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "primaryEmailAddress", + "storageKey": null + }, + (v27/*: any*/) + ], + "storageKey": null + }, + (v28/*: any*/) + ], + "storageKey": null + }, + (v29/*: any*/) + ], + "storageKey": "peoples(first:100,orderBy:{\"direction\":\"ASC\",\"field\":\"FULL_NAME\"})" + }, + { + "alias": null, + "args": (v34/*: any*/), + "filters": [ + "orderBy" + ], + "handle": "connection", + "key": "PeopleSelector_organization_peoples", + "kind": "LinkedHandle", + "name": "peoples" + } + ], + "type": "Organization", + "abstractKey": null + } + ], + "storageKey": null } ] }, "params": { - "cacheID": "2bef9911b76d74ced85cf4430410a297", + "cacheID": "337beb9fa1d1d29bcd974229385d8e7a", "id": null, "metadata": { "connection": [ @@ -462,11 +632,11 @@ return { }, "name": "VendorViewQuery", "operationKind": "query", - "text": "query VendorViewQuery(\n $vendorId: ID!\n) {\n node(id: $vendorId) {\n __typename\n ... on Vendor {\n id\n name\n description\n serviceStartAt\n serviceTerminationAt\n serviceCriticality\n riskTier\n statusPageUrl\n termsOfServiceUrl\n privacyPolicyUrl\n serviceLevelAgreementUrl\n dataProcessingAgreementUrl\n securityPageUrl\n trustPageUrl\n certifications\n headquarterAddress\n legalName\n websiteUrl\n createdAt\n updatedAt\n complianceReports(first: 100) {\n edges {\n node {\n id\n reportName\n reportDate\n validUntil\n fileUrl\n fileSize\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n id\n }\n}\n" + "text": "query VendorViewQuery(\n $vendorId: ID!\n $organizationId: ID!\n) {\n node(id: $vendorId) {\n __typename\n ... on Vendor {\n id\n name\n description\n serviceStartAt\n serviceTerminationAt\n serviceCriticality\n riskTier\n statusPageUrl\n termsOfServiceUrl\n privacyPolicyUrl\n serviceLevelAgreementUrl\n dataProcessingAgreementUrl\n securityPageUrl\n trustPageUrl\n certifications\n headquarterAddress\n legalName\n websiteUrl\n businessOwner {\n id\n fullName\n }\n securityOwner {\n id\n fullName\n }\n createdAt\n updatedAt\n complianceReports(first: 100) {\n edges {\n node {\n id\n reportName\n reportDate\n validUntil\n fileUrl\n fileSize\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n id\n }\n organization: node(id: $organizationId) {\n __typename\n ...PeopleSelector_organization\n id\n }\n}\n\nfragment PeopleSelector_organization on Organization {\n id\n peoples(first: 100, orderBy: {direction: ASC, field: FULL_NAME}) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n" } }; })(); -(node as any).hash = "f3f95bc0c893b61e83940c1cc0c33922"; +(node as any).hash = "4a621712e4dfaebf9ad42232ba7f713a"; export default node; diff --git a/apps/console/src/pages/organizations/vendors/__generated__/VendorViewUpdateVendorMutation.graphql.ts b/apps/console/src/pages/organizations/vendors/__generated__/VendorViewUpdateVendorMutation.graphql.ts index e6ea62d6f..3c3b3f825 100644 --- a/apps/console/src/pages/organizations/vendors/__generated__/VendorViewUpdateVendorMutation.graphql.ts +++ b/apps/console/src/pages/organizations/vendors/__generated__/VendorViewUpdateVendorMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<0e66de0cfba2c27bbf78dcb8e846ad8d>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -12,6 +12,7 @@ import { ConcreteRequest } from 'relay-runtime'; export type RiskTier = "CRITICAL" | "GENERAL" | "SIGNIFICANT"; export type ServiceCriticality = "HIGH" | "LOW" | "MEDIUM"; export type UpdateVendorInput = { + businessOwnerId?: string | null | undefined; category?: string | null | undefined; certifications?: ReadonlyArray | null | undefined; dataProcessingAgreementUrl?: string | null | undefined; @@ -22,6 +23,7 @@ export type UpdateVendorInput = { name?: string | null | undefined; privacyPolicyUrl?: string | null | undefined; riskTier?: RiskTier | null | undefined; + securityOwnerId?: string | null | undefined; securityPageUrl?: string | null | undefined; serviceCriticality?: ServiceCriticality | null | undefined; serviceLevelAgreementUrl?: string | null | undefined; @@ -38,6 +40,10 @@ export type VendorViewUpdateVendorMutation$variables = { export type VendorViewUpdateVendorMutation$data = { readonly updateVendor: { readonly vendor: { + readonly businessOwner: { + readonly fullName: string; + readonly id: string; + } | null | undefined; readonly certifications: ReadonlyArray; readonly dataProcessingAgreementUrl: string | null | undefined; readonly description: string | null | undefined; @@ -47,6 +53,10 @@ export type VendorViewUpdateVendorMutation$data = { readonly name: string; readonly privacyPolicyUrl: string | null | undefined; readonly riskTier: RiskTier; + readonly securityOwner: { + readonly fullName: string; + readonly id: string; + } | null | undefined; readonly securityPageUrl: string | null | undefined; readonly serviceCriticality: ServiceCriticality; readonly serviceLevelAgreementUrl: string | null | undefined; @@ -73,7 +83,24 @@ var v0 = [ "name": "input" } ], -v1 = [ +v1 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v2 = [ + (v1/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "fullName", + "storageKey": null + } +], +v3 = [ { "alias": null, "args": [ @@ -96,13 +123,7 @@ v1 = [ "name": "vendor", "plural": false, "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "id", - "storageKey": null - }, + (v1/*: any*/), { "alias": null, "args": null, @@ -222,6 +243,26 @@ v1 = [ "name": "websiteUrl", "storageKey": null }, + { + "alias": null, + "args": null, + "concreteType": "People", + "kind": "LinkedField", + "name": "businessOwner", + "plural": false, + "selections": (v2/*: any*/), + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "People", + "kind": "LinkedField", + "name": "securityOwner", + "plural": false, + "selections": (v2/*: any*/), + "storageKey": null + }, { "alias": null, "args": null, @@ -242,7 +283,7 @@ return { "kind": "Fragment", "metadata": null, "name": "VendorViewUpdateVendorMutation", - "selections": (v1/*: any*/), + "selections": (v3/*: any*/), "type": "Mutation", "abstractKey": null }, @@ -251,19 +292,19 @@ return { "argumentDefinitions": (v0/*: any*/), "kind": "Operation", "name": "VendorViewUpdateVendorMutation", - "selections": (v1/*: any*/) + "selections": (v3/*: any*/) }, "params": { - "cacheID": "8ed69675626a00cac2cc79aa570f123d", + "cacheID": "d582aaaddaa73ef8e0fdef172364c64e", "id": null, "metadata": {}, "name": "VendorViewUpdateVendorMutation", "operationKind": "mutation", - "text": "mutation VendorViewUpdateVendorMutation(\n $input: UpdateVendorInput!\n) {\n updateVendor(input: $input) {\n vendor {\n id\n name\n description\n serviceStartAt\n serviceTerminationAt\n serviceCriticality\n riskTier\n statusPageUrl\n termsOfServiceUrl\n privacyPolicyUrl\n serviceLevelAgreementUrl\n dataProcessingAgreementUrl\n securityPageUrl\n trustPageUrl\n certifications\n headquarterAddress\n legalName\n websiteUrl\n updatedAt\n }\n }\n}\n" + "text": "mutation VendorViewUpdateVendorMutation(\n $input: UpdateVendorInput!\n) {\n updateVendor(input: $input) {\n vendor {\n id\n name\n description\n serviceStartAt\n serviceTerminationAt\n serviceCriticality\n riskTier\n statusPageUrl\n termsOfServiceUrl\n privacyPolicyUrl\n serviceLevelAgreementUrl\n dataProcessingAgreementUrl\n securityPageUrl\n trustPageUrl\n certifications\n headquarterAddress\n legalName\n websiteUrl\n businessOwner {\n id\n fullName\n }\n securityOwner {\n id\n fullName\n }\n updatedAt\n }\n }\n}\n" } }; })(); -(node as any).hash = "517efd79b3eb1781ed562368a2ed1ecc"; +(node as any).hash = "627ef1694ebc04845b019bb65dd72c97"; export default node; diff --git a/pkg/coredata/migrations/20250412T141800Z.sql b/pkg/coredata/migrations/20250412T141800Z.sql new file mode 100644 index 000000000..74897c544 --- /dev/null +++ b/pkg/coredata/migrations/20250412T141800Z.sql @@ -0,0 +1,2 @@ +ALTER TABLE vendors ADD COLUMN business_owner_id TEXT REFERENCES peoples(id) ON DELETE SET NULL; +ALTER TABLE vendors ADD COLUMN security_owner_id TEXT REFERENCES peoples(id) ON DELETE SET NULL; diff --git a/pkg/coredata/vendor.go b/pkg/coredata/vendor.go index f33ccb7bd..cf7192786 100644 --- a/pkg/coredata/vendor.go +++ b/pkg/coredata/vendor.go @@ -47,6 +47,8 @@ type ( Certifications []string `db:"certifications"` ServiceCriticality ServiceCriticality `db:"service_criticality"` RiskTier RiskTier `db:"risk_tier"` + BusinessOwnerID *gid.GID `db:"business_owner_id"` + SecurityOwnerID *gid.GID `db:"security_owner_id"` StatusPageURL *string `db:"status_page_url"` TermsOfServiceURL *string `db:"terms_of_service_url"` SecurityPageURL *string `db:"security_page_url"` @@ -93,6 +95,8 @@ SELECT certifications, service_criticality, risk_tier, + business_owner_id, + security_owner_id, status_page_url, terms_of_service_url, security_page_url, @@ -153,6 +157,8 @@ INSERT INTO service_termination_at, service_criticality, risk_tier, + business_owner_id, + security_owner_id, status_page_url, terms_of_service_url, security_page_url, @@ -178,6 +184,8 @@ VALUES ( @service_termination_at, @service_criticality, @risk_tier, + @business_owner_id, + @security_owner_id, @status_page_url, @terms_of_service_url, @security_page_url, @@ -205,6 +213,8 @@ VALUES ( "service_termination_at": v.ServiceTerminationAt, "service_criticality": v.ServiceCriticality, "risk_tier": v.RiskTier, + "business_owner_id": v.BusinessOwnerID, + "security_owner_id": v.SecurityOwnerID, "status_page_url": v.StatusPageURL, "terms_of_service_url": v.TermsOfServiceURL, "security_page_url": v.SecurityPageURL, @@ -259,6 +269,8 @@ SELECT service_termination_at, service_criticality, risk_tier, + business_owner_id, + security_owner_id, status_page_url, terms_of_service_url, security_page_url, @@ -319,6 +331,8 @@ SET terms_of_service_url = @terms_of_service_url, security_page_url = @security_page_url, trust_page_url = @trust_page_url, + business_owner_id = @business_owner_id, + security_owner_id = @security_owner_id, updated_at = @updated_at WHERE %s AND id = @vendor_id @@ -346,6 +360,8 @@ WHERE %s "terms_of_service_url": v.TermsOfServiceURL, "security_page_url": v.SecurityPageURL, "trust_page_url": v.TrustPageURL, + "business_owner_id": v.BusinessOwnerID, + "security_owner_id": v.SecurityOwnerID, } maps.Copy(args, scope.SQLArguments()) diff --git a/pkg/probo/vendor_service.go b/pkg/probo/vendor_service.go index 766cae5e6..9bddd4394 100644 --- a/pkg/probo/vendor_service.go +++ b/pkg/probo/vendor_service.go @@ -50,6 +50,8 @@ type ( ServiceTerminationAt *time.Time ServiceCriticality coredata.ServiceCriticality RiskTier coredata.RiskTier + BusinessOwnerID *gid.GID + SecurityOwnerID *gid.GID } UpdateVendorRequest struct { @@ -72,6 +74,8 @@ type ( ServiceTerminationAt *time.Time ServiceCriticality *coredata.ServiceCriticality RiskTier *coredata.RiskTier + BusinessOwnerID *gid.GID + SecurityOwnerID *gid.GID } ) @@ -203,6 +207,14 @@ func (s VendorService) Update( vendor.TrustPageURL = req.TrustPageURL } + if req.BusinessOwnerID != nil { + vendor.BusinessOwnerID = req.BusinessOwnerID + } + + if req.SecurityOwnerID != nil { + vendor.SecurityOwnerID = req.SecurityOwnerID + } + vendor.UpdatedAt = time.Now() if err := vendor.Update(ctx, conn, s.svc.scope); err != nil { diff --git a/pkg/server/api/console/v1/schema.graphql b/pkg/server/api/console/v1/schema.graphql index 89ccd4b54..3e5e354d2 100644 --- a/pkg/server/api/console/v1/schema.graphql +++ b/pkg/server/api/console/v1/schema.graphql @@ -457,6 +457,9 @@ type Vendor implements Node { orderBy: VendorComplianceReportOrder ): VendorComplianceReportConnection! @goField(forceResolver: true) + businessOwner: People @goField(forceResolver: true) + securityOwner: People @goField(forceResolver: true) + serviceStartAt: Datetime! serviceTerminationAt: Datetime serviceCriticality: ServiceCriticality! @@ -943,6 +946,8 @@ input CreateVendorInput { serviceTerminationAt: Datetime serviceCriticality: ServiceCriticality! riskTier: RiskTier! + businessOwnerId: ID + securityOwnerId: ID } input UpdateVendorInput { @@ -965,6 +970,8 @@ input UpdateVendorInput { certifications: [String!] securityPageUrl: String trustPageUrl: String + businessOwnerId: ID + securityOwnerId: ID } input DeleteVendorInput { diff --git a/pkg/server/api/console/v1/schema/schema.go b/pkg/server/api/console/v1/schema/schema.go index 8d2e534bb..3994106bd 100644 --- a/pkg/server/api/console/v1/schema/schema.go +++ b/pkg/server/api/console/v1/schema/schema.go @@ -526,6 +526,7 @@ type ComplexityRoot struct { } Vendor struct { + BusinessOwner func(childComplexity int) int Certifications func(childComplexity int) int ComplianceReports func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorComplianceReportOrderBy) int CreatedAt func(childComplexity int) int @@ -537,6 +538,7 @@ type ComplexityRoot struct { Name func(childComplexity int) int PrivacyPolicyURL func(childComplexity int) int RiskTier func(childComplexity int) int + SecurityOwner func(childComplexity int) int SecurityPageURL func(childComplexity int) int ServiceCriticality func(childComplexity int) int ServiceLevelAgreementURL func(childComplexity int) int @@ -679,6 +681,8 @@ type TaskResolver interface { } type VendorResolver interface { ComplianceReports(ctx context.Context, obj *types.Vendor, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorComplianceReportOrderBy) (*types.VendorComplianceReportConnection, error) + BusinessOwner(ctx context.Context, obj *types.Vendor) (*types.People, error) + SecurityOwner(ctx context.Context, obj *types.Vendor) (*types.People, error) } type VendorComplianceReportResolver interface { Vendor(ctx context.Context, obj *types.VendorComplianceReport) (*types.Vendor, error) @@ -2633,6 +2637,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.UserEdge.Node(childComplexity), true + case "Vendor.businessOwner": + if e.complexity.Vendor.BusinessOwner == nil { + break + } + + return e.complexity.Vendor.BusinessOwner(childComplexity), true + case "Vendor.certifications": if e.complexity.Vendor.Certifications == nil { break @@ -2715,6 +2726,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Vendor.RiskTier(childComplexity), true + case "Vendor.securityOwner": + if e.complexity.Vendor.SecurityOwner == nil { + break + } + + return e.complexity.Vendor.SecurityOwner(childComplexity), true + case "Vendor.securityPageUrl": if e.complexity.Vendor.SecurityPageURL == nil { break @@ -3550,6 +3568,9 @@ type Vendor implements Node { orderBy: VendorComplianceReportOrder ): VendorComplianceReportConnection! @goField(forceResolver: true) + businessOwner: People @goField(forceResolver: true) + securityOwner: People @goField(forceResolver: true) + serviceStartAt: Datetime! serviceTerminationAt: Datetime serviceCriticality: ServiceCriticality! @@ -4036,6 +4057,8 @@ input CreateVendorInput { serviceTerminationAt: Datetime serviceCriticality: ServiceCriticality! riskTier: RiskTier! + businessOwnerId: ID + securityOwnerId: ID } input UpdateVendorInput { @@ -4058,6 +4081,8 @@ input UpdateVendorInput { certifications: [String!] securityPageUrl: String trustPageUrl: String + businessOwnerId: ID + securityOwnerId: ID } input DeleteVendorInput { @@ -18974,6 +18999,10 @@ func (ec *executionContext) fieldContext_UpdateVendorPayload_vendor(_ context.Co return ec.fieldContext_Vendor_description(ctx, field) case "complianceReports": return ec.fieldContext_Vendor_complianceReports(ctx, field) + case "businessOwner": + return ec.fieldContext_Vendor_businessOwner(ctx, field) + case "securityOwner": + return ec.fieldContext_Vendor_securityOwner(ctx, field) case "serviceStartAt": return ec.fieldContext_Vendor_serviceStartAt(ctx, field) case "serviceTerminationAt": @@ -19679,6 +19708,120 @@ func (ec *executionContext) fieldContext_Vendor_complianceReports(ctx context.Co return fc, nil } +func (ec *executionContext) _Vendor_businessOwner(ctx context.Context, field graphql.CollectedField, obj *types.Vendor) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vendor_businessOwner(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Vendor().BusinessOwner(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*types.People) + fc.Result = res + return ec.marshalOPeople2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐPeople(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Vendor_businessOwner(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Vendor", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_People_id(ctx, field) + case "fullName": + return ec.fieldContext_People_fullName(ctx, field) + case "primaryEmailAddress": + return ec.fieldContext_People_primaryEmailAddress(ctx, field) + case "additionalEmailAddresses": + return ec.fieldContext_People_additionalEmailAddresses(ctx, field) + case "kind": + return ec.fieldContext_People_kind(ctx, field) + case "createdAt": + return ec.fieldContext_People_createdAt(ctx, field) + case "updatedAt": + return ec.fieldContext_People_updatedAt(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type People", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Vendor_securityOwner(ctx context.Context, field graphql.CollectedField, obj *types.Vendor) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Vendor_securityOwner(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Vendor().SecurityOwner(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*types.People) + fc.Result = res + return ec.marshalOPeople2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐPeople(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Vendor_securityOwner(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Vendor", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_People_id(ctx, field) + case "fullName": + return ec.fieldContext_People_fullName(ctx, field) + case "primaryEmailAddress": + return ec.fieldContext_People_primaryEmailAddress(ctx, field) + case "additionalEmailAddresses": + return ec.fieldContext_People_additionalEmailAddresses(ctx, field) + case "kind": + return ec.fieldContext_People_kind(ctx, field) + case "createdAt": + return ec.fieldContext_People_createdAt(ctx, field) + case "updatedAt": + return ec.fieldContext_People_updatedAt(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type People", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _Vendor_serviceStartAt(ctx context.Context, field graphql.CollectedField, obj *types.Vendor) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Vendor_serviceStartAt(ctx, field) if err != nil { @@ -20485,6 +20628,10 @@ func (ec *executionContext) fieldContext_VendorComplianceReport_vendor(_ context return ec.fieldContext_Vendor_description(ctx, field) case "complianceReports": return ec.fieldContext_Vendor_complianceReports(ctx, field) + case "businessOwner": + return ec.fieldContext_Vendor_businessOwner(ctx, field) + case "securityOwner": + return ec.fieldContext_Vendor_securityOwner(ctx, field) case "serviceStartAt": return ec.fieldContext_Vendor_serviceStartAt(ctx, field) case "serviceTerminationAt": @@ -21238,6 +21385,10 @@ func (ec *executionContext) fieldContext_VendorEdge_node(_ context.Context, fiel return ec.fieldContext_Vendor_description(ctx, field) case "complianceReports": return ec.fieldContext_Vendor_complianceReports(ctx, field) + case "businessOwner": + return ec.fieldContext_Vendor_businessOwner(ctx, field) + case "securityOwner": + return ec.fieldContext_Vendor_securityOwner(ctx, field) case "serviceStartAt": return ec.fieldContext_Vendor_serviceStartAt(ctx, field) case "serviceTerminationAt": @@ -24069,7 +24220,7 @@ func (ec *executionContext) unmarshalInputCreateVendorInput(ctx context.Context, asMap[k] = v } - fieldsInOrder := [...]string{"organizationId", "name", "description", "headquarterAddress", "legalName", "websiteUrl", "privacyPolicyUrl", "category", "serviceLevelAgreementUrl", "dataProcessingAgreementUrl", "certifications", "securityPageUrl", "trustPageUrl", "statusPageUrl", "termsOfServiceUrl", "serviceStartAt", "serviceTerminationAt", "serviceCriticality", "riskTier"} + fieldsInOrder := [...]string{"organizationId", "name", "description", "headquarterAddress", "legalName", "websiteUrl", "privacyPolicyUrl", "category", "serviceLevelAgreementUrl", "dataProcessingAgreementUrl", "certifications", "securityPageUrl", "trustPageUrl", "statusPageUrl", "termsOfServiceUrl", "serviceStartAt", "serviceTerminationAt", "serviceCriticality", "riskTier", "businessOwnerId", "securityOwnerId"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -24209,6 +24360,20 @@ func (ec *executionContext) unmarshalInputCreateVendorInput(ctx context.Context, return it, err } it.RiskTier = data + case "businessOwnerId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("businessOwnerId")) + data, err := ec.unmarshalOID2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.BusinessOwnerID = data + case "securityOwnerId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("securityOwnerId")) + data, err := ec.unmarshalOID2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.SecurityOwnerID = data } } @@ -25538,7 +25703,7 @@ func (ec *executionContext) unmarshalInputUpdateVendorInput(ctx context.Context, asMap[k] = v } - fieldsInOrder := [...]string{"id", "name", "description", "serviceStartAt", "serviceTerminationAt", "serviceCriticality", "riskTier", "statusPageUrl", "termsOfServiceUrl", "privacyPolicyUrl", "serviceLevelAgreementUrl", "dataProcessingAgreementUrl", "websiteUrl", "legalName", "headquarterAddress", "category", "certifications", "securityPageUrl", "trustPageUrl"} + fieldsInOrder := [...]string{"id", "name", "description", "serviceStartAt", "serviceTerminationAt", "serviceCriticality", "riskTier", "statusPageUrl", "termsOfServiceUrl", "privacyPolicyUrl", "serviceLevelAgreementUrl", "dataProcessingAgreementUrl", "websiteUrl", "legalName", "headquarterAddress", "category", "certifications", "securityPageUrl", "trustPageUrl", "businessOwnerId", "securityOwnerId"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -25678,6 +25843,20 @@ func (ec *executionContext) unmarshalInputUpdateVendorInput(ctx context.Context, return it, err } it.TrustPageURL = data + case "businessOwnerId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("businessOwnerId")) + data, err := ec.unmarshalOID2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.BusinessOwnerID = data + case "securityOwnerId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("securityOwnerId")) + data, err := ec.unmarshalOID2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.SecurityOwnerID = data } } @@ -30634,6 +30813,72 @@ func (ec *executionContext) _Vendor(ctx context.Context, sel ast.SelectionSet, o continue } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "businessOwner": + field := field + + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Vendor_businessOwner(ctx, field, obj) + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "securityOwner": + field := field + + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Vendor_securityOwner(ctx, field, obj) + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "serviceStartAt": out.Values[i] = ec._Vendor_serviceStartAt(ctx, field, obj) diff --git a/pkg/server/api/console/v1/types/types.go b/pkg/server/api/console/v1/types/types.go index ee9c9a766..201573bf0 100644 --- a/pkg/server/api/console/v1/types/types.go +++ b/pkg/server/api/console/v1/types/types.go @@ -212,6 +212,8 @@ type CreateVendorInput struct { ServiceTerminationAt *time.Time `json:"serviceTerminationAt,omitempty"` ServiceCriticality coredata.ServiceCriticality `json:"serviceCriticality"` RiskTier coredata.RiskTier `json:"riskTier"` + BusinessOwnerID *gid.GID `json:"businessOwnerId,omitempty"` + SecurityOwnerID *gid.GID `json:"securityOwnerId,omitempty"` } type CreateVendorPayload struct { @@ -731,6 +733,8 @@ type UpdateVendorInput struct { Certifications []string `json:"certifications,omitempty"` SecurityPageURL *string `json:"securityPageUrl,omitempty"` TrustPageURL *string `json:"trustPageUrl,omitempty"` + BusinessOwnerID *gid.GID `json:"businessOwnerId,omitempty"` + SecurityOwnerID *gid.GID `json:"securityOwnerId,omitempty"` } type UpdateVendorPayload struct { @@ -775,6 +779,8 @@ type Vendor struct { Name string `json:"name"` Description *string `json:"description,omitempty"` ComplianceReports *VendorComplianceReportConnection `json:"complianceReports"` + BusinessOwner *People `json:"businessOwner,omitempty"` + SecurityOwner *People `json:"securityOwner,omitempty"` ServiceStartAt time.Time `json:"serviceStartAt"` ServiceTerminationAt *time.Time `json:"serviceTerminationAt,omitempty"` ServiceCriticality coredata.ServiceCriticality `json:"serviceCriticality"` diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go index d09e8b628..a90237601 100644 --- a/pkg/server/api/console/v1/v1_resolver.go +++ b/pkg/server/api/console/v1/v1_resolver.go @@ -377,6 +377,8 @@ func (r *mutationResolver) CreateVendor(ctx context.Context, input types.CreateV Certifications: input.Certifications, SecurityPageURL: input.SecurityPageURL, TrustPageURL: input.TrustPageURL, + BusinessOwnerID: input.BusinessOwnerID, + SecurityOwnerID: input.SecurityOwnerID, }, ) if err != nil { @@ -411,6 +413,8 @@ func (r *mutationResolver) UpdateVendor(ctx context.Context, input types.UpdateV WebsiteURL: input.WebsiteURL, Category: input.Category, Certifications: input.Certifications, + BusinessOwnerID: input.BusinessOwnerID, + SecurityOwnerID: input.SecurityOwnerID, }) if err != nil { return nil, fmt.Errorf("cannot update vendor: %w", err) @@ -1513,6 +1517,48 @@ func (r *vendorResolver) ComplianceReports(ctx context.Context, obj *types.Vendo return types.NewVendorComplianceReportConnection(page), nil } +// BusinessOwner is the resolver for the businessOwner field. +func (r *vendorResolver) BusinessOwner(ctx context.Context, obj *types.Vendor) (*types.People, error) { + svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + + vendor, err := svc.Vendors.Get(ctx, obj.ID) + if err != nil { + panic(fmt.Errorf("failed to get vendor: %w", err)) + } + + if vendor.BusinessOwnerID == nil { + return nil, nil + } + + people, err := svc.Peoples.Get(ctx, *vendor.BusinessOwnerID) + if err != nil { + panic(fmt.Errorf("failed to get business owner: %w", err)) + } + + return types.NewPeople(people), nil +} + +// SecurityOwner is the resolver for the securityOwner field. +func (r *vendorResolver) SecurityOwner(ctx context.Context, obj *types.Vendor) (*types.People, error) { + svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + + vendor, err := svc.Vendors.Get(ctx, obj.ID) + if err != nil { + panic(fmt.Errorf("failed to get vendor: %w", err)) + } + + if vendor.SecurityOwnerID == nil { + return nil, nil + } + + people, err := svc.Peoples.Get(ctx, *vendor.SecurityOwnerID) + if err != nil { + panic(fmt.Errorf("failed to get security owner: %w", err)) + } + + return types.NewPeople(people), nil +} + // Vendor is the resolver for the vendor field. func (r *vendorComplianceReportResolver) Vendor(ctx context.Context, obj *types.VendorComplianceReport) (*types.Vendor, error) { svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID())