Add vendor business and security owner

Signed-off-by: gearnode <bryan@frimin.fr>
This commit is contained in:
gearnode
2025-04-12 14:53:16 -07:00
parent 8261e13857
commit 9b868220f6
19 changed files with 908 additions and 240 deletions

View File

@@ -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

View File

@@ -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() {
<Route path="people" element={<PeopleListPage />} />
<Route path="people/new" element={<NewPeoplePage />} />
<Route path="people/:peopleId" element={<PeoplePage />} />
<Route path="vendors" element={<VendorListPage />} />
<Route path="vendors" element={<ListVendorPage />} />
<Route path="frameworks" element={<FrameworkListPage />} />
<Route path="frameworks/new" element={<NewFrameworkPage />} />
<Route path="frameworks/:frameworkId/*">

View File

@@ -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 (
<PageTemplateSkeleton
title="Vendors"
@@ -34,11 +34,11 @@ export function VendorListViewSkeleton() {
);
}
export function VendorListPage() {
export function ListVendorPage() {
const location = useLocation();
return (
<Suspense key={location.pathname} fallback={<VendorListViewSkeleton />}>
<Suspense key={location.pathname} fallback={<ListVendorViewSkeleton />}>
<ErrorBoundaryWithLocation>
<VendorListView />
</ErrorBoundaryWithLocation>

View File

@@ -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<VendorListViewQueryType>;
queryRef: PreloadedQuery<ListVendorViewQuery>;
}) {
const { toast } = useToast();
const data = usePreloadedQuery<VendorListViewQueryType>(
vendorListViewQuery,
const data = usePreloadedQuery<ListVendorViewQuery>(
listVendorViewQuery,
queryRef
);
const [, setSearchParams] = useSearchParams();
@@ -200,9 +200,9 @@ function VendorListContent({
const [vendorsData, setVendorsData] = useState<VendorData[]>([]);
const [isLoadingVendors, setIsLoadingVendors] = useState(false);
const [createVendor] =
useMutation<VendorListViewCreateVendorMutation>(createVendorMutation);
useMutation<ListVendorViewCreateVendorMutation>(createVendorMutation);
const [deleteVendor] =
useMutation<VendorListViewDeleteVendorMutation>(deleteVendorMutation);
useMutation<ListVendorViewDeleteVendorMutation>(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({
</Avatar>
<div className="flex items-center gap-2">
<p className="font-medium">{vendor?.name}</p>
{vendor?.description && (
<>
<span className="text-tertiary">•</span>
<p className="text-sm text-tertiary">
{vendor.description}
</p>
</>
)}
</div>
</div>
<div className="flex items-center gap-2">
@@ -484,10 +476,10 @@ function VendorListContent({
);
}
export default function VendorListView() {
export default function ListVendorView() {
const [searchParams] = useSearchParams();
const [queryRef, loadQuery] =
useQueryLoader<VendorListViewQueryType>(vendorListViewQuery);
useQueryLoader<ListVendorViewQuery>(listVendorViewQuery);
const { organizationId } = useParams();
@@ -505,12 +497,12 @@ export default function VendorListView() {
}, [loadQuery, organizationId]);
if (!queryRef) {
return <VendorListViewSkeleton />;
return <ListVendorViewSkeleton />;
}
return (
<Suspense fallback={<VendorListViewSkeleton />}>
<VendorListContent queryRef={queryRef} />
<Suspense fallback={<ListVendorViewSkeleton />}>
<ListVendorContent queryRef={queryRef} />
</Suspense>
);
}

View File

@@ -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 (
<div className="space-y-2">
@@ -151,6 +173,7 @@ function EditableField({
type={type}
value={value}
onChange={(e) => onChange(e.target.value)}
disabled={disabled}
/>
{helpText && <p className="text-sm text-secondary">{helpText}</p>}
</div>
@@ -348,6 +371,7 @@ function VendorViewContent({
}: {
queryRef: PreloadedQuery<VendorViewQueryType>;
}) {
const { organizationId } = useParams();
const data = usePreloadedQuery(vendorViewQuery, queryRef);
const [editedFields, setEditedFields] = useState<Set<string>>(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<VendorViewUpdateVendorMutation>(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 (
<PageTemplate title={formData.name}>
<div className="max-w-2xl space-y-6">
<EditableField
label="Name"
value={formData.name}
onChange={(value) => handleFieldChange("name", value)}
/>
<Card className="p-6">
<div className="space-y-4">
<div className="space-y-2">
<h2 className="text-lg font-medium">Basic Information</h2>
<p className="text-sm text-secondary">
General information about the vendor
</p>
</div>
<EditableField
label="Description"
value={formData.description}
onChange={(value) => handleFieldChange("description", value)}
/>
<div className="space-y-4">
<EditableField
label="Name"
value={formData.name}
onChange={(value) => handleFieldChange("name", value)}
/>
<EditableField
label="Legal Name"
value={formData.legalName}
onChange={(value) => handleFieldChange("legalName", value)}
/>
<EditableField
label="Description"
value={formData.description}
onChange={(value) => handleFieldChange("description", value)}
/>
<EditableField
label="Headquarter Address"
value={formData.headquarterAddress}
onChange={(value) => handleFieldChange("headquarterAddress", value)}
/>
<EditableField
label="Legal Name"
value={formData.legalName}
onChange={(value) => handleFieldChange("legalName", value)}
/>
<EditableField
label="Website URL"
value={formData.websiteUrl}
onChange={(value) => handleFieldChange("websiteUrl", value)}
/>
<EditableField
label="Headquarter Address"
value={formData.headquarterAddress}
onChange={(value) =>
handleFieldChange("headquarterAddress", value)
}
/>
<EditableField
label="Website URL"
value={formData.websiteUrl}
onChange={(value) => handleFieldChange("websiteUrl", value)}
/>
</div>
</div>
</Card>
<Card className="p-6">
<div className="space-y-4">
<div className="space-y-2">
<h2 className="text-lg font-medium">Service Information</h2>
<h2 className="text-lg font-medium">Ownership Information</h2>
<p className="text-sm text-secondary">
Basic information about the vendor service
Individuals responsible for this vendor
</p>
</div>
<div className="space-y-4">
<div className="space-y-2">
<div className="flex items-center gap-2">
<User className="h-4 w-4 text-tertiary" />
<Label className="text-sm">Business Owner</Label>
</div>
<PeopleSelector
organizationRef={data.organization}
selectedPersonId={formData.businessOwnerId}
onSelect={(value) =>
handleFieldChange("businessOwnerId", value)
}
placeholder="Select business owner (optional)"
/>
<p className="text-sm text-secondary">
The person responsible for business decisions related to this
vendor
</p>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<User className="h-4 w-4 text-tertiary" />
<Label className="text-sm">Security Owner</Label>
</div>
<PeopleSelector
organizationRef={data.organization}
selectedPersonId={formData.securityOwnerId}
onSelect={(value) =>
handleFieldChange("securityOwnerId", value)
}
placeholder="Select security owner (optional)"
/>
<p className="text-sm text-secondary">
The person responsible for security oversight of this vendor
</p>
</div>
</div>
</div>
</Card>
<Card className="p-6">
<div className="space-y-4">
<div className="space-y-2">
<h2 className="text-lg font-medium">
Risk & Service Information
</h2>
<p className="text-sm text-secondary">
Information about service criticality and risk
</p>
</div>
@@ -735,7 +854,7 @@ function VendorViewContent({
<Card className="p-6">
<div className="space-y-4">
<div className="space-y-2">
<h2 className="text-lg font-medium">URLs</h2>
<h2 className="text-lg font-medium">Documentation & Links</h2>
<p className="text-sm text-secondary">
Important URLs related to the vendor
</p>
@@ -860,13 +979,13 @@ function VendorViewContent({
}
export default function VendorView() {
const { vendorId } = useParams();
const { vendorId, organizationId } = useParams();
const [queryRef, loadQuery] =
useQueryLoader<VendorViewQueryType>(vendorViewQuery);
useEffect(() => {
loadQuery({ vendorId: vendorId! });
}, [loadQuery, vendorId]);
loadQuery({ vendorId: vendorId!, organizationId: organizationId! });
}, [loadQuery, vendorId, organizationId]);
if (!queryRef) {
return <VendorViewSkeleton />;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<d8ebd7f4de7a9e2e314f93e5e6b61e25>>
* @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<string> | 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<string>;
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;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<a46552774a4d24ce7d1849d7485fb323>>
* @generated SignedSource<<c376281753e99a7cada0872375288f35>>
* @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<string>;
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;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<e166844caf05e2dfc32321c2547cbf32>>
* @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;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<843c11acfe2ed3f07fe2e05237f4cb08>>
* @generated SignedSource<<faaaba37922ca105627be876b05672a7>>
* @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;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<dce76b1271e95cbf0d70b18be492a389>>
* @generated SignedSource<<b85fbf05b6b7f76bd8ceb87383bfd4b3>>
* @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;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<f69485d1f583f57e60850e2bc6428763>>
* @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<string>;
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;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<0e66de0cfba2c27bbf78dcb8e846ad8d>>
* @generated SignedSource<<e8b13d28eed7587f5fbefcc073c53dc4>>
* @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<string> | 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<string>;
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;

View File

@@ -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;

View File

@@ -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())

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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"`

View File

@@ -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())