diff --git a/apps/console/package.json b/apps/console/package.json index ceea7ab67..dafeed42e 100644 --- a/apps/console/package.json +++ b/apps/console/package.json @@ -21,7 +21,7 @@ "@probo/relay": "^1.0.0", "@probo/routes": "^1.0.0", "@probo/ui": "1.0.0", - "@probo/vendors": "0.0.1", + "@probo/third-parties": "0.0.1", "@tanstack/react-query": "^5.76.1", "clsx": "^2.1.1", "react": "^19.1.0", diff --git a/apps/console/src/components/assets/AssetsTable.tsx b/apps/console/src/components/assets/AssetsTable.tsx index ac9a3fbf3..13454602d 100644 --- a/apps/console/src/components/assets/AssetsTable.tsx +++ b/apps/console/src/components/assets/AssetsTable.tsx @@ -48,7 +48,7 @@ import { useOrganizationId } from "#/hooks/useOrganizationId"; import { EditableTable } from "../table/EditableTable"; import { PeopleCell } from "../table/PeopleCell"; -import { VendorsCell } from "../table/VendorsCell"; +import { ThirdPartiesCell } from "../table/ThirdPartiesCell"; type Props = { connectionId: string; @@ -65,7 +65,7 @@ const schema = z.object({ amount: z.coerce.number().min(1, "Amount is required"), assetType: z.enum(["PHYSICAL", "VIRTUAL"]), ownerId: z.string().trim().min(1, "Owner is required"), - vendorIds: z.array(z.string()).optional(), + thirdPartyIds: z.array(z.string()).optional(), dataTypesStored: z.string().trim().min(1, "Data types stored is required"), organizationId: z.string().trim().min(1, "Organization is required"), }); @@ -75,7 +75,7 @@ const defaultValue = { amount: 0, assetType: "VIRTUAL", ownerId: "", - vendorIds: [], + thirdPartyIds: [], dataTypesStored: "", organizationId: "", } satisfies z.infer; @@ -99,7 +99,7 @@ export function AssetsTable(props: Props) { __("Data Types stored"), __("Amount"), __("Owner"), - __("Vendors"), + __("Third parties"), ]} schema={schema} updateMutation={updateAssetMutation} @@ -154,10 +154,10 @@ export function AssetsTable(props: Props) { defaultValue={item?.owner} organizationId={organizationId} /> - edge.node) ?? []} + defaultValue={item?.thirdParties?.edges?.map(edge => edge.node) ?? []} /> )} diff --git a/apps/console/src/components/assets/ReadOnlyAssetsTable.tsx b/apps/console/src/components/assets/ReadOnlyAssetsTable.tsx index fabf299b2..0adcf010c 100644 --- a/apps/console/src/components/assets/ReadOnlyAssetsTable.tsx +++ b/apps/console/src/components/assets/ReadOnlyAssetsTable.tsx @@ -50,7 +50,7 @@ export function ReadOnlyAssetsTable(props: Props) { {__("Type")} {__("Amount")} {__("Owner")} - {__("Vendors")} + {__("Third parties")} @@ -65,7 +65,7 @@ export function ReadOnlyAssetsTable(props: Props) { function AssetRow({ entry }: { entry: AssetEntry }) { const organizationId = useOrganizationId(); const { __ } = useTranslate(); - const vendors = entry.vendors?.edges.map(edge => edge.node) ?? []; + const thirdParties = entry.thirdParties?.edges.map(edge => edge.node) ?? []; return ( @@ -78,27 +78,27 @@ function AssetRow({ entry }: { entry: AssetEntry }) { {entry.amount} {entry.owner?.fullName ?? __("Unassigned")} - {vendors.length > 0 + {thirdParties.length > 0 ? (
- {vendors.slice(0, 3).map(vendor => ( + {thirdParties.slice(0, 3).map(thirdParty => ( - {vendor.name} + {thirdParty.name} ))} - {vendors.length > 3 && ( + {thirdParties.length > 3 && ( + - {vendors.length - 3} + {thirdParties.length - 3} )}
diff --git a/apps/console/src/components/form/VendorsMultiSelectField.tsx b/apps/console/src/components/form/ThirdPartiesMultiSelectField.tsx similarity index 57% rename from apps/console/src/components/form/VendorsMultiSelectField.tsx rename to apps/console/src/components/form/ThirdPartiesMultiSelectField.tsx index 3752a26ed..08f1814a8 100644 --- a/apps/console/src/components/form/VendorsMultiSelectField.tsx +++ b/apps/console/src/components/form/ThirdPartiesMultiSelectField.tsx @@ -18,9 +18,9 @@ import { Avatar, Badge, Button, Field, IconCrossLargeX, Option, Select } from "@ import { type ComponentProps, Suspense, useState } from "react"; import { type Control, Controller, type FieldValues, type Path } from "react-hook-form"; -import { useVendors } from "#/hooks/graph/VendorGraph"; +import { useThirdParties } from "#/hooks/graph/ThirdPartyGraph"; -type Vendor = { +type ThirdParty = { id: string; name: string; websiteUrl: string | null | undefined; @@ -32,13 +32,13 @@ type Props = { name: string; label?: string; error?: string; - selectedVendors?: Vendor[]; + selectedThirdParties?: ThirdParty[]; } & ComponentProps; -export function VendorsMultiSelectField({ +export function ThirdPartiesMultiSelectField({ organizationId, control, - selectedVendors = [], + selectedThirdParties = [], ...props }: Props) { return ( @@ -46,31 +46,31 @@ export function VendorsMultiSelectField({ } > - ); } -function VendorsMultiSelectWithQuery( - props: Pick, "organizationId" | "control" | "name" | "disabled" | "selectedVendors">, +function ThirdPartiesMultiSelectWithQuery( + props: Pick, "organizationId" | "control" | "name" | "disabled" | "selectedThirdParties">, ) { const { __ } = useTranslate(); - const { name, organizationId, control, selectedVendors = [] } = props; - const vendors = useVendors(organizationId); + const { name, organizationId, control, selectedThirdParties = [] } = props; + const thirdParties = useThirdParties(organizationId); const [isOpen, setIsOpen] = useState(false); - const allVendors = [...vendors]; + const allThirdParties = [...thirdParties]; if (props.disabled) { - selectedVendors.forEach((selectedVendor) => { - if (!allVendors.find(v => v.id === selectedVendor.id)) { - allVendors.push(selectedVendor); + selectedThirdParties.forEach((selectedThirdParty) => { + if (!allThirdParties.find(v => v.id === selectedThirdParty.id)) { + allThirdParties.push(selectedThirdParty); } }); } @@ -81,49 +81,49 @@ function VendorsMultiSelectWithQuery( control={control} name={name as Path} render={({ field }) => { - const selectedVendorIds = (Array.isArray(field.value) ? field.value : []) as string[]; + const selectedThirdPartyIds = (Array.isArray(field.value) ? field.value : []) as string[]; - const selectedVendors = allVendors.filter(v => selectedVendorIds.includes(v.id)); - const availableVendors = allVendors.filter(v => !selectedVendorIds.includes(v.id)); + const selectedThirdParties = allThirdParties.filter(v => selectedThirdPartyIds.includes(v.id)); + const availableThirdParties = allThirdParties.filter(v => !selectedThirdPartyIds.includes(v.id)); - const handleAddVendor = (vendorId: string) => { - const newValue = [...selectedVendorIds, vendorId]; + const handleAddThirdParty = (thirdPartyId: string) => { + const newValue = [...selectedThirdPartyIds, thirdPartyId]; field.onChange(newValue); setIsOpen(false); }; - const handleRemoveVendor = (vendorId: string) => { - const newValue = selectedVendorIds.filter((id: string) => id !== vendorId); + const handleRemoveThirdParty = (thirdPartyId: string) => { + const newValue = selectedThirdPartyIds.filter((id: string) => id !== thirdPartyId); field.onChange(newValue); }; return (
- {availableVendors.length > 0 && !props.disabled && ( + {availableThirdParties.length > 0 && !props.disabled && ( )} - {selectedVendors.length > 0 && ( + {selectedThirdParties.length > 0 && (
- {selectedVendors.map(vendor => ( - + {selectedThirdParties.map(thirdParty => ( + - {vendor.name} + {thirdParty.name} {!props.disabled && (
)} - {selectedVendors.length === 0 && availableVendors.length === 0 && ( + {selectedThirdParties.length === 0 && availableThirdParties.length === 0 && (
- {__("No vendors available")} + {__("No third parties available")}
)}
diff --git a/apps/console/src/components/table/VendorsCell.tsx b/apps/console/src/components/table/ThirdPartiesCell.tsx similarity index 68% rename from apps/console/src/components/table/VendorsCell.tsx rename to apps/console/src/components/table/ThirdPartiesCell.tsx index 04469b5dc..09d8b93aa 100644 --- a/apps/console/src/components/table/VendorsCell.tsx +++ b/apps/console/src/components/table/ThirdPartiesCell.tsx @@ -15,11 +15,11 @@ import { faviconUrl } from "@probo/helpers"; import { Avatar, Badge, IconCrossLargeX } from "@probo/ui"; -import type { VendorGraphSelectQuery } from "#/__generated__/core/VendorGraphSelectQuery.graphql"; +import type { ThirdPartyGraphSelectQuery } from "#/__generated__/core/ThirdPartyGraphSelectQuery.graphql"; import { GraphQLCell } from "#/components/table/GraphQLCell"; -import { vendorsSelectQuery } from "#/hooks/graph/VendorGraph"; +import { thirdPartiesSelectQuery } from "#/hooks/graph/ThirdPartyGraph"; -type Vendor = { +type ThirdParty = { id: string; name: string; websiteUrl: string | null | undefined; @@ -27,47 +27,47 @@ type Vendor = { type Props = { name: string; - defaultValue?: Vendor[]; + defaultValue?: ThirdParty[]; organizationId: string; }; -const empty = [] as Vendor[]; +const empty = [] as ThirdParty[]; -export function VendorsCell(props: Props) { +export function ThirdPartiesCell(props: Props) { return ( - + multiple name={props.name} - query={vendorsSelectQuery} + query={thirdPartiesSelectQuery} variables={{ organizationId: props.organizationId, }} items={data => - data.organization?.vendors?.edges?.map(edge => edge.node) ?? []} + data.organization?.thirdParties?.edges?.map(edge => edge.node) ?? []} itemRenderer={({ item, onRemove }) => ( - + )} defaultValue={props.defaultValue ?? empty} /> ); } -function VendorBadge({ - vendor, +function ThirdPartyBadge({ + thirdParty, onRemove, }: { - vendor: Vendor; - onRemove?: (v: Vendor) => void; + thirdParty: ThirdParty; + onRemove?: (v: ThirdParty) => void; }) { return ( - + - {vendor.name} + {thirdParty.name} {onRemove && ( + )} + + + ); +}; diff --git a/apps/console/src/pages/organizations/compliance-page/vendors/_components/CompliancePageVendorListItem.tsx b/apps/console/src/pages/organizations/compliance-page/vendors/_components/CompliancePageVendorListItem.tsx deleted file mode 100644 index 4045833d6..000000000 --- a/apps/console/src/pages/organizations/compliance-page/vendors/_components/CompliancePageVendorListItem.tsx +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -import { useTranslate } from "@probo/i18n"; -import { Badge, Button, IconCheckmark1, IconCrossLargeX, Td, Tr } from "@probo/ui"; -import { useFragment } from "react-relay"; -import { graphql } from "relay-runtime"; - -import type { CompliancePageVendorListItem_vendorFragment$key } from "#/__generated__/core/CompliancePageVendorListItem_vendorFragment.graphql"; -import type { CompliancePageVendorListItemMutation } from "#/__generated__/core/CompliancePageVendorListItemMutation.graphql"; -import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; -import { useOrganizationId } from "#/hooks/useOrganizationId"; - -const vendorFragment = graphql` - fragment CompliancePageVendorListItem_vendorFragment on Vendor { - id - category - name - showOnTrustCenter - canUpdate: permission(action: "core:vendor:update") - } -`; - -const updateVendorVisibilityMutation = graphql` - mutation CompliancePageVendorListItemMutation($input: UpdateVendorInput!) { - updateVendor(input: $input) { - vendor { - id - showOnTrustCenter - ...CompliancePageVendorListItem_vendorFragment - } - } - } -`; - -export function CompliancePageVendorListItem(props: { - vendorFragmentRef: CompliancePageVendorListItem_vendorFragment$key; -}) { - const { vendorFragmentRef } = props; - - const organizationId = useOrganizationId(); - const { __ } = useTranslate(); - - const vendor = useFragment( - vendorFragment, - vendorFragmentRef, - ); - const [updateVendorVisibility, isUpadtingVendorVisibility] = useMutationWithToasts< - CompliancePageVendorListItemMutation - >( - updateVendorVisibilityMutation, - { - successMessage: __("Subprocessor visibility updated successfully."), - errorMessage: __("Failed to update subprocessor visibility"), - }, - ); - - return ( - - -
{vendor.name}
- - - {vendor.category} - - - - {vendor.showOnTrustCenter ? __("Visible") : __("None")} - - - - {vendor.canUpdate && ( - - )} - - - ); -}; diff --git a/apps/console/src/pages/organizations/data/DataPage.tsx b/apps/console/src/pages/organizations/data/DataPage.tsx index cf41ee3c3..ecfc324c3 100644 --- a/apps/console/src/pages/organizations/data/DataPage.tsx +++ b/apps/console/src/pages/organizations/data/DataPage.tsx @@ -81,7 +81,7 @@ const paginatedDataFragment = graphql` owner { fullName } - vendors(first: 50) { + thirdParties(first: 50) { edges { node { id @@ -192,7 +192,7 @@ export default function DataPage(props: Props) { {__("Name")} {__("Classification")} {__("Owner")} - {__("Vendors")} + {__("Third parties")} {hasAnyAction && } @@ -223,7 +223,7 @@ function DataRow({ const organizationId = useOrganizationId(); const { __ } = useTranslate(); const deleteDatum = useDeleteDatum(entry, connectionId); - const vendors = entry.vendors?.edges.map(edge => edge.node) ?? []; + const thirdParties = entry.thirdParties?.edges.map(edge => edge.node) ?? []; const detailUrl = `/organizations/${organizationId}/data/${entry.id}`; return ( @@ -234,27 +234,27 @@ function DataRow({ {entry.owner?.fullName ?? __("Unassigned")} - {vendors.length > 0 + {thirdParties.length > 0 ? (
- {vendors.slice(0, 3).map(vendor => ( + {thirdParties.slice(0, 3).map(thirdParty => ( - {vendor.name} + {thirdParty.name} ))} - {vendors.length > 3 && ( + {thirdParties.length > 3 && ( + - {vendors.length - 3} + {thirdParties.length - 3} )}
diff --git a/apps/console/src/pages/organizations/data/DatumDetailsPage.tsx b/apps/console/src/pages/organizations/data/DatumDetailsPage.tsx index b0c3f1d85..59866dbae 100644 --- a/apps/console/src/pages/organizations/data/DatumDetailsPage.tsx +++ b/apps/console/src/pages/organizations/data/DatumDetailsPage.tsx @@ -33,7 +33,7 @@ import { z } from "zod"; import type { DatumGraphNodeQuery } from "#/__generated__/core/DatumGraphNodeQuery.graphql"; import { ControlledField } from "#/components/form/ControlledField"; import { PeopleSelectField } from "#/components/form/PeopleSelectField"; -import { VendorsMultiSelectField } from "#/components/form/VendorsMultiSelectField"; +import { ThirdPartiesMultiSelectField } from "#/components/form/ThirdPartiesMultiSelectField"; import { datumNodeQuery, useDeleteDatum, @@ -46,7 +46,7 @@ const updateDatumSchema = z.object({ name: z.string().min(1, "Name is required"), dataClassification: z.enum(["PUBLIC", "INTERNAL", "CONFIDENTIAL", "SECRET"]), ownerId: z.string().min(1, "Owner is required"), - vendorIds: z.array(z.string()).optional(), + thirdPartyIds: z.array(z.string()).optional(), }); type Props = { @@ -69,8 +69,8 @@ export default function DatumDetailsPage(props: Props) { ConnectionHandler.getConnectionID(organizationId, "DataPage_data"), ); - const vendors = datumEntry?.vendors?.edges.map(edge => edge.node) ?? []; - const vendorIds = vendors.map(vendor => vendor.id); + const thirdParties = datumEntry?.thirdParties?.edges.map(edge => edge.node) ?? []; + const thirdPartyIds = thirdParties.map(thirdParty => thirdParty.id); const { control, formState, handleSubmit, register, reset } = useFormWithSchema(updateDatumSchema, { @@ -78,7 +78,7 @@ export default function DatumDetailsPage(props: Props) { name: datumEntry?.name || "", dataClassification: datumEntry?.dataClassification || "PUBLIC", ownerId: datumEntry?.owner?.id || "", - vendorIds: vendorIds, + thirdPartyIds: thirdPartyIds, }, }); @@ -161,13 +161,13 @@ export default function DatumDetailsPage(props: Props) { disabled={!datumEntry.canUpdate} /> -
diff --git a/apps/console/src/pages/organizations/data/dialogs/CreateDatumDialog.tsx b/apps/console/src/pages/organizations/data/dialogs/CreateDatumDialog.tsx index 53cbdd603..7b5c525a2 100644 --- a/apps/console/src/pages/organizations/data/dialogs/CreateDatumDialog.tsx +++ b/apps/console/src/pages/organizations/data/dialogs/CreateDatumDialog.tsx @@ -27,7 +27,7 @@ import { z } from "zod"; import { ControlledField } from "#/components/form/ControlledField"; import { PeopleSelectField } from "#/components/form/PeopleSelectField"; -import { VendorsMultiSelectField } from "#/components/form/VendorsMultiSelectField"; +import { ThirdPartiesMultiSelectField } from "#/components/form/ThirdPartiesMultiSelectField"; import { useFormWithSchema } from "#/hooks/useFormWithSchema"; import { useCreateDatum } from "../../../../hooks/graph/DatumGraph"; @@ -36,7 +36,7 @@ const schema = z.object({ name: z.string().min(1, "Name is required"), dataClassification: z.enum(["PUBLIC", "INTERNAL", "CONFIDENTIAL", "SECRET"]), ownerId: z.string().min(1, "Owner is required"), - vendorIds: z.array(z.string()).optional(), + thirdPartyIds: z.array(z.string()).optional(), }); type Props = { @@ -59,7 +59,7 @@ export function CreateDatumDialog({ name: "", dataClassification: "PUBLIC", ownerId: "", - vendorIds: [], + thirdPartyIds: [], }, }); const ref = useDialogRef(); @@ -105,11 +105,11 @@ export function CreateDatumDialog({ name="ownerId" label={__("Owner")} /> - diff --git a/apps/console/src/pages/organizations/processingActivities/ProcessingActivityDetailsPage.tsx b/apps/console/src/pages/organizations/processingActivities/ProcessingActivityDetailsPage.tsx index 4ed0ef99b..8d2fa83d6 100644 --- a/apps/console/src/pages/organizations/processingActivities/ProcessingActivityDetailsPage.tsx +++ b/apps/console/src/pages/organizations/processingActivities/ProcessingActivityDetailsPage.tsx @@ -46,7 +46,7 @@ import { z } from "zod"; import type { ProcessingActivityGraphNodeQuery } from "#/__generated__/core/ProcessingActivityGraphNodeQuery.graphql"; import { PeopleSelectField } from "#/components/form/PeopleSelectField"; -import { VendorsMultiSelectField } from "#/components/form/VendorsMultiSelectField"; +import { ThirdPartiesMultiSelectField } from "#/components/form/ThirdPartiesMultiSelectField"; import { useFormWithSchema } from "#/hooks/useFormWithSchema"; import { useOrganizationId } from "#/hooks/useOrganizationId"; @@ -102,7 +102,7 @@ const updateProcessingActivitySchema = z.object({ nextReviewDate: z.string().optional(), role: z.enum(["CONTROLLER", "PROCESSOR"] as const), dataProtectionOfficerId: z.string().optional(), - vendorIds: z.array(z.string()).optional(), + thirdPartyIds: z.array(z.string()).optional(), }); type Props = { @@ -199,8 +199,8 @@ export default function ProcessingActivityDetailsPage(props: Props) { connectionId, ); - const vendors = activity?.vendors?.edges.map(edge => edge.node) ?? []; - const vendorIds = vendors.map(vendor => vendor.id); + const thirdParties = activity?.thirdParties?.edges.map(edge => edge.node) ?? []; + const thirdPartyIds = thirdParties.map(thirdParty => thirdParty.id); const { register, handleSubmit, formState, control } = useFormWithSchema( updateProcessingActivitySchema, @@ -229,7 +229,7 @@ export default function ProcessingActivityDetailsPage(props: Props) { nextReviewDate: toDateInput(activity.nextReviewDate), role: activity.role || ("CONTROLLER" as const), dataProtectionOfficerId: activity.dataProtectionOfficer?.id || "", - vendorIds: vendorIds, + thirdPartyIds: thirdPartyIds, }, }, ); @@ -287,7 +287,7 @@ export default function ProcessingActivityDetailsPage(props: Props) { nextReviewDate: formatDatetime(formData.nextReviewDate) ?? null, role: formData.role, dataProtectionOfficerId: formData.dataProtectionOfficerId || null, - vendorIds: formData.vendorIds, + thirdPartyIds: formData.thirdPartyIds, }); toast({ @@ -777,12 +777,12 @@ export default function ProcessingActivityDetailsPage(props: Props) {
- diff --git a/apps/console/src/pages/organizations/processingActivities/dialogs/CreateProcessingActivityDialog.tsx b/apps/console/src/pages/organizations/processingActivities/dialogs/CreateProcessingActivityDialog.tsx index ca40e5ecf..10f2284a4 100644 --- a/apps/console/src/pages/organizations/processingActivities/dialogs/CreateProcessingActivityDialog.tsx +++ b/apps/console/src/pages/organizations/processingActivities/dialogs/CreateProcessingActivityDialog.tsx @@ -34,7 +34,7 @@ import { Controller } from "react-hook-form"; import { z } from "zod"; import { PeopleSelectField } from "#/components/form/PeopleSelectField"; -import { VendorsMultiSelectField } from "#/components/form/VendorsMultiSelectField"; +import { ThirdPartiesMultiSelectField } from "#/components/form/ThirdPartiesMultiSelectField"; import { useFormWithSchema } from "#/hooks/useFormWithSchema"; import { @@ -67,7 +67,7 @@ const schema = z.object({ nextReviewDate: z.string().optional(), role: z.enum(["CONTROLLER", "PROCESSOR"] as const), dataProtectionOfficerId: z.string().optional(), - vendorIds: z.array(z.string()).optional(), + thirdPartyIds: z.array(z.string()).optional(), }); type FormData = z.infer; @@ -110,7 +110,7 @@ export function CreateProcessingActivityDialog({ nextReviewDate: "", role: "PROCESSOR" as const, dataProtectionOfficerId: "", - vendorIds: [], + thirdPartyIds: [], }, }); @@ -137,7 +137,7 @@ export function CreateProcessingActivityDialog({ nextReviewDate: formatDatetime(formData.nextReviewDate), role: formData.role, dataProtectionOfficerId: formData.dataProtectionOfficerId || undefined, - vendorIds: formData.vendorIds, + thirdPartyIds: formData.thirdPartyIds, }); toast({ @@ -430,12 +430,12 @@ export function CreateProcessingActivityDialog({ - diff --git a/apps/console/src/pages/organizations/settings/WebhooksSettingsPage.tsx b/apps/console/src/pages/organizations/settings/WebhooksSettingsPage.tsx index 88fbb223e..c9292bc31 100644 --- a/apps/console/src/pages/organizations/settings/WebhooksSettingsPage.tsx +++ b/apps/console/src/pages/organizations/settings/WebhooksSettingsPage.tsx @@ -155,9 +155,9 @@ const deleteWebhookSubscriptionMutation = graphql` `; const EVENT_TYPES = [ - { value: "VENDOR_CREATED", label: "vendor:created" }, - { value: "VENDOR_UPDATED", label: "vendor:updated" }, - { value: "VENDOR_DELETED", label: "vendor:deleted" }, + { value: "THIRD_PARTY_CREATED", label: "third-party:created" }, + { value: "THIRD_PARTY_UPDATED", label: "third-party:updated" }, + { value: "THIRD_PARTY_DELETED", label: "third-party:deleted" }, { value: "USER_CREATED", label: "user:created" }, { value: "USER_UPDATED", label: "user:updated" }, { value: "USER_DELETED", label: "user:deleted" }, diff --git a/apps/console/src/pages/organizations/vendors/VendorsPage.tsx b/apps/console/src/pages/organizations/third-parties/ThirdPartiesPage.tsx similarity index 62% rename from apps/console/src/pages/organizations/vendors/VendorsPage.tsx rename to apps/console/src/pages/organizations/third-parties/ThirdPartiesPage.tsx index d318184df..f3f8c614d 100644 --- a/apps/console/src/pages/organizations/vendors/VendorsPage.tsx +++ b/apps/console/src/pages/organizations/third-parties/ThirdPartiesPage.tsx @@ -39,75 +39,75 @@ import { } from "react-relay"; import { useNavigate } from "react-router"; -import type { VendorGraphListQuery } from "#/__generated__/core/VendorGraphListQuery.graphql"; +import type { ThirdPartyGraphListQuery } from "#/__generated__/core/ThirdPartyGraphListQuery.graphql"; import type { - VendorGraphPaginatedFragment$data, - VendorGraphPaginatedFragment$key, -} from "#/__generated__/core/VendorGraphPaginatedFragment.graphql"; + ThirdPartyGraphPaginatedFragment$data, + ThirdPartyGraphPaginatedFragment$key, +} from "#/__generated__/core/ThirdPartyGraphPaginatedFragment.graphql"; import { SortableTable, SortableTh } from "#/components/SortableTable"; import { - paginatedVendorsFragment, - useDeleteVendor, - vendorsQuery, -} from "#/hooks/graph/VendorGraph"; + paginatedThirdPartiesFragment, + thirdPartiesQuery, + useDeleteThirdParty, +} from "#/hooks/graph/ThirdPartyGraph"; import { useOrganizationId } from "#/hooks/useOrganizationId"; import type { NodeOf } from "#/types"; -import { CreateVendorDialog } from "./dialogs/CreateVendorDialog"; -import { PublishVendorListDialog } from "./dialogs/PublishVendorListDialog"; +import { CreateThirdPartyDialog } from "./dialogs/CreateThirdPartyDialog"; +import { PublishThirdPartyListDialog } from "./dialogs/PublishThirdPartyListDialog"; -type Vendor = NodeOf; +type ThirdParty = NodeOf; type Props = { - queryRef: PreloadedQuery; + queryRef: PreloadedQuery; }; -export default function VendorsPage(props: Props) { +export default function ThirdPartiesPage(props: Props) { const { __ } = useTranslate(); const organizationId = useOrganizationId(); const navigate = useNavigate(); - const data = usePreloadedQuery(vendorsQuery, props.queryRef); + const data = usePreloadedQuery(thirdPartiesQuery, props.queryRef); // eslint-disable-next-line relay/generated-typescript-types const pagination = usePaginationFragment( - paginatedVendorsFragment, - data.node as VendorGraphPaginatedFragment$key, + paginatedThirdPartiesFragment, + data.node as ThirdPartyGraphPaginatedFragment$key, ); - const vendors = pagination.data.vendors?.edges.map(edge => edge.node); - const connectionId = pagination.data.vendors.__id; + const thirdParties = pagination.data.thirdParties?.edges.map(edge => edge.node); + const connectionId = pagination.data.thirdParties.__id; - usePageTitle(__("Vendors")); + usePageTitle(__("Third parties")); const hasAnyAction - = vendors.some(({ canUpdate, canDelete }) => canUpdate || canDelete); + = thirdParties.some(({ canUpdate, canDelete }) => canUpdate || canDelete); - const vendorsDocument = data.node?.vendorsDocument; + const thirdPartiesDocument = data.node?.thirdPartiesDocument; const defaultApproverIds - = vendorsDocument?.defaultApprovers?.map(a => a.id) ?? []; + = thirdPartiesDocument?.defaultApprovers?.map(a => a.id) ?? []; return (
- {vendorsDocument && ( + {thirdPartiesDocument && ( )} - {data.node.canPublishVendor && ( - void navigate( @@ -117,22 +117,22 @@ export default function VendorsPage(props: Props) { - + )} - {data.node.canCreateVendor && ( - - - + + )}
- {__("Vendor")} + {__("Third party")} {__("Accessed At")} {__("Data Risk")} {__("Business Risk")} @@ -140,10 +140,10 @@ export default function VendorsPage(props: Props) { - {vendors?.map(vendor => ( - ( + - +
- -
{vendor.name}
+ +
{thirdParty.name}
@@ -195,9 +195,9 @@ function VendorRow({ {hasAnyAction && ( - {vendor.canDelete && ( + {thirdParty.canDelete && ( diff --git a/apps/console/src/pages/organizations/vendors/VendorDetailPage.tsx b/apps/console/src/pages/organizations/third-parties/ThirdPartyDetailPage.tsx similarity index 58% rename from apps/console/src/pages/organizations/vendors/VendorDetailPage.tsx rename to apps/console/src/pages/organizations/third-parties/ThirdPartyDetailPage.tsx index 3cf5f6bc7..cb5e74cee 100644 --- a/apps/console/src/pages/organizations/vendors/VendorDetailPage.tsx +++ b/apps/console/src/pages/organizations/third-parties/ThirdPartyDetailPage.tsx @@ -33,52 +33,52 @@ import { } from "react-relay"; import { Outlet } from "react-router"; -import type { VendorComplianceTabFragment$key } from "#/__generated__/core/VendorComplianceTabFragment.graphql"; -import type { VendorGraphNodeQuery } from "#/__generated__/core/VendorGraphNodeQuery.graphql"; +import type { ThirdPartyComplianceTabFragment$key } from "#/__generated__/core/ThirdPartyComplianceTabFragment.graphql"; +import type { ThirdPartyGraphNodeQuery } from "#/__generated__/core/ThirdPartyGraphNodeQuery.graphql"; import { - useDeleteVendor, - vendorConnectionKey, - vendorNodeQuery, -} from "#/hooks/graph/VendorGraph"; + thirdPartyConnectionKey, + thirdPartyNodeQuery, + useDeleteThirdParty, +} from "#/hooks/graph/ThirdPartyGraph"; import { useOrganizationId } from "#/hooks/useOrganizationId"; import { ImportAssessmentDialog } from "./dialogs/ImportAssessmentDialog"; -import { complianceReportsFragment } from "./tabs/VendorComplianceTab"; +import { complianceReportsFragment } from "./tabs/ThirdPartyComplianceTab"; type Props = { - queryRef: PreloadedQuery; + queryRef: PreloadedQuery; }; -export default function VendorDetailPage(props: Props) { - const { node: vendor } = usePreloadedQuery(vendorNodeQuery, props.queryRef); +export default function ThirdPartyDetailPage(props: Props) { + const { node: thirdParty } = usePreloadedQuery(thirdPartyNodeQuery, props.queryRef); const { __ } = useTranslate(); const organizationId = useOrganizationId(); - const deleteVendor = useDeleteVendor( - vendor, - ConnectionHandler.getConnectionID(organizationId, vendorConnectionKey), + const deleteThirdParty = useDeleteThirdParty( + thirdParty, + ConnectionHandler.getConnectionID(organizationId, thirdPartyConnectionKey), ); - const logo = faviconUrl(vendor.websiteUrl); + const logo = faviconUrl(thirdParty.websiteUrl); const reportsCount = useFragment( complianceReportsFragment, - vendor as VendorComplianceTabFragment$key, + thirdParty as ThirdPartyComplianceTabFragment$key, ).complianceReports.edges.length; - const vendorsUrl = `/organizations/${organizationId}/vendors`; + const thirdPartiesUrl = `/organizations/${organizationId}/third-parties`; - const baseVendorUrl - = `/organizations/${organizationId}/vendors/${vendor.id}`; + const baseThirdPartyUrl + = `/organizations/${organizationId}/third-parties/${thirdParty.id}`; return (
@@ -87,26 +87,26 @@ export default function VendorDetailPage(props: Props) { {logo && ( {vendor.name )} -
{vendor.name}
+
{thirdParty.name}
- {vendor.canAssess && ( - + {thirdParty.canAssess && ( + )} - {vendor.canDelete && ( + {thirdParty.canDelete && ( {__("Delete")} @@ -116,20 +116,20 @@ export default function VendorDetailPage(props: Props) {
- {__("Overview")} - + {__("Overview")} + {__("Certifications")} - + {__("Compliance reports")} {reportsCount > 0 && {reportsCount}} - {__("Risk Assessment")} - {__("Contacts")} - {__("Services")} + {__("Risk Assessment")} + {__("Contacts")} + {__("Services")} - +
); } diff --git a/apps/console/src/pages/organizations/vendors/dialogs/CommonThirdPartyCombobox.tsx b/apps/console/src/pages/organizations/third-parties/dialogs/CommonThirdPartyCombobox.tsx similarity index 94% rename from apps/console/src/pages/organizations/vendors/dialogs/CommonThirdPartyCombobox.tsx rename to apps/console/src/pages/organizations/third-parties/dialogs/CommonThirdPartyCombobox.tsx index 7da30d2e8..292737d44 100644 --- a/apps/console/src/pages/organizations/vendors/dialogs/CommonThirdPartyCombobox.tsx +++ b/apps/console/src/pages/organizations/third-parties/dialogs/CommonThirdPartyCombobox.tsx @@ -22,7 +22,7 @@ import type { CommonThirdPartyCombobox_commonThirdParty$key, } from "#/__generated__/core/CommonThirdPartyCombobox_commonThirdParty.graphql"; import type { CommonThirdPartyComboboxQuery } from "#/__generated__/core/CommonThirdPartyComboboxQuery.graphql"; -import type { CreateVendorInput } from "#/__generated__/core/VendorGraphCreateMutation.graphql"; +import type { CreateThirdPartyInput } from "#/__generated__/core/ThirdPartyGraphCreateMutation.graphql"; export type CommonThirdPartyRef = CommonThirdPartyCombobox_commonThirdParty$data; @@ -59,7 +59,7 @@ export const commonThirdPartiesQuery = graphql` interface CommonThirdPartyComboboxProps { queryRef: PreloadedQuery; - onSelect: (thridParty: Omit) => void; + onSelect: (thridParty: Omit) => void; } export function CommonThirdPartyCombobox({ diff --git a/apps/console/src/pages/organizations/vendors/dialogs/CreateContactDialog.tsx b/apps/console/src/pages/organizations/third-parties/dialogs/CreateContactDialog.tsx similarity index 91% rename from apps/console/src/pages/organizations/vendors/dialogs/CreateContactDialog.tsx rename to apps/console/src/pages/organizations/third-parties/dialogs/CreateContactDialog.tsx index b059ec004..46959df6a 100644 --- a/apps/console/src/pages/organizations/vendors/dialogs/CreateContactDialog.tsx +++ b/apps/console/src/pages/organizations/third-parties/dialogs/CreateContactDialog.tsx @@ -33,20 +33,20 @@ import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; type Props = { children: ReactNode; connectionId: string; - vendorId: string; + thirdPartyId: string; }; const createContactMutation = graphql` mutation CreateContactDialogMutation( - $input: CreateVendorContactInput! + $input: CreateThirdPartyContactInput! $connections: [ID!]! ) { - createVendorContact(input: $input) { - vendorContactEdge @prependEdge(connections: $connections) { + createThirdPartyContact(input: $input) { + thirdPartyContactEdge @prependEdge(connections: $connections) { node { - canUpdate: permission(action: "core:vendor-contact:update") - canDelete: permission(action: "core:vendor-contact:delete") - ...VendorContactsTabFragment_contact + canUpdate: permission(action: "core:thirdParty-contact:update") + canDelete: permission(action: "core:thirdParty-contact:delete") + ...ThirdPartyContactsTabFragment_contact } } } @@ -58,7 +58,7 @@ const phoneRegex = /^\+[0-9]{8,15}$/; export function CreateContactDialog({ children, connectionId, - vendorId, + thirdPartyId, }: Props) { const { __ } = useTranslate(); @@ -107,7 +107,7 @@ export function CreateContactDialog({ await createContact({ variables: { input: { - vendorId, + thirdPartyId, ...cleanData, }, connections: [connectionId], diff --git a/apps/console/src/pages/organizations/vendors/dialogs/CreateRiskAssessmentDialog.tsx b/apps/console/src/pages/organizations/third-parties/dialogs/CreateRiskAssessmentDialog.tsx similarity index 93% rename from apps/console/src/pages/organizations/vendors/dialogs/CreateRiskAssessmentDialog.tsx rename to apps/console/src/pages/organizations/third-parties/dialogs/CreateRiskAssessmentDialog.tsx index 85db32968..ecca46944 100644 --- a/apps/console/src/pages/organizations/vendors/dialogs/CreateRiskAssessmentDialog.tsx +++ b/apps/console/src/pages/organizations/third-parties/dialogs/CreateRiskAssessmentDialog.tsx @@ -35,18 +35,18 @@ import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; type Props = { children: ReactNode; connection: string; - vendorId: string; + thirdPartyId: string; }; const createRiskAssessmentMutation = graphql` mutation CreateRiskAssessmentDialogMutation( - $input: CreateVendorRiskAssessmentInput! + $input: CreateThirdPartyRiskAssessmentInput! $connections: [ID!]! ) { - createVendorRiskAssessment(input: $input) { - vendorRiskAssessmentEdge @prependEdge(connections: $connections) { + createThirdPartyRiskAssessment(input: $input) { + thirdPartyRiskAssessmentEdge @prependEdge(connections: $connections) { node { - ...VendorRiskAssessmentTabFragment_assessment + ...ThirdPartyRiskAssessmentTabFragment_assessment } } } @@ -65,7 +65,7 @@ const schema = z.object({ export function CreateRiskAssessmentDialog({ children, connection, - vendorId, + thirdPartyId, }: Props) { const { __ } = useTranslate(); @@ -92,7 +92,7 @@ export function CreateRiskAssessmentDialog({ input: { ...data, notes: data.notes || null, - vendorId, + thirdPartyId, expiresAt: nextYear.toISOString(), }, connections: [connection], diff --git a/apps/console/src/pages/organizations/vendors/dialogs/CreateServiceDialog.tsx b/apps/console/src/pages/organizations/third-parties/dialogs/CreateServiceDialog.tsx similarity index 92% rename from apps/console/src/pages/organizations/vendors/dialogs/CreateServiceDialog.tsx rename to apps/console/src/pages/organizations/third-parties/dialogs/CreateServiceDialog.tsx index cbc90c035..43d3e538c 100644 --- a/apps/console/src/pages/organizations/vendors/dialogs/CreateServiceDialog.tsx +++ b/apps/console/src/pages/organizations/third-parties/dialogs/CreateServiceDialog.tsx @@ -33,18 +33,18 @@ import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; type Props = { children: ReactNode; connectionId: string; - vendorId: string; + thirdPartyId: string; }; const createServiceMutation = graphql` mutation CreateServiceDialogMutation( - $input: CreateVendorServiceInput! + $input: CreateThirdPartyServiceInput! $connections: [ID!]! ) { - createVendorService(input: $input) { - vendorServiceEdge @prependEdge(connections: $connections) { + createThirdPartyService(input: $input) { + thirdPartyServiceEdge @prependEdge(connections: $connections) { node { - ...VendorServicesTabFragment_service + ...ThirdPartyServicesTabFragment_service } } } @@ -54,7 +54,7 @@ const createServiceMutation = graphql` export function CreateServiceDialog({ children, connectionId, - vendorId, + thirdPartyId, }: Props) { const { __ } = useTranslate(); @@ -86,7 +86,7 @@ export function CreateServiceDialog({ await createService({ variables: { input: { - vendorId, + thirdPartyId, ...cleanData, }, connections: [connectionId], diff --git a/apps/console/src/pages/organizations/vendors/dialogs/CreateVendorDialog.tsx b/apps/console/src/pages/organizations/third-parties/dialogs/CreateThirdPartyDialog.tsx similarity index 86% rename from apps/console/src/pages/organizations/vendors/dialogs/CreateVendorDialog.tsx rename to apps/console/src/pages/organizations/third-parties/dialogs/CreateThirdPartyDialog.tsx index 01c5ba78d..8cd9131f8 100644 --- a/apps/console/src/pages/organizations/vendors/dialogs/CreateVendorDialog.tsx +++ b/apps/console/src/pages/organizations/third-parties/dialogs/CreateThirdPartyDialog.tsx @@ -27,8 +27,8 @@ import { useQueryLoader } from "react-relay"; import { useDebounceCallback } from "usehooks-ts"; import type { CommonThirdPartyComboboxQuery } from "#/__generated__/core/CommonThirdPartyComboboxQuery.graphql"; -import type { CreateVendorInput } from "#/__generated__/core/VendorGraphCreateMutation.graphql"; -import { useCreateVendorMutation } from "#/hooks/graph/VendorGraph"; +import type { CreateThirdPartyInput } from "#/__generated__/core/ThirdPartyGraphCreateMutation.graphql"; +import { useCreateThirdPartyMutation } from "#/hooks/graph/ThirdPartyGraph"; import { commonThirdPartiesQuery, @@ -41,19 +41,19 @@ type Props = { connection: string; }; -export function CreateVendorDialog({ +export function CreateThirdPartyDialog({ children, organizationId, connection, }: Props) { const { __ } = useTranslate(); - const [createVendor] = useCreateVendorMutation(); + const [createThirdParty] = useCreateThirdPartyMutation(); const dialogRef = useDialogRef(); const [searchQuery, setSearchQuery] = useState(""); const [queryRef, loadQuery] = useQueryLoader(commonThirdPartiesQuery); - const onSelect = async (thirdParty: Omit | string) => { + const onSelect = async (thirdParty: Omit | string) => { const input = typeof thirdParty === "string" ? { @@ -65,7 +65,7 @@ export function CreateVendorDialog({ ...thirdParty, organizationId, }; - await createVendor({ + await createThirdParty({ variables: { input, connections: [connection], @@ -95,9 +95,9 @@ export function CreateVendorDialog({ }; return ( - + - + {searchQuery.trim().length >= 2 && queryRef && ( = 2 && ( void onSelect(searchQuery.trim())}> - {__("Create a new vendor")} + {__("Create a new third party")} {" "} : {searchQuery} diff --git a/apps/console/src/pages/organizations/vendors/dialogs/DeleteBusinessAssociateAgreementDialog.tsx b/apps/console/src/pages/organizations/third-parties/dialogs/DeleteBusinessAssociateAgreementDialog.tsx similarity index 92% rename from apps/console/src/pages/organizations/vendors/dialogs/DeleteBusinessAssociateAgreementDialog.tsx rename to apps/console/src/pages/organizations/third-parties/dialogs/DeleteBusinessAssociateAgreementDialog.tsx index 73af76141..0d0211d88 100644 --- a/apps/console/src/pages/organizations/vendors/dialogs/DeleteBusinessAssociateAgreementDialog.tsx +++ b/apps/console/src/pages/organizations/third-parties/dialogs/DeleteBusinessAssociateAgreementDialog.tsx @@ -28,24 +28,24 @@ import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; const deleteBusinessAssociateAgreementMutation = graphql` mutation DeleteBusinessAssociateAgreementDialogMutation( - $input: DeleteVendorBusinessAssociateAgreementInput! + $input: DeleteThirdPartyBusinessAssociateAgreementInput! ) { - deleteVendorBusinessAssociateAgreement(input: $input) { - deletedVendorId + deleteThirdPartyBusinessAssociateAgreement(input: $input) { + deletedThirdPartyId } } `; type Props = { children: React.ReactNode; - vendorId: string; + thirdPartyId: string; fileName: string; onSuccess?: () => void; }; export function DeleteBusinessAssociateAgreementDialog({ children, - vendorId, + thirdPartyId, fileName, onSuccess, }: Props) { @@ -61,7 +61,7 @@ export function DeleteBusinessAssociateAgreementDialog({ await mutate({ variables: { input: { - vendorId, + thirdPartyId, }, }, }); diff --git a/apps/console/src/pages/organizations/vendors/dialogs/DeleteDataPrivacyAgreementDialog.tsx b/apps/console/src/pages/organizations/third-parties/dialogs/DeleteDataPrivacyAgreementDialog.tsx similarity index 92% rename from apps/console/src/pages/organizations/vendors/dialogs/DeleteDataPrivacyAgreementDialog.tsx rename to apps/console/src/pages/organizations/third-parties/dialogs/DeleteDataPrivacyAgreementDialog.tsx index 8e79049fe..ece0f11bc 100644 --- a/apps/console/src/pages/organizations/vendors/dialogs/DeleteDataPrivacyAgreementDialog.tsx +++ b/apps/console/src/pages/organizations/third-parties/dialogs/DeleteDataPrivacyAgreementDialog.tsx @@ -28,24 +28,24 @@ import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; const deleteDataPrivacyAgreementMutation = graphql` mutation DeleteDataPrivacyAgreementDialogMutation( - $input: DeleteVendorDataPrivacyAgreementInput! + $input: DeleteThirdPartyDataPrivacyAgreementInput! ) { - deleteVendorDataPrivacyAgreement(input: $input) { - deletedVendorId + deleteThirdPartyDataPrivacyAgreement(input: $input) { + deletedThirdPartyId } } `; type Props = { children: React.ReactNode; - vendorId: string; + thirdPartyId: string; fileName: string; onSuccess?: () => void; }; export function DeleteDataPrivacyAgreementDialog({ children, - vendorId, + thirdPartyId, fileName, onSuccess, }: Props) { @@ -61,7 +61,7 @@ export function DeleteDataPrivacyAgreementDialog({ await mutate({ variables: { input: { - vendorId, + thirdPartyId, }, }, }); diff --git a/apps/console/src/pages/organizations/vendors/dialogs/EditBusinessAssociateAgreementDialog.tsx b/apps/console/src/pages/organizations/third-parties/dialogs/EditBusinessAssociateAgreementDialog.tsx similarity index 94% rename from apps/console/src/pages/organizations/vendors/dialogs/EditBusinessAssociateAgreementDialog.tsx rename to apps/console/src/pages/organizations/third-parties/dialogs/EditBusinessAssociateAgreementDialog.tsx index b511e3cad..adcc0c06c 100644 --- a/apps/console/src/pages/organizations/vendors/dialogs/EditBusinessAssociateAgreementDialog.tsx +++ b/apps/console/src/pages/organizations/third-parties/dialogs/EditBusinessAssociateAgreementDialog.tsx @@ -31,10 +31,10 @@ import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; const updateBusinessAssociateAgreementMutation = graphql` mutation EditBusinessAssociateAgreementDialogMutation( - $input: UpdateVendorBusinessAssociateAgreementInput! + $input: UpdateThirdPartyBusinessAssociateAgreementInput! ) { - updateVendorBusinessAssociateAgreement(input: $input) { - vendorBusinessAssociateAgreement { + updateThirdPartyBusinessAssociateAgreement(input: $input) { + thirdPartyBusinessAssociateAgreement { id fileUrl validFrom @@ -52,7 +52,7 @@ const schema = z.object({ type Props = { children: React.ReactNode; - vendorId: string; + thirdPartyId: string; agreement: { validFrom?: string | null; validUntil?: string | null; @@ -62,7 +62,7 @@ type Props = { export function EditBusinessAssociateAgreementDialog({ children, - vendorId, + thirdPartyId, agreement, onSuccess, }: Props) { @@ -100,7 +100,7 @@ export function EditBusinessAssociateAgreementDialog({ await mutate({ variables: { input: { - vendorId, + thirdPartyId, validFrom: formatDatetime(data.validFrom), validUntil: formatDatetime(data.validUntil), }, diff --git a/apps/console/src/pages/organizations/vendors/dialogs/EditContactDialog.tsx b/apps/console/src/pages/organizations/third-parties/dialogs/EditContactDialog.tsx similarity index 91% rename from apps/console/src/pages/organizations/vendors/dialogs/EditContactDialog.tsx rename to apps/console/src/pages/organizations/third-parties/dialogs/EditContactDialog.tsx index 46fb1e8b3..fdb8f6552 100644 --- a/apps/console/src/pages/organizations/vendors/dialogs/EditContactDialog.tsx +++ b/apps/console/src/pages/organizations/third-parties/dialogs/EditContactDialog.tsx @@ -27,21 +27,21 @@ import { useEffect } from "react"; import { graphql } from "relay-runtime"; import { z } from "zod"; -import type { VendorContactsTabFragment_contact$data } from "#/__generated__/core/VendorContactsTabFragment_contact.graphql"; +import type { ThirdPartyContactsTabFragment_contact$data } from "#/__generated__/core/ThirdPartyContactsTabFragment_contact.graphql"; import { useFormWithSchema } from "#/hooks/useFormWithSchema"; import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; type Props = { contactId: string; - contact: VendorContactsTabFragment_contact$data; + contact: ThirdPartyContactsTabFragment_contact$data; onClose: () => void; }; const updateContactMutation = graphql` - mutation EditContactDialogUpdateMutation($input: UpdateVendorContactInput!) { - updateVendorContact(input: $input) { - vendorContact { - ...VendorContactsTabFragment_contact + mutation EditContactDialogUpdateMutation($input: UpdateThirdPartyContactInput!) { + updateThirdPartyContact(input: $input) { + thirdPartyContact { + ...ThirdPartyContactsTabFragment_contact } } } diff --git a/apps/console/src/pages/organizations/vendors/dialogs/EditDataPrivacyAgreementDialog.tsx b/apps/console/src/pages/organizations/third-parties/dialogs/EditDataPrivacyAgreementDialog.tsx similarity index 94% rename from apps/console/src/pages/organizations/vendors/dialogs/EditDataPrivacyAgreementDialog.tsx rename to apps/console/src/pages/organizations/third-parties/dialogs/EditDataPrivacyAgreementDialog.tsx index c249397ad..2ced6c54c 100644 --- a/apps/console/src/pages/organizations/vendors/dialogs/EditDataPrivacyAgreementDialog.tsx +++ b/apps/console/src/pages/organizations/third-parties/dialogs/EditDataPrivacyAgreementDialog.tsx @@ -31,10 +31,10 @@ import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; const updateDataPrivacyAgreementMutation = graphql` mutation EditDataPrivacyAgreementDialogMutation( - $input: UpdateVendorDataPrivacyAgreementInput! + $input: UpdateThirdPartyDataPrivacyAgreementInput! ) { - updateVendorDataPrivacyAgreement(input: $input) { - vendorDataPrivacyAgreement { + updateThirdPartyDataPrivacyAgreement(input: $input) { + thirdPartyDataPrivacyAgreement { id fileUrl validFrom @@ -52,7 +52,7 @@ const schema = z.object({ type Props = { children: React.ReactNode; - vendorId: string; + thirdPartyId: string; agreement: { validFrom?: string | null; validUntil?: string | null; @@ -62,7 +62,7 @@ type Props = { export function EditDataPrivacyAgreementDialog({ children, - vendorId, + thirdPartyId, agreement, onSuccess, }: Props) { @@ -100,7 +100,7 @@ export function EditDataPrivacyAgreementDialog({ await mutate({ variables: { input: { - vendorId, + thirdPartyId, validFrom: formatDatetime(data.validFrom), validUntil: formatDatetime(data.validUntil), }, diff --git a/apps/console/src/pages/organizations/vendors/dialogs/EditServiceDialog.tsx b/apps/console/src/pages/organizations/third-parties/dialogs/EditServiceDialog.tsx similarity index 94% rename from apps/console/src/pages/organizations/vendors/dialogs/EditServiceDialog.tsx rename to apps/console/src/pages/organizations/third-parties/dialogs/EditServiceDialog.tsx index 5224ce4e8..5f0f5a4b9 100644 --- a/apps/console/src/pages/organizations/vendors/dialogs/EditServiceDialog.tsx +++ b/apps/console/src/pages/organizations/third-parties/dialogs/EditServiceDialog.tsx @@ -40,10 +40,10 @@ type Props = { }; const updateServiceMutation = graphql` - mutation EditServiceDialogUpdateMutation($input: UpdateVendorServiceInput!) { - updateVendorService(input: $input) { - vendorService { - ...VendorServicesTabFragment_service + mutation EditServiceDialogUpdateMutation($input: UpdateThirdPartyServiceInput!) { + updateThirdPartyService(input: $input) { + thirdPartyService { + ...ThirdPartyServicesTabFragment_service } } } diff --git a/apps/console/src/pages/organizations/vendors/dialogs/ImportAssessmentDialog.tsx b/apps/console/src/pages/organizations/third-parties/dialogs/ImportAssessmentDialog.tsx similarity index 83% rename from apps/console/src/pages/organizations/vendors/dialogs/ImportAssessmentDialog.tsx rename to apps/console/src/pages/organizations/third-parties/dialogs/ImportAssessmentDialog.tsx index 2e052dfce..4ba7a1f57 100644 --- a/apps/console/src/pages/organizations/vendors/dialogs/ImportAssessmentDialog.tsx +++ b/apps/console/src/pages/organizations/third-parties/dialogs/ImportAssessmentDialog.tsx @@ -33,26 +33,26 @@ const schema = z.object({ }); const importAssessmentMutation = graphql` - mutation ImportAssessmentDialogMutation($input: AssessVendorInput!) { - assessVendor(input: $input) { - vendor { + mutation ImportAssessmentDialogMutation($input: AssessThirdPartyInput!) { + assessThirdParty(input: $input) { + thirdParty { id name websiteUrl - ...useVendorFormFragment - ...VendorComplianceTabFragment - ...VendorRiskAssessmentTabFragment + ...useThirdPartyFormFragment + ...ThirdPartyComplianceTabFragment + ...ThirdPartyRiskAssessmentTabFragment } } } `; type Props = { - vendorId: string; + thirdPartyId: string; children: ReactNode; }; -export function ImportAssessmentDialog({ vendorId, children }: Props) { +export function ImportAssessmentDialog({ thirdPartyId, children }: Props) { const { __ } = useTranslate(); const dialogRef = useDialogRef(); const { register, handleSubmit, reset, formState } = useFormWithSchema( @@ -66,8 +66,8 @@ export function ImportAssessmentDialog({ vendorId, children }: Props) { const [assess, isAssessing] = useMutationWithToasts( importAssessmentMutation, { - successMessage: __("Vendor assessed successfully."), - errorMessage: __("Failed to assess vendor"), + successMessage: __("Third party assessed successfully."), + errorMessage: __("Failed to assess third party"), }, ); @@ -75,7 +75,7 @@ export function ImportAssessmentDialog({ vendorId, children }: Props) { await assess({ variables: { input: { - id: vendorId, + id: thirdPartyId, websiteUrl: data.url, }, }, diff --git a/apps/console/src/pages/organizations/vendors/dialogs/PublishVendorListDialog.tsx b/apps/console/src/pages/organizations/third-parties/dialogs/PublishThirdPartyListDialog.tsx similarity index 87% rename from apps/console/src/pages/organizations/vendors/dialogs/PublishVendorListDialog.tsx rename to apps/console/src/pages/organizations/third-parties/dialogs/PublishThirdPartyListDialog.tsx index a44dec916..bc30f4ca8 100644 --- a/apps/console/src/pages/organizations/vendors/dialogs/PublishVendorListDialog.tsx +++ b/apps/console/src/pages/organizations/third-parties/dialogs/PublishThirdPartyListDialog.tsx @@ -30,15 +30,15 @@ import { useMutation } from "react-relay"; import { graphql } from "relay-runtime"; import { z } from "zod"; -import type { PublishVendorListDialogMutation } from "#/__generated__/core/PublishVendorListDialogMutation.graphql"; +import type { PublishThirdPartyListDialogMutation } from "#/__generated__/core/PublishThirdPartyListDialogMutation.graphql"; import { PeopleMultiSelectField } from "#/components/form/PeopleMultiSelectField"; import { useFormWithSchema } from "#/hooks/useFormWithSchema"; const publishMutation = graphql` - mutation PublishVendorListDialogMutation( - $input: PublishVendorListInput! + mutation PublishThirdPartyListDialogMutation( + $input: PublishThirdPartyListInput! ) { - publishVendorList(input: $input) { + publishThirdPartyList(input: $input) { documentEdge { node { id @@ -55,7 +55,7 @@ type Props = { onPublished?: (documentId: string) => void; }; -export function PublishVendorListDialog({ +export function PublishThirdPartyListDialog({ children, organizationId, defaultApproverIds, @@ -82,7 +82,7 @@ export function PublishVendorListDialog({ }); const [publish, isPublishing] - = useMutation(publishMutation); + = useMutation(publishMutation); const minorRef = useRef(false); @@ -99,13 +99,13 @@ export function PublishVendorListDialog({ }, }, onCompleted(response) { - const documentId = response.publishVendorList?.documentEdge?.node?.id; + const documentId = response.publishThirdPartyList?.documentEdge?.node?.id; if (documentId) { toast({ title: __("Success"), description: hasApprovers ? __("Approval requested successfully.") - : __("Vendors published successfully."), + : __("Third parties published successfully."), variant: "success", }); dialogRef.current?.close(); @@ -117,7 +117,7 @@ export function PublishVendorListDialog({ toast({ title: __("Error"), description: formatError( - __("Failed to publish vendors"), + __("Failed to publish third parties"), error as GraphQLError, ), variant: "error", @@ -131,7 +131,7 @@ export function PublishVendorListDialog({ className="max-w-xl" ref={dialogRef} trigger={children} - title={__("Publish Vendors")} + title={__("Publish third parties")} >
void handleSubmit(onSubmit)(e)}> diff --git a/apps/console/src/pages/organizations/vendors/dialogs/UploadBusinessAssociateAgreementDialog.tsx b/apps/console/src/pages/organizations/third-parties/dialogs/UploadBusinessAssociateAgreementDialog.tsx similarity index 95% rename from apps/console/src/pages/organizations/vendors/dialogs/UploadBusinessAssociateAgreementDialog.tsx rename to apps/console/src/pages/organizations/third-parties/dialogs/UploadBusinessAssociateAgreementDialog.tsx index e84284fcb..bf7445799 100644 --- a/apps/console/src/pages/organizations/vendors/dialogs/UploadBusinessAssociateAgreementDialog.tsx +++ b/apps/console/src/pages/organizations/third-parties/dialogs/UploadBusinessAssociateAgreementDialog.tsx @@ -33,10 +33,10 @@ import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; const uploadBusinessAssociateAgreementMutation = graphql` mutation UploadBusinessAssociateAgreementDialogMutation( - $input: UploadVendorBusinessAssociateAgreementInput! + $input: UploadThirdPartyBusinessAssociateAgreementInput! ) { - uploadVendorBusinessAssociateAgreement(input: $input) { - vendorBusinessAssociateAgreement { + uploadThirdPartyBusinessAssociateAgreement(input: $input) { + thirdPartyBusinessAssociateAgreement { id fileName fileUrl @@ -56,13 +56,13 @@ const schema = z.object({ type Props = { children: React.ReactNode; - vendorId: string; + thirdPartyId: string; onSuccess?: () => void; }; export function UploadBusinessAssociateAgreementDialog({ children, - vendorId, + thirdPartyId, onSuccess, }: Props) { const { __ } = useTranslate(); @@ -109,7 +109,7 @@ export function UploadBusinessAssociateAgreementDialog({ await mutate({ variables: { input: { - vendorId, + thirdPartyId, fileName: data.fileName, validFrom: formatDatetime(data.validFrom), validUntil: formatDatetime(data.validUntil), diff --git a/apps/console/src/pages/organizations/vendors/dialogs/UploadComplianceReportDialog.tsx b/apps/console/src/pages/organizations/third-parties/dialogs/UploadComplianceReportDialog.tsx similarity index 93% rename from apps/console/src/pages/organizations/vendors/dialogs/UploadComplianceReportDialog.tsx rename to apps/console/src/pages/organizations/third-parties/dialogs/UploadComplianceReportDialog.tsx index 161a2be88..ce98c7704 100644 --- a/apps/console/src/pages/organizations/vendors/dialogs/UploadComplianceReportDialog.tsx +++ b/apps/console/src/pages/organizations/third-parties/dialogs/UploadComplianceReportDialog.tsx @@ -34,11 +34,11 @@ import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; const uploadComplianceReportMutation = graphql` mutation UploadComplianceReportDialogMutation( - $input: UploadVendorComplianceReportInput! + $input: UploadThirdPartyComplianceReportInput! $connections: [ID!]! ) { - uploadVendorComplianceReport(input: $input) { - vendorComplianceReportEdge @appendEdge(connections: $connections) { + uploadThirdPartyComplianceReport(input: $input) { + thirdPartyComplianceReportEdge @appendEdge(connections: $connections) { node { id reportName @@ -50,7 +50,7 @@ const uploadComplianceReportMutation = graphql` size downloadUrl } - canDelete: permission(action: "core:vendor-compliance-report:delete") + canDelete: permission(action: "core:thirdParty-compliance-report:delete") } } } @@ -64,14 +64,14 @@ const schema = z.object({ type Props = { children: React.ReactNode; - vendorId: string; + thirdPartyId: string; connectionId: string; onSuccess?: () => void; }; export function UploadComplianceReportDialog({ children, - vendorId, + thirdPartyId, connectionId, onSuccess, }: Props) { @@ -111,7 +111,7 @@ export function UploadComplianceReportDialog({ variables: { connections: [connectionId], input: { - vendorId, + thirdPartyId, reportName: uploadedFile.name, reportDate: `${data.reportDate}T00:00:00Z`, validUntil: formatDatetime(data.validUntil), diff --git a/apps/console/src/pages/organizations/vendors/dialogs/UploadDataPrivacyAgreementDialog.tsx b/apps/console/src/pages/organizations/third-parties/dialogs/UploadDataPrivacyAgreementDialog.tsx similarity index 95% rename from apps/console/src/pages/organizations/vendors/dialogs/UploadDataPrivacyAgreementDialog.tsx rename to apps/console/src/pages/organizations/third-parties/dialogs/UploadDataPrivacyAgreementDialog.tsx index 8793b5080..8e0266bda 100644 --- a/apps/console/src/pages/organizations/vendors/dialogs/UploadDataPrivacyAgreementDialog.tsx +++ b/apps/console/src/pages/organizations/third-parties/dialogs/UploadDataPrivacyAgreementDialog.tsx @@ -33,10 +33,10 @@ import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; const uploadDataPrivacyAgreementMutation = graphql` mutation UploadDataPrivacyAgreementDialogMutation( - $input: UploadVendorDataPrivacyAgreementInput! + $input: UploadThirdPartyDataPrivacyAgreementInput! ) { - uploadVendorDataPrivacyAgreement(input: $input) { - vendorDataPrivacyAgreement { + uploadThirdPartyDataPrivacyAgreement(input: $input) { + thirdPartyDataPrivacyAgreement { id fileName fileUrl @@ -56,13 +56,13 @@ const schema = z.object({ type Props = { children: React.ReactNode; - vendorId: string; + thirdPartyId: string; onSuccess?: () => void; }; export function UploadDataPrivacyAgreementDialog({ children, - vendorId, + thirdPartyId, onSuccess, }: Props) { const { __ } = useTranslate(); @@ -109,7 +109,7 @@ export function UploadDataPrivacyAgreementDialog({ await mutate({ variables: { input: { - vendorId, + thirdPartyId, fileName: data.fileName, validFrom: formatDatetime(data.validFrom), validUntil: formatDatetime(data.validUntil), diff --git a/apps/console/src/pages/organizations/vendors/tabs/VendorCertificationsTab.tsx b/apps/console/src/pages/organizations/third-parties/tabs/ThirdPartyCertificationsTab.tsx similarity index 89% rename from apps/console/src/pages/organizations/vendors/tabs/VendorCertificationsTab.tsx rename to apps/console/src/pages/organizations/third-parties/tabs/ThirdPartyCertificationsTab.tsx index a57ab2c89..f31c6d036 100644 --- a/apps/console/src/pages/organizations/vendors/tabs/VendorCertificationsTab.tsx +++ b/apps/console/src/pages/organizations/third-parties/tabs/ThirdPartyCertificationsTab.tsx @@ -32,23 +32,23 @@ import { useState } from "react"; import { Controller } from "react-hook-form"; import { useOutletContext } from "react-router"; -import type { VendorGraphNodeQuery$data } from "#/__generated__/core/VendorGraphNodeQuery.graphql"; -import { useVendorForm } from "#/hooks/forms/useVendorForm"; +import type { ThirdPartyGraphNodeQuery$data } from "#/__generated__/core/ThirdPartyGraphNodeQuery.graphql"; +import { useThirdPartyForm } from "#/hooks/forms/useThirdPartyForm"; /** - * Vendor certifications tab + * ThirdParty certifications tab */ -export default function VendorCertificationsTab() { - const { vendor } = useOutletContext<{ - vendor: VendorGraphNodeQuery$data["node"]; +export default function ThirdPartyCertificationsTab() { + const { thirdParty } = useOutletContext<{ + thirdParty: ThirdPartyGraphNodeQuery$data["node"]; }>(); const { __ } = useTranslate(); - const { control, handleSubmit } = useVendorForm(vendor); + const { control, handleSubmit } = useThirdPartyForm(thirdParty); return ( void handleSubmit(e) : undefined} > @@ -60,14 +60,14 @@ export default function VendorCertificationsTab() { )} /> - {vendor.canUpdate && ( + {thirdParty.canUpdate && (
- +
)} diff --git a/apps/console/src/pages/organizations/vendors/tabs/VendorComplianceTab.tsx b/apps/console/src/pages/organizations/third-parties/tabs/ThirdPartyComplianceTab.tsx similarity index 76% rename from apps/console/src/pages/organizations/vendors/tabs/VendorComplianceTab.tsx rename to apps/console/src/pages/organizations/third-parties/tabs/ThirdPartyComplianceTab.tsx index 338676cf1..aff446f66 100644 --- a/apps/console/src/pages/organizations/vendors/tabs/VendorComplianceTab.tsx +++ b/apps/console/src/pages/organizations/third-parties/tabs/ThirdPartyComplianceTab.tsx @@ -36,20 +36,20 @@ import { useOutletContext } from "react-router"; import { graphql } from "relay-runtime"; import type { ComplianceReportListQuery } from "#/__generated__/core/ComplianceReportListQuery.graphql"; -import type { VendorComplianceTabFragment$key } from "#/__generated__/core/VendorComplianceTabFragment.graphql"; -import type { VendorComplianceTabFragment_report$key } from "#/__generated__/core/VendorComplianceTabFragment_report.graphql"; -import type { VendorGraphNodeQuery$data } from "#/__generated__/core/VendorGraphNodeQuery.graphql"; +import type { ThirdPartyComplianceTabFragment$key } from "#/__generated__/core/ThirdPartyComplianceTabFragment.graphql"; +import type { ThirdPartyComplianceTabFragment_report$key } from "#/__generated__/core/ThirdPartyComplianceTabFragment_report.graphql"; +import type { ThirdPartyGraphNodeQuery$data } from "#/__generated__/core/ThirdPartyGraphNodeQuery.graphql"; import { SortableTable, SortableTh } from "#/components/SortableTable"; import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; import { UploadComplianceReportDialog } from "../dialogs/UploadComplianceReportDialog"; export const complianceReportsFragment = graphql` - fragment VendorComplianceTabFragment on Vendor + fragment ThirdPartyComplianceTabFragment on ThirdParty @refetchable(queryName: "ComplianceReportListQuery") @argumentDefinitions( first: { type: "Int", defaultValue: 50 } - order: { type: "VendorComplianceReportOrder", defaultValue: null } + order: { type: "ThirdPartyComplianceReportOrder", defaultValue: null } after: { type: "CursorKey", defaultValue: null } before: { type: "CursorKey", defaultValue: null } last: { type: "Int", defaultValue: null } @@ -60,13 +60,13 @@ export const complianceReportsFragment = graphql` last: $last before: $before orderBy: $order - ) @connection(key: "VendorComplianceTabFragment_complianceReports") { + ) @connection(key: "ThirdPartyComplianceTabFragment_complianceReports") { __id edges { node { id - canDelete: permission(action: "core:vendor-compliance-report:delete") - ...VendorComplianceTabFragment_report + canDelete: permission(action: "core:thirdParty-compliance-report:delete") + ...ThirdPartyComplianceTabFragment_report } } } @@ -74,7 +74,7 @@ export const complianceReportsFragment = graphql` `; const complianceReportFragment = graphql` - fragment VendorComplianceTabFragment_report on VendorComplianceReport { + fragment ThirdPartyComplianceTabFragment_report on ThirdPartyComplianceReport { id reportDate validUntil @@ -84,43 +84,43 @@ const complianceReportFragment = graphql` size downloadUrl } - canDelete: permission(action: "core:vendor-compliance-report:delete") + canDelete: permission(action: "core:thirdParty-compliance-report:delete") } `; const deleteReportMutation = graphql` - mutation VendorComplianceTabDeleteReportMutation( - $input: DeleteVendorComplianceReportInput! + mutation ThirdPartyComplianceTabDeleteReportMutation( + $input: DeleteThirdPartyComplianceReportInput! $connections: [ID!]! ) { - deleteVendorComplianceReport(input: $input) { - deletedVendorComplianceReportId @deleteEdge(connections: $connections) + deleteThirdPartyComplianceReport(input: $input) { + deletedThirdPartyComplianceReportId @deleteEdge(connections: $connections) } } `; -export default function VendorComplianceTab() { - const { vendor } = useOutletContext<{ - vendor: VendorGraphNodeQuery$data["node"]; +export default function ThirdPartyComplianceTab() { + const { thirdParty } = useOutletContext<{ + thirdParty: ThirdPartyGraphNodeQuery$data["node"]; }>(); const [data, refetch] = useRefetchableFragment< ComplianceReportListQuery, - VendorComplianceTabFragment$key - >(complianceReportsFragment, vendor); + ThirdPartyComplianceTabFragment$key + >(complianceReportsFragment, thirdParty); const connectionId = data.complianceReports.__id; const reports = data.complianceReports.edges.map(edge => edge.node); const { __ } = useTranslate(); - usePageTitle(vendor.name + " - " + __("Compliance reports")); + usePageTitle(thirdParty.name + " - " + __("Compliance reports")); return (
- {vendor.canUploadComplianceReport && ( + {thirdParty.canUploadComplianceReport && ( @@ -155,13 +155,13 @@ export default function VendorComplianceTab() { } type ReportRowProps = { - reportKey: VendorComplianceTabFragment_report$key; + reportKey: ThirdPartyComplianceTabFragment_report$key; connectionId: string; }; function ReportRow(props: ReportRowProps) { const { __ } = useTranslate(); - const report = useFragment( + const report = useFragment( complianceReportFragment, props.reportKey, ); diff --git a/apps/console/src/pages/organizations/vendors/tabs/VendorContactsTab.tsx b/apps/console/src/pages/organizations/third-parties/tabs/ThirdPartyContactsTab.tsx similarity index 72% rename from apps/console/src/pages/organizations/vendors/tabs/VendorContactsTab.tsx rename to apps/console/src/pages/organizations/third-parties/tabs/ThirdPartyContactsTab.tsx index 6d28548a2..1a1170e5f 100644 --- a/apps/console/src/pages/organizations/vendors/tabs/VendorContactsTab.tsx +++ b/apps/console/src/pages/organizations/third-parties/tabs/ThirdPartyContactsTab.tsx @@ -35,25 +35,25 @@ import { useFragment, useRefetchableFragment } from "react-relay"; import { useOutletContext } from "react-router"; import { graphql } from "relay-runtime"; -import type { VendorContactsListQuery } from "#/__generated__/core/VendorContactsListQuery.graphql"; -import type { VendorContactsTabFragment$key } from "#/__generated__/core/VendorContactsTabFragment.graphql"; +import type { ThirdPartyContactsListQuery } from "#/__generated__/core/ThirdPartyContactsListQuery.graphql"; +import type { ThirdPartyContactsTabFragment$key } from "#/__generated__/core/ThirdPartyContactsTabFragment.graphql"; import type { - VendorContactsTabFragment_contact$data, - VendorContactsTabFragment_contact$key, -} from "#/__generated__/core/VendorContactsTabFragment_contact.graphql"; -import type { VendorGraphNodeQuery$data } from "#/__generated__/core/VendorGraphNodeQuery.graphql"; + ThirdPartyContactsTabFragment_contact$data, + ThirdPartyContactsTabFragment_contact$key, +} from "#/__generated__/core/ThirdPartyContactsTabFragment_contact.graphql"; +import type { ThirdPartyGraphNodeQuery$data } from "#/__generated__/core/ThirdPartyGraphNodeQuery.graphql"; import { SortableTable, SortableTh } from "#/components/SortableTable"; import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; import { CreateContactDialog } from "../dialogs/CreateContactDialog"; import { EditContactDialog } from "../dialogs/EditContactDialog"; -export const vendorContactsFragment = graphql` - fragment VendorContactsTabFragment on Vendor - @refetchable(queryName: "VendorContactsListQuery") +export const thirdPartyContactsFragment = graphql` + fragment ThirdPartyContactsTabFragment on ThirdParty + @refetchable(queryName: "ThirdPartyContactsListQuery") @argumentDefinitions( first: { type: "Int", defaultValue: 50 } - order: { type: "VendorContactOrder", defaultValue: null } + order: { type: "ThirdPartyContactOrder", defaultValue: null } after: { type: "CursorKey", defaultValue: null } before: { type: "CursorKey", defaultValue: null } last: { type: "Int", defaultValue: null } @@ -64,14 +64,14 @@ export const vendorContactsFragment = graphql` last: $last before: $before orderBy: $order - ) @connection(key: "VendorContactsTabFragment_contacts") { + ) @connection(key: "ThirdPartyContactsTabFragment_contacts") { __id edges { node { id - canUpdate: permission(action: "core:vendor-contact:update") - canDelete: permission(action: "core:vendor-contact:delete") - ...VendorContactsTabFragment_contact + canUpdate: permission(action: "core:thirdParty-contact:update") + canDelete: permission(action: "core:thirdParty-contact:delete") + ...ThirdPartyContactsTabFragment_contact } } } @@ -79,55 +79,55 @@ export const vendorContactsFragment = graphql` `; const contactFragment = graphql` - fragment VendorContactsTabFragment_contact on VendorContact { + fragment ThirdPartyContactsTabFragment_contact on ThirdPartyContact { id fullName email phone role - canUpdate: permission(action: "core:vendor-contact:update") - canDelete: permission(action: "core:vendor-contact:delete") + canUpdate: permission(action: "core:thirdParty-contact:update") + canDelete: permission(action: "core:thirdParty-contact:delete") } `; const deleteContactMutation = graphql` - mutation VendorContactsTabDeleteContactMutation( - $input: DeleteVendorContactInput! + mutation ThirdPartyContactsTabDeleteContactMutation( + $input: DeleteThirdPartyContactInput! $connections: [ID!]! ) { - deleteVendorContact(input: $input) { - deletedVendorContactId @deleteEdge(connections: $connections) + deleteThirdPartyContact(input: $input) { + deletedThirdPartyContactId @deleteEdge(connections: $connections) } } `; -export default function VendorContactsTab() { - const { vendor } = useOutletContext<{ - vendor: VendorGraphNodeQuery$data["node"]; +export default function ThirdPartyContactsTab() { + const { thirdParty } = useOutletContext<{ + thirdParty: ThirdPartyGraphNodeQuery$data["node"]; }>(); const [data, refetch] = useRefetchableFragment< - VendorContactsListQuery, - VendorContactsTabFragment$key - >(vendorContactsFragment, vendor); + ThirdPartyContactsListQuery, + ThirdPartyContactsTabFragment$key + >(thirdPartyContactsFragment, thirdParty); const connectionId = data.contacts.__id; const contacts = data.contacts.edges.map(edge => edge.node); const { __ } = useTranslate(); const [editingContact, setEditingContact] - = useState(null); + = useState(null); const hasAnyAction = contacts.some( ({ canUpdate, canDelete }) => canUpdate || canDelete, ); - usePageTitle(vendor.name + " - " + __("Contacts")); + usePageTitle(thirdParty.name + " - " + __("Contacts")); return (
- {vendor.canCreateContact && ( - + {thirdParty.canCreateContact && ( + )} @@ -169,14 +169,14 @@ export default function VendorContactsTab() { } type ContactRowProps = { - contactKey: VendorContactsTabFragment_contact$key; + contactKey: ThirdPartyContactsTabFragment_contact$key; connectionId: string; - onEdit: (contact: VendorContactsTabFragment_contact$data) => void; + onEdit: (contact: ThirdPartyContactsTabFragment_contact$data) => void; }; function ContactRow(props: ContactRowProps) { const { __ } = useTranslate(); - const contact = useFragment( + const contact = useFragment( contactFragment, props.contactKey, ); @@ -194,7 +194,7 @@ function ContactRow(props: ContactRowProps) { variables: { connections: [props.connectionId], input: { - vendorContactId: contact.id, + thirdPartyContactId: contact.id, }, }, }), diff --git a/apps/console/src/pages/organizations/vendors/tabs/VendorOverviewTab.tsx b/apps/console/src/pages/organizations/third-parties/tabs/ThirdPartyOverviewTab.tsx similarity index 84% rename from apps/console/src/pages/organizations/vendors/tabs/VendorOverviewTab.tsx rename to apps/console/src/pages/organizations/third-parties/tabs/ThirdPartyOverviewTab.tsx index 0eb946f99..85708af6b 100644 --- a/apps/console/src/pages/organizations/vendors/tabs/VendorOverviewTab.tsx +++ b/apps/console/src/pages/organizations/third-parties/tabs/ThirdPartyOverviewTab.tsx @@ -15,6 +15,7 @@ import { downloadFile, formatDate } from "@probo/helpers"; import { usePageTitle } from "@probo/hooks"; import { useTranslate } from "@probo/i18n"; +import type { ThirdPartyCategory } from "@probo/third-parties"; import { Button, Card, @@ -25,18 +26,17 @@ import { Input, Option, } from "@probo/ui"; -import type { VendorCategory } from "@probo/vendors"; import { useMemo } from "react"; import { graphql, useFragment } from "react-relay"; import { useOutletContext } from "react-router"; -import type { VendorGraphNodeQuery$data } from "#/__generated__/core/VendorGraphNodeQuery.graphql"; -import type { VendorOverviewTabBusinessAssociateAgreementFragment$key } from "#/__generated__/core/VendorOverviewTabBusinessAssociateAgreementFragment.graphql"; -import type { VendorOverviewTabDataPrivacyAgreementFragment$key } from "#/__generated__/core/VendorOverviewTabDataPrivacyAgreementFragment.graphql"; +import type { ThirdPartyGraphNodeQuery$data } from "#/__generated__/core/ThirdPartyGraphNodeQuery.graphql"; +import type { ThirdPartyOverviewTabBusinessAssociateAgreementFragment$key } from "#/__generated__/core/ThirdPartyOverviewTabBusinessAssociateAgreementFragment.graphql"; +import type { ThirdPartyOverviewTabDataPrivacyAgreementFragment$key } from "#/__generated__/core/ThirdPartyOverviewTabDataPrivacyAgreementFragment.graphql"; import { ControlledField } from "#/components/form/ControlledField"; import { CountriesField } from "#/components/form/CountriesField"; import { PeopleSelectField } from "#/components/form/PeopleSelectField"; -import { useVendorForm } from "#/hooks/forms/useVendorForm"; +import { useThirdPartyForm } from "#/hooks/forms/useThirdPartyForm"; import { useOrganizationId } from "#/hooks/useOrganizationId"; import { DeleteBusinessAssociateAgreementDialog } from "../dialogs/DeleteBusinessAssociateAgreementDialog"; @@ -46,8 +46,8 @@ import { EditDataPrivacyAgreementDialog } from "../dialogs/EditDataPrivacyAgreem import { UploadBusinessAssociateAgreementDialog } from "../dialogs/UploadBusinessAssociateAgreementDialog"; import { UploadDataPrivacyAgreementDialog } from "../dialogs/UploadDataPrivacyAgreementDialog"; -const vendorBusinessAssociateAgreementFragment = graphql` - fragment VendorOverviewTabBusinessAssociateAgreementFragment on Vendor { +const thirdPartyBusinessAssociateAgreementFragment = graphql` + fragment ThirdPartyOverviewTabBusinessAssociateAgreementFragment on ThirdParty { businessAssociateAgreement { id fileName @@ -55,36 +55,36 @@ const vendorBusinessAssociateAgreementFragment = graphql` validFrom validUntil canUpdate: permission( - action: "core:vendor-business-associate-agreement:update" + action: "core:thirdParty-business-associate-agreement:update" ) canDelete: permission( - action: "core:vendor-business-associate-agreement:delete" + action: "core:thirdParty-business-associate-agreement:delete" ) } } `; -const vendorDataPrivacyAgreementFragment = graphql` - fragment VendorOverviewTabDataPrivacyAgreementFragment on Vendor { +const thirdPartyDataPrivacyAgreementFragment = graphql` + fragment ThirdPartyOverviewTabDataPrivacyAgreementFragment on ThirdParty { dataPrivacyAgreement { id fileName fileUrl validFrom validUntil - canUpdate: permission(action: "core:vendor-data-privacy-agreement:update") - canDelete: permission(action: "core:vendor-data-privacy-agreement:delete") + canUpdate: permission(action: "core:thirdParty-data-privacy-agreement:update") + canDelete: permission(action: "core:thirdParty-data-privacy-agreement:delete") } } `; -export default function VendorOverviewTab() { - const { vendor } = useOutletContext<{ - vendor: VendorGraphNodeQuery$data["node"]; +export default function ThirdPartyOverviewTab() { + const { thirdParty } = useOutletContext<{ + thirdParty: ThirdPartyGraphNodeQuery$data["node"]; }>(); const { __ } = useTranslate(); - const vendorCategories: { value: VendorCategory; label: string }[] = [ + const thirdPartyCategories: { value: ThirdPartyCategory; label: string }[] = [ { value: "ANALYTICS", label: __("Analytics") }, { value: "CLOUD_MONITORING", label: __("Cloud Monitoring") }, { value: "CLOUD_PROVIDER", label: __("Cloud Provider") }, @@ -118,21 +118,21 @@ export default function VendorOverviewTab() { register, handleSubmit, formState: { errors, isSubmitting }, - } = useVendorForm(vendor); + } = useThirdPartyForm(thirdParty); - const vendorWithBAA - = useFragment( - vendorBusinessAssociateAgreementFragment, - vendor, + const thirdPartyWithBAA + = useFragment( + thirdPartyBusinessAssociateAgreementFragment, + thirdParty, ); - const businessAssociateAgreement = vendorWithBAA.businessAssociateAgreement; + const businessAssociateAgreement = thirdPartyWithBAA.businessAssociateAgreement; - const vendorWithDPA - = useFragment( - vendorDataPrivacyAgreementFragment, - vendor, + const thirdPartyWithDPA + = useFragment( + thirdPartyDataPrivacyAgreementFragment, + thirdParty, ); - const dataPrivacyAgreement = vendorWithDPA.dataPrivacyAgreement; + const dataPrivacyAgreement = thirdPartyWithDPA.dataPrivacyAgreement; const urls = useMemo( () => @@ -154,20 +154,20 @@ export default function VendorOverviewTab() { [__], ); - usePageTitle(vendor.name + " - " + __("Overview")); + usePageTitle(thirdParty.name + " - " + __("Overview")); - const isFormDisabled = isSubmitting || !vendor.canUpdate; + const isFormDisabled = isSubmitting || !thirdParty.canUpdate; return (
void handleSubmit(e)} className="space-y-12" > - {/* Vendor Details */} + {/* ThirdParty Details */}
-

{__("Vendor details")}

+

{__("Third party details")}

- {vendorCategories.map(category => ( + {thirdPartyCategories.map(category => ( @@ -330,7 +330,7 @@ export default function VendorOverviewTab() { {businessAssociateAgreement.canUpdate && ( window.location.reload()} > @@ -352,9 +352,9 @@ export default function VendorOverviewTab() { ) : ( - vendor.canUploadBAA && ( + thirdParty.canUploadBAA && ( window.location.reload()} > {dataPrivacyAgreement.canUpdate && ( window.location.reload()} > @@ -426,9 +426,9 @@ export default function VendorOverviewTab() { ) : ( - vendor.canUploadDPA && ( + thirdParty.canUploadDPA && ( window.location.reload()} > )}
diff --git a/apps/console/src/pages/organizations/vendors/tabs/VendorRiskAssessmentTab.tsx b/apps/console/src/pages/organizations/third-parties/tabs/ThirdPartyRiskAssessmentTab.tsx similarity index 78% rename from apps/console/src/pages/organizations/vendors/tabs/VendorRiskAssessmentTab.tsx rename to apps/console/src/pages/organizations/third-parties/tabs/ThirdPartyRiskAssessmentTab.tsx index 54f99052e..0780c2e13 100644 --- a/apps/console/src/pages/organizations/vendors/tabs/VendorRiskAssessmentTab.tsx +++ b/apps/console/src/pages/organizations/third-parties/tabs/ThirdPartyRiskAssessmentTab.tsx @@ -33,20 +33,20 @@ import { useFragment, useRefetchableFragment } from "react-relay"; import { useOutletContext } from "react-router"; import { graphql } from "relay-runtime"; -import type { VendorGraphNodeQuery$data } from "#/__generated__/core/VendorGraphNodeQuery.graphql"; -import type { VendorRiskAssessmentTabFragment$key } from "#/__generated__/core/VendorRiskAssessmentTabFragment.graphql"; -import type { VendorRiskAssessmentTabFragment_assessment$key } from "#/__generated__/core/VendorRiskAssessmentTabFragment_assessment.graphql"; -import type { VendorRiskAssessmentTabQuery } from "#/__generated__/core/VendorRiskAssessmentTabQuery.graphql"; +import type { ThirdPartyGraphNodeQuery$data } from "#/__generated__/core/ThirdPartyGraphNodeQuery.graphql"; +import type { ThirdPartyRiskAssessmentTabFragment$key } from "#/__generated__/core/ThirdPartyRiskAssessmentTabFragment.graphql"; +import type { ThirdPartyRiskAssessmentTabFragment_assessment$key } from "#/__generated__/core/ThirdPartyRiskAssessmentTabFragment_assessment.graphql"; +import type { ThirdPartyRiskAssessmentTabQuery } from "#/__generated__/core/ThirdPartyRiskAssessmentTabQuery.graphql"; import { SortableTable, SortableTh } from "#/components/SortableTable"; import { CreateRiskAssessmentDialog } from "../dialogs/CreateRiskAssessmentDialog"; const riskAssessmentsFragment = graphql` - fragment VendorRiskAssessmentTabFragment on Vendor - @refetchable(queryName: "VendorRiskAssessmentTabQuery") + fragment ThirdPartyRiskAssessmentTabFragment on ThirdParty + @refetchable(queryName: "ThirdPartyRiskAssessmentTabQuery") @argumentDefinitions( first: { type: "Int", defaultValue: 50 } - order: { type: "VendorRiskAssessmentOrder", defaultValue: null } + order: { type: "ThirdPartyRiskAssessmentOrder", defaultValue: null } after: { type: "CursorKey", defaultValue: null } before: { type: "CursorKey", defaultValue: null } last: { type: "Int", defaultValue: null } @@ -59,12 +59,12 @@ const riskAssessmentsFragment = graphql` last: $last before: $before orderBy: $order - ) @connection(key: "VendorRiskAssessmentTabFragment_riskAssessments") { + ) @connection(key: "ThirdPartyRiskAssessmentTabFragment_riskAssessments") { __id edges { node { id - ...VendorRiskAssessmentTabFragment_assessment + ...ThirdPartyRiskAssessmentTabFragment_assessment } } pageInfo { @@ -76,7 +76,7 @@ const riskAssessmentsFragment = graphql` `; const riskAssessmentFragment = graphql` - fragment VendorRiskAssessmentTabFragment_assessment on VendorRiskAssessment { + fragment ThirdPartyRiskAssessmentTabFragment_assessment on ThirdPartyRiskAssessment { id createdAt expiresAt @@ -86,27 +86,27 @@ const riskAssessmentFragment = graphql` } `; -export default function VendorRiskAssessmentTab() { - const { vendor } = useOutletContext<{ - vendor: VendorGraphNodeQuery$data["node"]; +export default function ThirdPartyRiskAssessmentTab() { + const { thirdParty } = useOutletContext<{ + thirdParty: ThirdPartyGraphNodeQuery$data["node"]; }>(); const [data, refetch] = useRefetchableFragment< - VendorRiskAssessmentTabQuery, - VendorRiskAssessmentTabFragment$key - >(riskAssessmentsFragment, vendor); + ThirdPartyRiskAssessmentTabQuery, + ThirdPartyRiskAssessmentTabFragment$key + >(riskAssessmentsFragment, thirdParty); const assessments = data.riskAssessments.edges.map(edge => edge.node); const { __ } = useTranslate(); const [expanded, setExpanded] = useState(null); - usePageTitle(vendor.name + " - " + __("Risk Assessments")); + usePageTitle(thirdParty.name + " - " + __("Risk Assessments")); if (assessments.length === 0) { return (
{__("No risk assessments found")} - {vendor.canCreateRiskAssessment && ( + {thirdParty.canCreateRiskAssessment && ( )} @@ -165,14 +165,14 @@ export default function VendorServicesTab() { } type ServiceRowProps = { - serviceKey: VendorServicesTabFragment_service$key; + serviceKey: ThirdPartyServicesTabFragment_service$key; connectionId: string; - onEdit: (service: VendorServicesTabFragment_service$data) => void; + onEdit: (service: ThirdPartyServicesTabFragment_service$data) => void; }; function ServiceRow(props: ServiceRowProps) { const { __ } = useTranslate(); - const service = useFragment( + const service = useFragment( serviceFragment, props.serviceKey, ); @@ -190,7 +190,7 @@ function ServiceRow(props: ServiceRowProps) { variables: { connections: [props.connectionId], input: { - vendorServiceId: service.id, + thirdPartyServiceId: service.id, }, }, }), diff --git a/apps/console/src/routes.tsx b/apps/console/src/routes.tsx index bcf44ed09..099fb52e1 100644 --- a/apps/console/src/routes.tsx +++ b/apps/console/src/routes.tsx @@ -47,7 +47,7 @@ import { rightsRequestRoutes } from "./routes/rightsRequestRoutes"; import { riskRoutes } from "./routes/riskRoutes"; import { statementsOfApplicabilityRoutes } from "./routes/statementsOfApplicabilityRoutes"; import { taskRoutes } from "./routes/taskRoutes"; -import { vendorRoutes } from "./routes/vendorRoutes"; +import { thirdPartyRoutes } from "./routes/thirdPartyRoutes"; const routes = [ { @@ -291,7 +291,7 @@ const routes = [ ...riskRoutes, ...measureRoutes, ...documentsRoutes, - ...vendorRoutes, + ...thirdPartyRoutes, ...frameworkRoutes, ...taskRoutes, ...assetRoutes, diff --git a/apps/console/src/routes/vendorRoutes.ts b/apps/console/src/routes/thirdPartyRoutes.ts similarity index 61% rename from apps/console/src/routes/vendorRoutes.ts rename to apps/console/src/routes/thirdPartyRoutes.ts index d69d9df6a..1996f3af1 100644 --- a/apps/console/src/routes/vendorRoutes.ts +++ b/apps/console/src/routes/thirdPartyRoutes.ts @@ -20,43 +20,43 @@ import { } from "@probo/routes"; import { loadQuery } from "react-relay"; -import type { VendorGraphListQuery } from "#/__generated__/core/VendorGraphListQuery.graphql"; -import type { VendorGraphNodeQuery } from "#/__generated__/core/VendorGraphNodeQuery.graphql"; +import type { ThirdPartyGraphListQuery } from "#/__generated__/core/ThirdPartyGraphListQuery.graphql"; +import type { ThirdPartyGraphNodeQuery } from "#/__generated__/core/ThirdPartyGraphNodeQuery.graphql"; import { LinkCardSkeleton } from "#/components/skeletons/LinkCardSkeleton"; import { PageSkeleton } from "#/components/skeletons/PageSkeleton"; import { coreEnvironment } from "#/environments"; -import { vendorNodeQuery, vendorsQuery } from "#/hooks/graph/VendorGraph"; +import { thirdPartiesQuery, thirdPartyNodeQuery } from "#/hooks/graph/ThirdPartyGraph"; -export const vendorRoutes = [ +export const thirdPartyRoutes = [ { - path: "vendors", + path: "third-parties", Fallback: PageSkeleton, loader: loaderFromQueryLoader(({ organizationId }) => - loadQuery(coreEnvironment, vendorsQuery, { + loadQuery(coreEnvironment, thirdPartiesQuery, { organizationId: organizationId, }), ), Component: withQueryRef( - lazy(() => import("#/pages/organizations/vendors/VendorsPage")), + lazy(() => import("#/pages/organizations/third-parties/ThirdPartiesPage")), ), }, { - path: "vendors/:vendorId", + path: "third-parties/:thirdPartyId", Fallback: PageSkeleton, - loader: loaderFromQueryLoader(({ vendorId }) => - loadQuery(coreEnvironment, vendorNodeQuery, { - vendorId: vendorId, + loader: loaderFromQueryLoader(({ thirdPartyId }) => + loadQuery(coreEnvironment, thirdPartyNodeQuery, { + thirdPartyId: thirdPartyId, }), ), Component: withQueryRef( - lazy(() => import("../pages/organizations/vendors/VendorDetailPage")), + lazy(() => import("../pages/organizations/third-parties/ThirdPartyDetailPage")), ), children: [ { path: "overview", Fallback: LinkCardSkeleton, Component: lazy( - () => import("../pages/organizations/vendors/tabs/VendorOverviewTab"), + () => import("../pages/organizations/third-parties/tabs/ThirdPartyOverviewTab"), ), }, { @@ -64,7 +64,7 @@ export const vendorRoutes = [ Fallback: LinkCardSkeleton, Component: lazy( () => - import("../pages/organizations/vendors/tabs/VendorCertificationsTab"), + import("../pages/organizations/third-parties/tabs/ThirdPartyCertificationsTab"), ), }, { @@ -72,7 +72,7 @@ export const vendorRoutes = [ Fallback: LinkCardSkeleton, Component: lazy( () => - import("../pages/organizations/vendors/tabs/VendorComplianceTab"), + import("../pages/organizations/third-parties/tabs/ThirdPartyComplianceTab"), ), }, { @@ -80,21 +80,21 @@ export const vendorRoutes = [ Fallback: LinkCardSkeleton, Component: lazy( () => - import("../pages/organizations/vendors/tabs/VendorRiskAssessmentTab"), + import("../pages/organizations/third-parties/tabs/ThirdPartyRiskAssessmentTab"), ), }, { path: "contacts", Fallback: LinkCardSkeleton, Component: lazy( - () => import("../pages/organizations/vendors/tabs/VendorContactsTab"), + () => import("../pages/organizations/third-parties/tabs/ThirdPartyContactsTab"), ), }, { path: "services", Fallback: LinkCardSkeleton, Component: lazy( - () => import("../pages/organizations/vendors/tabs/VendorServicesTab"), + () => import("../pages/organizations/third-parties/tabs/ThirdPartyServicesTab"), ), }, ], diff --git a/cmd/common-third-parties-import/main.go b/cmd/common-third-parties-import/main.go index ad1661eab..c3dcd2948 100644 --- a/cmd/common-third-parties-import/main.go +++ b/cmd/common-third-parties-import/main.go @@ -13,7 +13,7 @@ // PERFORMANCE OF THIS SOFTWARE. // Command common-third-parties-import seeds the common_third_parties table from -// packages/vendors/data.json. It is idempotent: re-running upserts on conflict +// packages/thirdParties/data.json. It is idempotent: re-running upserts on conflict // (lower(name)) so existing rows keep their id and created_at. // // When -fetch-logos is set, the tool inspects each third party's website to @@ -419,15 +419,15 @@ func loadThirdParties(path string) ([]thirdPartyData, error) { return thirdParties, nil } -func parseCategory(tp thirdPartyData) coredata.VendorCategory { +func parseCategory(tp thirdPartyData) coredata.ThirdPartyCategory { if tp.Category == nil || *tp.Category == "" { - return coredata.VendorCategoryOther + return coredata.ThirdPartyCategoryOther } - var c coredata.VendorCategory + var c coredata.ThirdPartyCategory if err := c.Scan(*tp.Category); err != nil { fmt.Fprintf(os.Stderr, "warning: third party %q has unknown category %q, falling back to OTHER\n", tp.Name, *tp.Category) - return coredata.VendorCategoryOther + return coredata.ThirdPartyCategoryOther } return c diff --git a/cmd/migrate-asset-snapshots-to-documents/main.go b/cmd/migrate-asset-snapshots-to-documents/main.go index 1c0342c22..902eaf8e9 100644 --- a/cmd/migrate-asset-snapshots-to-documents/main.go +++ b/cmd/migrate-asset-snapshots-to-documents/main.go @@ -339,41 +339,41 @@ ORDER BY a.name ASC; return "", err } - vendorRows, err := tx.Query( + thirdPartyRows, err := tx.Query( ctx, ` SELECT av.asset_id, v.name -FROM asset_vendors av -JOIN vendors v ON v.id = av.vendor_id +FROM asset_third_parties av +JOIN third_parties v ON v.id = av.third_party_id WHERE av.snapshot_id = @snapshot_id ORDER BY v.name ASC; `, pgx.NamedArgs{"snapshot_id": snapshotID}, ) if err != nil { - return "", fmt.Errorf("cannot load snapshot asset vendors: %w", err) + return "", fmt.Errorf("cannot load snapshot asset thirdParties: %w", err) } - defer vendorRows.Close() + defer thirdPartyRows.Close() - vendorsByAsset := make(map[string][]string) - for vendorRows.Next() { - var assetID, vendorName string - if err := vendorRows.Scan(&assetID, &vendorName); err != nil { - return "", fmt.Errorf("cannot scan vendor: %w", err) + thirdPartiesByAsset := make(map[string][]string) + for thirdPartyRows.Next() { + var assetID, thirdPartyName string + if err := thirdPartyRows.Scan(&assetID, &thirdPartyName); err != nil { + return "", fmt.Errorf("cannot scan thirdParty: %w", err) } - vendorsByAsset[assetID] = append(vendorsByAsset[assetID], vendorName) + thirdPartiesByAsset[assetID] = append(thirdPartiesByAsset[assetID], thirdPartyName) } - if err := vendorRows.Err(); err != nil { + if err := thirdPartyRows.Err(); err != nil { return "", err } assetRows := make([]docgen.AssetListRow, len(assets)) for i, a := range assets { - vendors := "-" - if v, ok := vendorsByAsset[a.id]; ok && len(v) > 0 { - vendors = strings.Join(v, ", ") + thirdParties := "-" + if v, ok := thirdPartiesByAsset[a.id]; ok && len(v) > 0 { + thirdParties = strings.Join(v, ", ") } assetRows[i] = docgen.AssetListRow{ @@ -382,7 +382,7 @@ ORDER BY v.name ASC; Amount: a.amount, DataTypesStored: a.dataTypesStored, Owner: a.ownerName, - Vendors: vendors, + ThirdParties: thirdParties, } } diff --git a/cmd/migrate-data-snapshots-to-documents/main.go b/cmd/migrate-data-snapshots-to-documents/main.go index f009ca0e9..ca5dfd375 100644 --- a/cmd/migrate-data-snapshots-to-documents/main.go +++ b/cmd/migrate-data-snapshots-to-documents/main.go @@ -335,49 +335,49 @@ ORDER BY d.name ASC; return "", err } - // Load vendors for each datum in this snapshot. - vendorRows, err := tx.Query( + // Load thirdParties for each datum in this snapshot. + thirdPartyRows, err := tx.Query( ctx, ` SELECT dv.datum_id, v.name -FROM data_vendors dv -JOIN vendors v ON v.id = dv.vendor_id +FROM data_third_parties dv +JOIN third_parties v ON v.id = dv.third_party_id WHERE dv.snapshot_id = @snapshot_id ORDER BY v.name ASC; `, pgx.NamedArgs{"snapshot_id": snapshotID}, ) if err != nil { - return "", fmt.Errorf("cannot load snapshot data vendors: %w", err) + return "", fmt.Errorf("cannot load snapshot data thirdParties: %w", err) } - defer vendorRows.Close() + defer thirdPartyRows.Close() - vendorsByDatum := make(map[string][]string) - for vendorRows.Next() { - var datumID, vendorName string - if err := vendorRows.Scan(&datumID, &vendorName); err != nil { - return "", fmt.Errorf("cannot scan vendor: %w", err) + thirdPartiesByDatum := make(map[string][]string) + for thirdPartyRows.Next() { + var datumID, thirdPartyName string + if err := thirdPartyRows.Scan(&datumID, &thirdPartyName); err != nil { + return "", fmt.Errorf("cannot scan thirdParty: %w", err) } - vendorsByDatum[datumID] = append(vendorsByDatum[datumID], vendorName) + thirdPartiesByDatum[datumID] = append(thirdPartiesByDatum[datumID], thirdPartyName) } - if err := vendorRows.Err(); err != nil { + if err := thirdPartyRows.Err(); err != nil { return "", err } dataRows := make([]docgen.DataListRow, len(data)) for i, d := range data { - vendors := "-" - if v, ok := vendorsByDatum[d.id]; ok && len(v) > 0 { - vendors = strings.Join(v, ", ") + thirdParties := "-" + if v, ok := thirdPartiesByDatum[d.id]; ok && len(v) > 0 { + thirdParties = strings.Join(v, ", ") } dataRows[i] = docgen.DataListRow{ Name: d.name, Classification: formatClassificationString(d.classification), Owner: d.ownerName, - Vendors: vendors, + ThirdParties: thirdParties, } } diff --git a/cmd/migrate-processing-activity-snapshots-to-documents/main.go b/cmd/migrate-processing-activity-snapshots-to-documents/main.go index 1c9393ab6..1666a5e13 100644 --- a/cmd/migrate-processing-activity-snapshots-to-documents/main.go +++ b/cmd/migrate-processing-activity-snapshots-to-documents/main.go @@ -403,7 +403,7 @@ ORDER BY pa.name ASC; return "", 0, nil } - vendorMap, err := loadVendorsForSnapshot(ctx, tx, snapshotID) + thirdPartyMap, err := loadThirdPartiesForSnapshot(ctx, tx, snapshotID) if err != nil { return "", 0, err } @@ -415,9 +415,9 @@ ORDER BY pa.name ASC; dpo = p.dpoName } - vendors := "None" - if v, ok := vendorMap[p.id]; ok && len(v) > 0 { - vendors = strings.Join(v, ", ") + thirdParties := "None" + if v, ok := thirdPartyMap[p.id]; ok && len(v) > 0 { + thirdParties = strings.Join(v, ", ") } listRows[i] = docgen.ProcessingActivityListRow{ @@ -440,7 +440,7 @@ ORDER BY pa.name ASC; LastReviewDate: formatDateOrNotSpecified(p.lastReviewDate), NextReviewDate: formatDateOrNotSpecified(p.nextReviewDate), DataProtectionOfficer: dpo, - Vendors: vendors, + ThirdParties: thirdParties, } } @@ -457,20 +457,20 @@ ORDER BY pa.name ASC; return content, len(listRows), nil } -func loadVendorsForSnapshot(ctx context.Context, tx pg.Tx, snapshotID string) (map[gid.GID][]string, error) { +func loadThirdPartiesForSnapshot(ctx context.Context, tx pg.Tx, snapshotID string) (map[gid.GID][]string, error) { rows, err := tx.Query( ctx, ` SELECT pav.processing_activity_id, v.name -FROM processing_activity_vendors pav -INNER JOIN vendors v ON v.id = pav.vendor_id +FROM processing_activity_third_parties pav +INNER JOIN third_parties v ON v.id = pav.third_party_id WHERE pav.snapshot_id = @snapshot_id ORDER BY pav.processing_activity_id, v.name; `, pgx.NamedArgs{"snapshot_id": snapshotID}, ) if err != nil { - return nil, fmt.Errorf("cannot load snapshot vendors: %w", err) + return nil, fmt.Errorf("cannot load snapshot thirdParties: %w", err) } defer rows.Close() @@ -479,7 +479,7 @@ ORDER BY pav.processing_activity_id, v.name; var paID gid.GID var name string if err := rows.Scan(&paID, &name); err != nil { - return nil, fmt.Errorf("cannot scan vendor row: %w", err) + return nil, fmt.Errorf("cannot scan thirdParty row: %w", err) } result[paID] = append(result[paID], name) } diff --git a/cmd/migrate-vendor-snapshots-to-documents/main.go b/cmd/migrate-third-party-snapshots-to-documents/main.go similarity index 66% rename from cmd/migrate-vendor-snapshots-to-documents/main.go rename to cmd/migrate-third-party-snapshots-to-documents/main.go index aa46132f0..53c241a36 100644 --- a/cmd/migrate-vendor-snapshots-to-documents/main.go +++ b/cmd/migrate-third-party-snapshots-to-documents/main.go @@ -12,9 +12,9 @@ // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -// Command migrate-vendor-snapshots-to-documents creates documents and document -// versions from existing vendor snapshots. For each organization that has vendor -// snapshots, it generates a vendor list document using the same ProseMirror +// Command migrate-thirdParty-snapshots-to-documents creates documents and document +// versions from existing thirdParty snapshots. For each organization that has thirdParty +// snapshots, it generates a thirdParty list document using the same ProseMirror // builder as the publish flow. package main @@ -72,22 +72,22 @@ func run() error { return migrate(ctx, pgClient, dryRun) } -type orgWithVendorSnapshots struct { +type orgWithThirdPartySnapshots struct { organizationID gid.GID tenantID gid.TenantID organizationName string } -type vendorSnapshot struct { +type thirdPartySnapshot struct { snapshotID string publishedAt time.Time } func migrate(ctx context.Context, pgClient *pg.Client, dryRun bool) error { - var orgs []orgWithVendorSnapshots + var orgs []orgWithThirdPartySnapshots err := pgClient.WithConn(ctx, func(ctx context.Context, conn pg.Querier) error { var err error - orgs, err = loadOrgsWithVendorSnapshots(ctx, conn) + orgs, err = loadOrgsWithThirdPartySnapshots(ctx, conn) return err }) if err != nil { @@ -95,7 +95,7 @@ func migrate(ctx context.Context, pgClient *pg.Client, dryRun bool) error { } if len(orgs) == 0 { - fmt.Println("no organizations with vendor snapshots to migrate") + fmt.Println("no organizations with thirdParty snapshots to migrate") return nil } @@ -107,14 +107,14 @@ func migrate(ctx context.Context, pgClient *pg.Client, dryRun bool) error { if dryRun { var count int err := pgClient.WithConn(ctx, func(ctx context.Context, conn pg.Querier) error { - snapshots, err := loadVendorSnapshots(ctx, conn, org.organizationID) + snapshots, err := loadThirdPartySnapshots(ctx, conn, org.organizationID) count = len(snapshots) return err }) if err != nil { return err } - fmt.Printf("would migrate org %s (%s) — %d vendor snapshot(s)\n", + fmt.Printf("would migrate org %s (%s) — %d thirdParty snapshot(s)\n", org.organizationID, org.organizationName, count) continue } @@ -143,8 +143,8 @@ func migrate(ctx context.Context, pgClient *pg.Client, dryRun bool) error { return nil } -func migrateOrg(ctx context.Context, tx pg.Tx, org orgWithVendorSnapshots) error { - snapshots, err := loadVendorSnapshots(ctx, tx, org.organizationID) +func migrateOrg(ctx context.Context, tx pg.Tx, org orgWithThirdPartySnapshots) error { + snapshots, err := loadThirdPartySnapshots(ctx, tx, org.organizationID) if err != nil { return err } @@ -186,15 +186,15 @@ INSERT INTO documents ( _, err = tx.Exec( ctx, - `INSERT INTO generated_documents (organization_id, tenant_id, vendors_document_id, created_at, updated_at) -VALUES (@organization_id, @tenant_id, @vendors_document_id, @created_at, @updated_at) -ON CONFLICT (organization_id) DO UPDATE SET vendors_document_id = @vendors_document_id, updated_at = @updated_at`, + `INSERT INTO generated_documents (organization_id, tenant_id, third_parties_document_id, created_at, updated_at) +VALUES (@organization_id, @tenant_id, @third_parties_document_id, @created_at, @updated_at) +ON CONFLICT (organization_id) DO UPDATE SET third_parties_document_id = @third_parties_document_id, updated_at = @updated_at`, pgx.NamedArgs{ - "organization_id": org.organizationID, - "tenant_id": org.tenantID, - "vendors_document_id": documentID, - "created_at": now, - "updated_at": now, + "organization_id": org.organizationID, + "tenant_id": org.tenantID, + "third_parties_document_id": documentID, + "created_at": now, + "updated_at": now, }, ) if err != nil { @@ -234,7 +234,7 @@ INSERT INTO document_versions ( "tenant_id": org.tenantID, "organization_id": org.organizationID, "document_id": documentID, - "title": "Vendors", + "title": "ThirdParties", "major": major + 1, "content": content, "published_at": snap.publishedAt, @@ -251,7 +251,7 @@ INSERT INTO document_versions ( return nil } -func loadOrgsWithVendorSnapshots(ctx context.Context, conn pg.Querier) ([]orgWithVendorSnapshots, error) { +func loadOrgsWithThirdPartySnapshots(ctx context.Context, conn pg.Querier) ([]orgWithThirdPartySnapshots, error) { rows, err := conn.Query( ctx, ` @@ -263,7 +263,7 @@ SELECT DISTINCT FROM organizations o WHERE NOT EXISTS ( SELECT 1 FROM generated_documents gd - WHERE gd.organization_id = o.id AND gd.vendors_document_id IS NOT NULL + WHERE gd.organization_id = o.id AND gd.third_parties_document_id IS NOT NULL ) AND EXISTS ( SELECT 1 FROM snapshots s @@ -273,13 +273,13 @@ ORDER BY o.created_at; `, ) if err != nil { - return nil, fmt.Errorf("cannot query organizations with vendor snapshots: %w", err) + return nil, fmt.Errorf("cannot query organizations with thirdParty snapshots: %w", err) } defer rows.Close() - var result []orgWithVendorSnapshots + var result []orgWithThirdPartySnapshots for rows.Next() { - var o orgWithVendorSnapshots + var o orgWithThirdPartySnapshots var createdAt time.Time if err := rows.Scan(&o.organizationID, &o.tenantID, &o.organizationName, &createdAt); err != nil { return nil, fmt.Errorf("cannot scan organization: %w", err) @@ -290,7 +290,7 @@ ORDER BY o.created_at; return result, rows.Err() } -func loadVendorSnapshots(ctx context.Context, conn pg.Querier, organizationID gid.GID) ([]vendorSnapshot, error) { +func loadThirdPartySnapshots(ctx context.Context, conn pg.Querier, organizationID gid.GID) ([]thirdPartySnapshot, error) { rows, err := conn.Query( ctx, ` @@ -305,13 +305,13 @@ ORDER BY s.created_at ASC; pgx.NamedArgs{"organization_id": organizationID}, ) if err != nil { - return nil, fmt.Errorf("cannot query vendor snapshots for org %s: %w", organizationID, err) + return nil, fmt.Errorf("cannot query thirdParty snapshots for org %s: %w", organizationID, err) } defer rows.Close() - var result []vendorSnapshot + var result []thirdPartySnapshot for rows.Next() { - var s vendorSnapshot + var s thirdPartySnapshot if err := rows.Scan(&s.snapshotID, &s.publishedAt); err != nil { return nil, fmt.Errorf("cannot scan snapshot: %w", err) } @@ -321,7 +321,7 @@ ORDER BY s.created_at ASC; return result, rows.Err() } -type vendorInfo struct { +type thirdPartyInfo struct { id string name string category string @@ -352,7 +352,7 @@ func buildSnapshotContent( orgName string, publishedAt time.Time, ) (string, error) { - vendorRows, err := tx.Query( + thirdPartyRows, err := tx.Query( ctx, ` SELECT @@ -376,7 +376,7 @@ SELECT v.countries, COALESCE(bo.full_name, 'Not assigned'), COALESCE(so.full_name, 'Not assigned') -FROM vendors v +FROM third_parties v LEFT JOIN iam_membership_profiles bo ON bo.id = v.business_owner_profile_id LEFT JOIN iam_membership_profiles so ON so.id = v.security_owner_profile_id WHERE v.snapshot_id = @snapshot_id @@ -385,14 +385,14 @@ ORDER BY v.name ASC; pgx.NamedArgs{"snapshot_id": snapshotID}, ) if err != nil { - return "", fmt.Errorf("cannot load snapshot vendors: %w", err) + return "", fmt.Errorf("cannot load snapshot thirdParties: %w", err) } - defer vendorRows.Close() + defer thirdPartyRows.Close() - var vendors []vendorInfo - for vendorRows.Next() { - var v vendorInfo - if err := vendorRows.Scan( + var thirdParties []thirdPartyInfo + for thirdPartyRows.Next() { + var v thirdPartyInfo + if err := thirdPartyRows.Scan( &v.id, &v.name, &v.category, &v.legalName, &v.description, &v.headquarterAddress, &v.websiteURL, &v.privacyPolicyURL, &v.serviceLevelAgreementURL, @@ -402,52 +402,52 @@ ORDER BY v.name ASC; &v.certifications, &v.countries, &v.businessOwnerName, &v.securityOwnerName, ); err != nil { - return "", fmt.Errorf("cannot scan vendor: %w", err) + return "", fmt.Errorf("cannot scan thirdParty: %w", err) } - vendors = append(vendors, v) + thirdParties = append(thirdParties, v) } - if err := vendorRows.Err(); err != nil { + if err := thirdPartyRows.Err(); err != nil { return "", err } - vendorIDs := make([]string, len(vendors)) - for i, v := range vendors { - vendorIDs[i] = v.id + thirdPartyIDs := make([]string, len(thirdParties)) + for i, v := range thirdParties { + thirdPartyIDs[i] = v.id } - servicesByVendor, err := loadSnapshotServices(ctx, tx, snapshotID, vendorIDs) + servicesByThirdParty, err := loadSnapshotServices(ctx, tx, snapshotID, thirdPartyIDs) if err != nil { return "", err } - contactsByVendor, err := loadSnapshotContacts(ctx, tx, snapshotID, vendorIDs) + contactsByThirdParty, err := loadSnapshotContacts(ctx, tx, snapshotID, thirdPartyIDs) if err != nil { return "", err } - assessmentsByVendor, err := loadSnapshotRiskAssessments(ctx, tx, snapshotID, vendorIDs) + assessmentsByThirdParty, err := loadSnapshotRiskAssessments(ctx, tx, snapshotID, thirdPartyIDs) if err != nil { return "", err } - reportsByVendor, err := loadSnapshotComplianceReports(ctx, tx, snapshotID, vendorIDs) + reportsByThirdParty, err := loadSnapshotComplianceReports(ctx, tx, snapshotID, thirdPartyIDs) if err != nil { return "", err } - baaByVendor, err := loadSnapshotBAAs(ctx, tx, snapshotID, vendorIDs) + baaByThirdParty, err := loadSnapshotBAAs(ctx, tx, snapshotID, thirdPartyIDs) if err != nil { return "", err } - dpaByVendor, err := loadSnapshotDPAs(ctx, tx, snapshotID, vendorIDs) + dpaByThirdParty, err := loadSnapshotDPAs(ctx, tx, snapshotID, thirdPartyIDs) if err != nil { return "", err } - rows := make([]docgen.VendorListRow, 0, len(vendors)) - for _, v := range vendors { - row := docgen.VendorListRow{ + rows := make([]docgen.ThirdPartyListRow, 0, len(thirdParties)) + for _, v := range thirdParties { + row := docgen.ThirdPartyListRow{ Name: v.name, LegalName: deref(v.legalName), Description: deref(v.description), @@ -467,99 +467,99 @@ ORDER BY v.name ASC; Countries: joinOrDefault(v.countries), BusinessOwner: v.businessOwnerName, SecurityOwner: v.securityOwnerName, - Services: servicesByVendor[v.id], - Contacts: contactsByVendor[v.id], - RiskAssessments: assessmentsByVendor[v.id], - ComplianceReports: reportsByVendor[v.id], - BusinessAssociateAgreement: baaByVendor[v.id], - DataPrivacyAgreement: dpaByVendor[v.id], + Services: servicesByThirdParty[v.id], + Contacts: contactsByThirdParty[v.id], + RiskAssessments: assessmentsByThirdParty[v.id], + ComplianceReports: reportsByThirdParty[v.id], + BusinessAssociateAgreement: baaByThirdParty[v.id], + DataPrivacyAgreement: dpaByThirdParty[v.id], } rows = append(rows, row) } - docData := docgen.VendorListData{ - Title: "Vendors", - OrganizationName: orgName, - CreatedAt: publishedAt, - TotalVendors: len(rows), - Rows: rows, + docData := docgen.ThirdPartyListData{ + Title: "ThirdParties", + OrganizationName: orgName, + CreatedAt: publishedAt, + TotalThirdParties: len(rows), + Rows: rows, } - return probo.BuildVendorListDocument(docData) + return probo.BuildThirdPartyListDocument(docData) } -func loadSnapshotServices(ctx context.Context, tx pg.Tx, snapshotID string, vendorIDs []string) (map[string][]docgen.VendorListService, error) { +func loadSnapshotServices(ctx context.Context, tx pg.Tx, snapshotID string, thirdPartyIDs []string) (map[string][]docgen.ThirdPartyListService, error) { rows, err := tx.Query(ctx, - `SELECT vs.vendor_id, vs.name, COALESCE(vs.description, 'Not specified') - FROM vendor_services vs - WHERE vs.snapshot_id = @snapshot_id AND vs.vendor_id = ANY(@vendor_ids) - ORDER BY vs.vendor_id, vs.name ASC`, - pgx.NamedArgs{"snapshot_id": snapshotID, "vendor_ids": vendorIDs}) + `SELECT vs.third_party_id, vs.name, COALESCE(vs.description, 'Not specified') + FROM third_party_services vs + WHERE vs.snapshot_id = @snapshot_id AND vs.third_party_id = ANY(@third_party_ids) + ORDER BY vs.third_party_id, vs.name ASC`, + pgx.NamedArgs{"snapshot_id": snapshotID, "third_party_ids": thirdPartyIDs}) if err != nil { return nil, fmt.Errorf("cannot load snapshot services: %w", err) } defer rows.Close() - result := make(map[string][]docgen.VendorListService) + result := make(map[string][]docgen.ThirdPartyListService) for rows.Next() { - var vendorID, name, desc string - if err := rows.Scan(&vendorID, &name, &desc); err != nil { + var thirdPartyID, name, desc string + if err := rows.Scan(&thirdPartyID, &name, &desc); err != nil { return nil, fmt.Errorf("cannot scan service: %w", err) } - result[vendorID] = append(result[vendorID], docgen.VendorListService{Name: name, Description: desc}) + result[thirdPartyID] = append(result[thirdPartyID], docgen.ThirdPartyListService{Name: name, Description: desc}) } return result, rows.Err() } -func loadSnapshotContacts(ctx context.Context, tx pg.Tx, snapshotID string, vendorIDs []string) (map[string][]docgen.VendorListContact, error) { +func loadSnapshotContacts(ctx context.Context, tx pg.Tx, snapshotID string, thirdPartyIDs []string) (map[string][]docgen.ThirdPartyListContact, error) { rows, err := tx.Query(ctx, - `SELECT vc.vendor_id, + `SELECT vc.third_party_id, COALESCE(vc.full_name, 'Not specified'), COALESCE(vc.email, 'Not specified'), COALESCE(vc.phone, 'Not specified'), COALESCE(vc.role, 'Not specified') - FROM vendor_contacts vc - WHERE vc.snapshot_id = @snapshot_id AND vc.vendor_id = ANY(@vendor_ids) - ORDER BY vc.vendor_id, vc.full_name ASC`, - pgx.NamedArgs{"snapshot_id": snapshotID, "vendor_ids": vendorIDs}) + FROM third_party_contacts vc + WHERE vc.snapshot_id = @snapshot_id AND vc.third_party_id = ANY(@third_party_ids) + ORDER BY vc.third_party_id, vc.full_name ASC`, + pgx.NamedArgs{"snapshot_id": snapshotID, "third_party_ids": thirdPartyIDs}) if err != nil { return nil, fmt.Errorf("cannot load snapshot contacts: %w", err) } defer rows.Close() - result := make(map[string][]docgen.VendorListContact) + result := make(map[string][]docgen.ThirdPartyListContact) for rows.Next() { - var vendorID, name, email, phone, role string - if err := rows.Scan(&vendorID, &name, &email, &phone, &role); err != nil { + var thirdPartyID, name, email, phone, role string + if err := rows.Scan(&thirdPartyID, &name, &email, &phone, &role); err != nil { return nil, fmt.Errorf("cannot scan contact: %w", err) } - result[vendorID] = append(result[vendorID], docgen.VendorListContact{ + result[thirdPartyID] = append(result[thirdPartyID], docgen.ThirdPartyListContact{ FullName: name, Email: email, Phone: phone, Role: role, }) } return result, rows.Err() } -func loadSnapshotRiskAssessments(ctx context.Context, tx pg.Tx, snapshotID string, vendorIDs []string) (map[string][]docgen.VendorListRiskAssessment, error) { +func loadSnapshotRiskAssessments(ctx context.Context, tx pg.Tx, snapshotID string, thirdPartyIDs []string) (map[string][]docgen.ThirdPartyListRiskAssessment, error) { rows, err := tx.Query(ctx, - `SELECT vra.vendor_id, vra.created_at, vra.expires_at, vra.data_sensitivity, vra.business_impact, COALESCE(vra.notes, 'Not specified') - FROM vendor_risk_assessments vra - WHERE vra.snapshot_id = @snapshot_id AND vra.vendor_id = ANY(@vendor_ids) - ORDER BY vra.vendor_id, vra.created_at DESC`, - pgx.NamedArgs{"snapshot_id": snapshotID, "vendor_ids": vendorIDs}) + `SELECT vra.third_party_id, vra.created_at, vra.expires_at, vra.data_sensitivity, vra.business_impact, COALESCE(vra.notes, 'Not specified') + FROM third_party_risk_assessments vra + WHERE vra.snapshot_id = @snapshot_id AND vra.third_party_id = ANY(@third_party_ids) + ORDER BY vra.third_party_id, vra.created_at DESC`, + pgx.NamedArgs{"snapshot_id": snapshotID, "third_party_ids": thirdPartyIDs}) if err != nil { return nil, fmt.Errorf("cannot load snapshot risk assessments: %w", err) } defer rows.Close() - result := make(map[string][]docgen.VendorListRiskAssessment) + result := make(map[string][]docgen.ThirdPartyListRiskAssessment) for rows.Next() { - var vendorID, sensitivity, impact, notes string + var thirdPartyID, sensitivity, impact, notes string var assessedAt, expiresAt time.Time - if err := rows.Scan(&vendorID, &assessedAt, &expiresAt, &sensitivity, &impact, ¬es); err != nil { + if err := rows.Scan(&thirdPartyID, &assessedAt, &expiresAt, &sensitivity, &impact, ¬es); err != nil { return nil, fmt.Errorf("cannot scan risk assessment: %w", err) } - result[vendorID] = append(result[vendorID], docgen.VendorListRiskAssessment{ + result[thirdPartyID] = append(result[thirdPartyID], docgen.ThirdPartyListRiskAssessment{ AssessedAt: assessedAt.Format("2006-01-02"), ExpiresAt: expiresAt.Format("2006-01-02"), DataSensitivity: sensitivity, @@ -570,81 +570,81 @@ func loadSnapshotRiskAssessments(ctx context.Context, tx pg.Tx, snapshotID strin return result, rows.Err() } -func loadSnapshotComplianceReports(ctx context.Context, tx pg.Tx, snapshotID string, vendorIDs []string) (map[string][]docgen.VendorListComplianceReport, error) { +func loadSnapshotComplianceReports(ctx context.Context, tx pg.Tx, snapshotID string, thirdPartyIDs []string) (map[string][]docgen.ThirdPartyListComplianceReport, error) { rows, err := tx.Query(ctx, - `SELECT vcr.vendor_id, vcr.report_name, vcr.report_date, vcr.valid_until - FROM vendor_compliance_reports vcr - WHERE vcr.snapshot_id = @snapshot_id AND vcr.vendor_id = ANY(@vendor_ids) - ORDER BY vcr.vendor_id, vcr.report_date DESC`, - pgx.NamedArgs{"snapshot_id": snapshotID, "vendor_ids": vendorIDs}) + `SELECT vcr.third_party_id, vcr.report_name, vcr.report_date, vcr.valid_until + FROM third_party_compliance_reports vcr + WHERE vcr.snapshot_id = @snapshot_id AND vcr.third_party_id = ANY(@third_party_ids) + ORDER BY vcr.third_party_id, vcr.report_date DESC`, + pgx.NamedArgs{"snapshot_id": snapshotID, "third_party_ids": thirdPartyIDs}) if err != nil { return nil, fmt.Errorf("cannot load snapshot compliance reports: %w", err) } defer rows.Close() - result := make(map[string][]docgen.VendorListComplianceReport) + result := make(map[string][]docgen.ThirdPartyListComplianceReport) for rows.Next() { - var vendorID, name string + var thirdPartyID, name string var reportDate time.Time var validUntil *time.Time - if err := rows.Scan(&vendorID, &name, &reportDate, &validUntil); err != nil { + if err := rows.Scan(&thirdPartyID, &name, &reportDate, &validUntil); err != nil { return nil, fmt.Errorf("cannot scan compliance report: %w", err) } vu := "Not specified" if validUntil != nil { vu = validUntil.Format("2006-01-02") } - result[vendorID] = append(result[vendorID], docgen.VendorListComplianceReport{ + result[thirdPartyID] = append(result[thirdPartyID], docgen.ThirdPartyListComplianceReport{ ReportName: name, ReportDate: reportDate.Format("2006-01-02"), ValidUntil: vu, }) } return result, rows.Err() } -func loadSnapshotBAAs(ctx context.Context, tx pg.Tx, snapshotID string, vendorIDs []string) (map[string]*docgen.VendorListAgreement, error) { +func loadSnapshotBAAs(ctx context.Context, tx pg.Tx, snapshotID string, thirdPartyIDs []string) (map[string]*docgen.ThirdPartyListAgreement, error) { rows, err := tx.Query(ctx, - `SELECT vbaa.vendor_id, vbaa.valid_from, vbaa.valid_until - FROM vendor_business_associate_agreements vbaa - WHERE vbaa.snapshot_id = @snapshot_id AND vbaa.vendor_id = ANY(@vendor_ids)`, - pgx.NamedArgs{"snapshot_id": snapshotID, "vendor_ids": vendorIDs}) + `SELECT vbaa.third_party_id, vbaa.valid_from, vbaa.valid_until + FROM third_party_business_associate_agreements vbaa + WHERE vbaa.snapshot_id = @snapshot_id AND vbaa.third_party_id = ANY(@third_party_ids)`, + pgx.NamedArgs{"snapshot_id": snapshotID, "third_party_ids": thirdPartyIDs}) if err != nil { return nil, fmt.Errorf("cannot load snapshot BAAs: %w", err) } defer rows.Close() - result := make(map[string]*docgen.VendorListAgreement) + result := make(map[string]*docgen.ThirdPartyListAgreement) for rows.Next() { - var vendorID string + var thirdPartyID string var validFrom, validUntil *time.Time - if err := rows.Scan(&vendorID, &validFrom, &validUntil); err != nil { + if err := rows.Scan(&thirdPartyID, &validFrom, &validUntil); err != nil { return nil, fmt.Errorf("cannot scan BAA: %w", err) } - result[vendorID] = &docgen.VendorListAgreement{ + result[thirdPartyID] = &docgen.ThirdPartyListAgreement{ ValidFrom: fmtTime(validFrom), ValidUntil: fmtTime(validUntil), } } return result, rows.Err() } -func loadSnapshotDPAs(ctx context.Context, tx pg.Tx, snapshotID string, vendorIDs []string) (map[string]*docgen.VendorListAgreement, error) { +func loadSnapshotDPAs(ctx context.Context, tx pg.Tx, snapshotID string, thirdPartyIDs []string) (map[string]*docgen.ThirdPartyListAgreement, error) { rows, err := tx.Query(ctx, - `SELECT vdpa.vendor_id, vdpa.valid_from, vdpa.valid_until - FROM vendor_data_privacy_agreements vdpa - WHERE vdpa.snapshot_id = @snapshot_id AND vdpa.vendor_id = ANY(@vendor_ids)`, - pgx.NamedArgs{"snapshot_id": snapshotID, "vendor_ids": vendorIDs}) + `SELECT vdpa.third_party_id, vdpa.valid_from, vdpa.valid_until + FROM third_party_data_privacy_agreements vdpa + WHERE vdpa.snapshot_id = @snapshot_id AND vdpa.third_party_id = ANY(@third_party_ids)`, + pgx.NamedArgs{"snapshot_id": snapshotID, "third_party_ids": thirdPartyIDs}) if err != nil { return nil, fmt.Errorf("cannot load snapshot DPAs: %w", err) } defer rows.Close() - result := make(map[string]*docgen.VendorListAgreement) + result := make(map[string]*docgen.ThirdPartyListAgreement) for rows.Next() { - var vendorID string + var thirdPartyID string var validFrom, validUntil *time.Time - if err := rows.Scan(&vendorID, &validFrom, &validUntil); err != nil { + if err := rows.Scan(&thirdPartyID, &validFrom, &validUntil); err != nil { return nil, fmt.Errorf("cannot scan DPA: %w", err) } - result[vendorID] = &docgen.VendorListAgreement{ + result[thirdPartyID] = &docgen.ThirdPartyListAgreement{ ValidFrom: fmtTime(validFrom), ValidUntil: fmtTime(validUntil), } } diff --git a/contrib/claude/app-arborescence.md b/contrib/claude/app-arborescence.md index e62b05243..f3bb886da 100644 --- a/contrib/claude/app-arborescence.md +++ b/contrib/claude/app-arborescence.md @@ -22,12 +22,12 @@ The `pages/` folder **is** the route tree. Every route segment maps to a folder // Bad — separate routes/ folder duplicates pages/ structure src/ routes/ - vendorRoutes.ts # route definitions for vendors + thirdPartyRoutes.ts # route definitions for third parties assetRoutes.ts # route definitions for assets pages/ organizations/ - vendors/ - VendorsPage.tsx + third-parties/ + ThirdPartiesPage.tsx assets/ AssetsPage.tsx ``` @@ -37,9 +37,9 @@ src/ src/ pages/ organizations/ - vendors/ - routes.ts # route definitions for vendors - VendorsPage.tsx + third-parties/ + routes.ts # route definitions for third parties + ThirdPartiesPage.tsx assets/ routes.ts # route definitions for assets AssetsPage.tsx @@ -77,11 +77,11 @@ Use the correct suffix so the role is clear from the file name alone: ```text // Bad — a layout route named as a "Page" -VendorDetailPage.tsx # renders , wraps child routes +ThirdPartyDetailPage.tsx # renders , wraps child routes CookieBannerConfigPage.tsx # renders tabs + // Good — layout routes use the "Layout" suffix -VendorDetailLayout.tsx +ThirdPartyDetailLayout.tsx CookieBannerConfigLayout.tsx ``` @@ -142,26 +142,26 @@ export default function CookieBannerLayout() { Contains route objects for the current folder's feature, exported as a named array and spread into the parent. Keep imports minimal — only `lazy`, skeleton components, and typing. ```ts -// pages/organizations/vendors/routes.ts +// pages/organizations/third-parties/routes.ts import { lazy } from "@probo/react-lazy"; import type { AppRoute } from "@probo/routes"; -import { VendorsPageSkeleton } from "./VendorsPageSkeleton"; +import { ThirdPartiesPageSkeleton } from "./ThirdPartiesPageSkeleton"; -export const vendorRoutes = [ +export const thirdPartyRoutes = [ { - path: "vendors", - Fallback: VendorsPageSkeleton, - Component: lazy(() => import("./VendorsPageLoader")), + path: "third-parties", + Fallback: ThirdPartiesPageSkeleton, + Component: lazy(() => import("./ThirdPartiesPageLoader")), }, { - path: "vendors/:vendorId", - Fallback: VendorsPageSkeleton, - Component: lazy(() => import("./VendorDetailLayoutLoader")), + path: "third-parties/:thirdPartyId", + Fallback: ThirdPartiesPageSkeleton, + Component: lazy(() => import("./ThirdPartyDetailLayoutLoader")), children: [ { path: "overview", - Component: lazy(() => import("./overview/VendorOverviewPage")), + Component: lazy(() => import("./overview/ThirdPartyOverviewPage")), }, ], }, @@ -173,35 +173,35 @@ export const vendorRoutes = [ The loader is the **lazy bundle entry point**. It sets up providers, triggers the Relay query, shows a skeleton until the query resolves, then renders the page. ```tsx -// pages/organizations/vendors/VendorsPageLoader.tsx +// pages/organizations/third-parties/ThirdPartiesPageLoader.tsx import { Suspense, useEffect } from "react"; import { useQueryLoader } from "react-relay"; -import type { VendorsPageQuery } from "#/__generated__/core/VendorsPageQuery.graphql"; +import type { ThirdPartiesPageQuery } from "#/__generated__/core/ThirdPartiesPageQuery.graphql"; import { useOrganizationId } from "#/hooks/useOrganizationId"; -import VendorsPage, { vendorsPageQuery } from "./VendorsPage"; -import { VendorsPageSkeleton } from "./VendorsPageSkeleton"; +import ThirdPartiesPage, { thirdPartiesPageQuery } from "./ThirdPartiesPage"; +import { ThirdPartiesPageSkeleton } from "./ThirdPartiesPageSkeleton"; -function VendorsPageQueryLoader() { +function ThirdPartiesPageQueryLoader() { const organizationId = useOrganizationId(); - const [queryRef, loadQuery] = useQueryLoader(vendorsPageQuery); + const [queryRef, loadQuery] = useQueryLoader(thirdPartiesPageQuery); useEffect(() => { loadQuery({ organizationId }); }, [loadQuery, organizationId]); if (!queryRef) { - return ; + return ; } - return + return } -export default function VendorsPageLoader() { +export default function ThirdPartiesPageLoader() { return ( - + ); } @@ -212,9 +212,9 @@ export default function VendorsPageLoader() { Receives the `queryRef` from the loader and renders the UI. Default export so `lazy()` can import it. ```tsx -// pages/organizations/vendors/VendorsPage.tsx -export default function VendorsPage({ queryRef }: VendorsPageProps) { - const data = usePreloadedQuery(vendorsPageQuery, queryRef); +// pages/organizations/third-parties/ThirdPartiesPage.tsx +export default function ThirdPartiesPage({ queryRef }: ThirdPartiesPageProps) { + const data = usePreloadedQuery(thirdPartiesPageQuery, queryRef); return (/* … */); } ``` @@ -224,8 +224,8 @@ export default function VendorsPage({ queryRef }: VendorsPageProps) { A lightweight loading placeholder. Keep it free of data-fetching logic so it loads instantly. ```tsx -// pages/organizations/vendors/VendorsPageSkeleton.tsx -export function VendorsPageSkeleton() { +// pages/organizations/third-parties/ThirdPartiesPageSkeleton.tsx +export function ThirdPartiesPageSkeleton() { return (/* pulse / skeleton UI */); } ``` @@ -235,8 +235,8 @@ export function VendorsPageSkeleton() { Rendered by the route error boundary when the page throws. ```tsx -// pages/organizations/vendors/VendorsPageError.tsx -export function VendorsPageError() { +// pages/organizations/third-parties/ThirdPartiesPageError.tsx +export function ThirdPartiesPageError() { const error = useRouteError(); return (/* error UI */); } @@ -244,24 +244,24 @@ export function VendorsPageError() { ## File naming -Component files (`.tsx` that export a React component) use **PascalCase**: `VendorsPage.tsx`, `VendorContactRow.tsx`, `VendorsPageSkeleton.tsx`. +Component files (`.tsx` that export a React component) use **PascalCase**: `ThirdPartiesPage.tsx`, `ThirdPartyContactRow.tsx`, `ThirdPartiesPageSkeleton.tsx`. -All other helper files (utilities, hooks, constants, configuration) use **camelCase**: `routes.ts`, `useVendorFilters.ts`, `formatCurrency.ts`, `constants.ts`. +All other helper files (utilities, hooks, constants, configuration) use **camelCase**: `routes.ts`, `useThirdPartyFilters.ts`, `formatCurrency.ts`, `constants.ts`. ### Do / don't: file naming ```text // Bad — helper file in PascalCase -pages/organizations/vendors/FormatVendorStatus.ts -pages/organizations/vendors/UseVendorFilters.ts -pages/organizations/vendors/Routes.ts +pages/organizations/third-parties/FormatThirdPartyStatus.ts +pages/organizations/third-parties/UseThirdPartyFilters.ts +pages/organizations/third-parties/Routes.ts // Good — helpers are camelCase, components are PascalCase -pages/organizations/vendors/formatVendorStatus.ts -pages/organizations/vendors/useVendorFilters.ts -pages/organizations/vendors/routes.ts -pages/organizations/vendors/VendorsPage.tsx -pages/organizations/vendors/VendorsPageSkeleton.tsx +pages/organizations/third-parties/formatThirdPartyStatus.ts +pages/organizations/third-parties/useThirdPartyFilters.ts +pages/organizations/third-parties/routes.ts +pages/organizations/third-parties/ThirdPartiesPage.tsx +pages/organizations/third-parties/ThirdPartiesPageSkeleton.tsx ``` ## `_components` folder @@ -270,7 +270,7 @@ Sub-components that are used **only** by a single page live in a `_components/` | Situation | Where the component lives | | ------------------------------------------ | ---------------------------------------------------------------------------------- | -| Used by one page only | `pages/organizations/vendors/_components/` | +| Used by one page only | `pages/organizations/third-parties/_components/` | | Used by multiple pages in the same feature | Nearest common ancestor's `_components/` (e.g. `pages/organizations/_components/`) | | Reusable UI primitive | `@probo/ui` package | @@ -278,8 +278,8 @@ Sub-components that are used **only** by a single page live in a `_components/` ```text // Bad — shared component buried in a single page's _components -pages/organizations/vendors/_components/StatusBadge.tsx # also used by risks page -pages/organizations/risks/SomeRiskPage.tsx # imports ../../vendors/_components/StatusBadge +pages/organizations/third-parties/_components/StatusBadge.tsx # also used by risks page +pages/organizations/risks/SomeRiskPage.tsx # imports ../../third-parties/_components/StatusBadge // Good — shared component hoisted to common ancestor pages/organizations/_components/StatusBadge.tsx @@ -287,10 +287,10 @@ pages/organizations/_components/StatusBadge.tsx ```text // Bad — page-specific helper placed in a global folder -src/components/VendorContactRow.tsx # only used by VendorContactsTab +src/components/ThirdPartyContactRow.tsx # only used by ThirdPartyContactsTab // Good — scoped to the page that uses it -pages/organizations/vendors/_components/VendorContactRow.tsx +pages/organizations/third-parties/_components/ThirdPartyContactRow.tsx ``` ## Child-route folder naming @@ -303,40 +303,40 @@ Folders that contain child-route pages are named after the **resource or concept // Bad — folder named after a UI element configuration/ tabs/ # "tabs" is a UI component, not a resource - VendorOverviewTab.tsx - VendorComplianceTab.tsx + ThirdPartyOverviewTab.tsx + ThirdPartyComplianceTab.tsx // Good — folders named after the resource each child route represents configuration/ overview/ - VendorOverviewPage.tsx + ThirdPartyOverviewPage.tsx compliance/ - VendorCompliancePage.tsx + ThirdPartyCompliancePage.tsx ``` This also means child-route components use the `*Page` suffix (not `*Tab`), because they are pages in their own right — the fact that a tab bar navigates between them is an implementation detail of the parent layout. ## Full example tree -Target layout for a `vendors` feature under `pages/organizations/`: +Target layout for a `third-parties` feature under `pages/organizations/`: ```text -pages/organizations/vendors/ - routes.ts # route definitions for vendors - VendorsPageLoader.tsx # lazy entry — providers + Suspense + query loader - VendorsPage.tsx # page component (usePreloadedQuery) - VendorsPageSkeleton.tsx # loading fallback - VendorDetailLayoutLoader.tsx # lazy entry for detail layout - VendorDetailLayout.tsx # layout — breadcrumbs, tabs, - VendorDetailLayoutSkeleton.tsx # detail loading fallback - NewVendorPage.tsx # mutation-only page — default export, wraps itself in the Relay provider - _components/ # sub-components used only by vendor pages - VendorContactRow.tsx - VendorRiskSummary.tsx - overview/ # child route: /vendors/:vendorId/overview - VendorOverviewPage.tsx - compliance/ # child route: /vendors/:vendorId/compliance - VendorCompliancePage.tsx - contacts/ # child route: /vendors/:vendorId/contacts - VendorContactsPage.tsx +pages/organizations/third-parties/ + routes.ts # route definitions for third parties + ThirdPartiesPageLoader.tsx # lazy entry — providers + Suspense + query loader + ThirdPartiesPage.tsx # page component (usePreloadedQuery) + ThirdPartiesPageSkeleton.tsx # loading fallback + ThirdPartyDetailLayoutLoader.tsx # lazy entry for detail layout + ThirdPartyDetailLayout.tsx # layout — breadcrumbs, tabs, + ThirdPartyDetailLayoutSkeleton.tsx # detail loading fallback + NewThirdPartyPage.tsx # mutation-only page — default export, wraps itself in the Relay provider + _components/ # sub-components used only by third party pages + ThirdPartyContactRow.tsx + ThirdPartyRiskSummary.tsx + overview/ # child route: /third-parties/:thirdPartyId/overview + ThirdPartyOverviewPage.tsx + compliance/ # child route: /third-parties/:thirdPartyId/compliance + ThirdPartyCompliancePage.tsx + contacts/ # child route: /third-parties/:thirdPartyId/contacts + ThirdPartyContactsPage.tsx ``` diff --git a/contrib/claude/authorization.md b/contrib/claude/authorization.md index c34e82cec..197090415 100644 --- a/contrib/claude/authorization.md +++ b/contrib/claude/authorization.md @@ -8,18 +8,18 @@ Policy-based authorization in `pkg/iam/` using an evaluation model similar to AW **Policy** — a named collection of statements: ```go -policy.NewPolicy("vendor-crud", "Vendor CRUD", - policy.Allow(ActionVendorGet, ActionVendorList).WithSID("read-vendors"), - policy.Deny(ActionVendorDelete).WithSID("deny-vendor-delete"), -).WithDescription("Standard vendor access") +policy.NewPolicy("thirdParty-crud", "ThirdParty CRUD", + policy.Allow(ActionThirdPartyGet, ActionThirdPartyList).WithSID("read-thirdParties"), + policy.Deny(ActionThirdPartyDelete).WithSID("deny-thirdParty-delete"), +).WithDescription("Standard third party access") ``` **Statement** — a single permission rule with effect (allow/deny), actions, optional resources, and optional conditions. **Action format** — `SERVICE:RESOURCE:OPERATION` with wildcard support: ``` -core:vendor:create # specific action -core:vendor:* # all vendor actions +core:thirdParty:create # specific action +core:thirdParty:* # all third party actions core:* # all core actions * # everything ``` @@ -39,8 +39,8 @@ The evaluator processes all statements against a request: ```go err := iamService.Authorizer.Authorize(ctx, iam.AuthorizeParams{ Principal: identityID, // who - Resource: vendorID, // what - Action: probo.ActionVendorGet, // which action + Resource: thirdPartyID, // what + Action: probo.ActionThirdPartyGet, // which action ResourceAttributes: map[string]string{}, // optional extra attributes }) ``` @@ -78,8 +78,8 @@ Conditions constrain when a statement applies. All conditions must be satisfied. // Users can only access resources in their organization organizationCondition := policy.Equals("principal.organization_id", "resource.organization_id") -policy.Allow(ActionVendorGet). - WithSID("view-vendor"). +policy.Allow(ActionThirdPartyGet). + WithSID("view-thirdParty"). When(organizationCondition) ``` @@ -97,14 +97,14 @@ Key paths use `principal.ATTR` or `resource.ATTR` (e.g., `principal.organization Resources that support authorization must implement this interface in `pkg/coredata/`: ```go -func (v *Vendor) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) { - q := `SELECT organization_id FROM vendors WHERE id = $1 LIMIT 1;` +func (v *ThirdParty) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) { + q := `SELECT organization_id FROM thirdParties WHERE id = $1 LIMIT 1;` var organizationID gid.GID if err := conn.QueryRow(ctx, q, v.ID).Scan(&organizationID); err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, ErrResourceNotFound } - return nil, fmt.Errorf("cannot query vendor authorization attributes: %w", err) + return nil, fmt.Errorf("cannot query third party authorization attributes: %w", err) } return map[string]string{"organization_id": organizationID.String()}, nil } @@ -126,14 +126,14 @@ var ( **GraphQL resolvers** use `AuthorizeFunc` from `pkg/server/api/authz/`: ```go -if err := authorize(ctx, vendorID, probo.ActionVendorGet); err != nil { +if err := authorize(ctx, thirdPartyID, probo.ActionThirdPartyGet); err != nil { return nil, err } ``` **MCP resolvers** use `MustAuthorize` which panics (caught by middleware): ```go -r.MustAuthorize(ctx, input.ID, probo.ActionVendorGet) +r.MustAuthorize(ctx, input.ID, probo.ActionThirdPartyGet) ``` ## File locations @@ -155,11 +155,11 @@ IAM actions live in `pkg/iam/iam_actions.go`, probo actions in `pkg/probo/action ```go const ( - ActionVendorGet = "core:vendor:get" - ActionVendorList = "core:vendor:list" - ActionVendorCreate = "core:vendor:create" - ActionVendorUpdate = "core:vendor:update" - ActionVendorDelete = "core:vendor:delete" + ActionThirdPartyGet = "core:thirdParty:get" + ActionThirdPartyList = "core:thirdParty:list" + ActionThirdPartyCreate = "core:thirdParty:create" + ActionThirdPartyUpdate = "core:thirdParty:update" + ActionThirdPartyDelete = "core:thirdParty:delete" ) ``` diff --git a/contrib/claude/commit.md b/contrib/claude/commit.md index 1a1585433..9594e03b0 100644 --- a/contrib/claude/commit.md +++ b/contrib/claude/commit.md @@ -13,18 +13,18 @@ Follow the [seven rules of a great Git commit message](https://cbea.ms/git-commi The subject line should complete the sentence: "If applied, this commit will *your subject line here*". ``` -Add vendor assessment agent for third-party reviews +Add third-party assessment agent for third-party reviews The existing changelog generator only covers internal changes. This introduces a dedicated agent that evaluates third-party -vendors against our compliance criteria, producing a structured +thirdParties against our compliance criteria, producing a structured risk report. ``` Not every commit needs a body -- a single line is fine when the change is self-explanatory: ``` -Fix typo in vendor assessment prompt +Fix typo in third-party assessment prompt ``` ## Signing and Authorship diff --git a/contrib/claude/e2e.md b/contrib/claude/e2e.md index ab079cedc..6d731a2ea 100644 --- a/contrib/claude/e2e.md +++ b/contrib/claude/e2e.md @@ -43,7 +43,7 @@ Two patterns in `e2e/internal/factory/`: **Builder pattern (preferred):** ```go -vendorID := factory.NewVendor(owner). +thirdPartyID := factory.NewThirdParty(owner). WithName("Stripe"). WithCategory("CLOUD_PROVIDER"). Create() @@ -59,7 +59,7 @@ controlID := factory.NewControl(owner, frameworkID). **Simple factory functions:** ```go -vendorID := factory.CreateVendor(c, factory.Attrs{"name": "Acme"}) +thirdPartyID := factory.CreateThirdParty(c, factory.Attrs{"name": "Acme"}) taskID := factory.CreateTask(c, &measureID, factory.Attrs{"name": "Task 1"}) ``` @@ -70,7 +70,7 @@ Use `factory.SafeName("prefix")` for unique names and `factory.SafeEmail()` for Every test and subtest **must** call `t.Parallel()`. One test file per entity in `e2e/console/`. Function naming: `TestEntity_Operation`. ```go -func TestVendor_Create(t *testing.T) { +func TestThirdParty_Create(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) @@ -78,9 +78,9 @@ func TestVendor_Create(t *testing.T) { t.Parallel() const query = ` - mutation CreateVendor($input: CreateVendorInput!) { - createVendor(input: $input) { - vendorEdge { + mutation CreateThirdParty($input: CreateThirdPartyInput!) { + createThirdParty(input: $input) { + thirdPartyEdge { node { id name } } } @@ -88,25 +88,25 @@ func TestVendor_Create(t *testing.T) { ` var result struct { - CreateVendor struct { - VendorEdge struct { + CreateThirdParty struct { + ThirdPartyEdge struct { Node struct { ID string `json:"id"` Name string `json:"name"` } `json:"node"` - } `json:"vendorEdge"` - } `json:"createVendor"` + } `json:"thirdPartyEdge"` + } `json:"createThirdParty"` } err := owner.Execute(query, map[string]any{ "input": map[string]any{ "organizationId": owner.GetOrganizationID().String(), - "name": factory.SafeName("Vendor"), + "name": factory.SafeName("ThirdParty"), }, }, &result) require.NoError(t, err) - assert.NotEmpty(t, result.CreateVendor.VendorEdge.Node.ID) + assert.NotEmpty(t, result.CreateThirdParty.ThirdPartyEdge.Node.ID) }) } ``` @@ -139,13 +139,13 @@ t.Run("other org cannot access", func(t *testing.T) { owner1 := testutil.NewClient(t, testutil.RoleOwner) owner2 := testutil.NewClient(t, testutil.RoleOwner) - vendorID := factory.NewVendor(owner1).WithName("Vendor").Create() + thirdPartyID := factory.NewThirdParty(owner1).WithName("ThirdParty").Create() var result struct { Node *struct{ ID string } `json:"node"` } - err := owner2.Execute(nodeQuery, map[string]any{"id": vendorID}, &result) - testutil.AssertNodeNotAccessible(t, err, result.Node == nil, "Vendor") + err := owner2.Execute(nodeQuery, map[string]any{"id": thirdPartyID}, &result) + testutil.AssertNodeNotAccessible(t, err, result.Node == nil, "ThirdParty") }) ``` @@ -214,7 +214,7 @@ for _, tt := range tests { ```go err := owner.ExecuteWithFile( uploadQuery, - map[string]any{"input": map[string]any{"vendorId": vendorID, "file": nil}}, + map[string]any{"input": map[string]any{"thirdPartyId": thirdPartyID, "file": nil}}, "input.file", testutil.UploadFile{ Filename: "report.pdf", diff --git a/contrib/claude/go-style.md b/contrib/claude/go-style.md index 07ed771ee..1e6ca3d89 100644 --- a/contrib/claude/go-style.md +++ b/contrib/claude/go-style.md @@ -131,7 +131,7 @@ if errors.As(err, &ve) { - Constructors: `New*` (e.g. `NewService`, `NewServer`, `NewBridge`) - Config structs: `*Config` suffix (e.g. `APIConfig`, `PgConfig`, `TrustCenterConfig`) - Request structs: `*Request` suffix (e.g. `UpdateTrustCenterRequest`) -- Unexported types for internal data: lowercase (e.g. `vendorInfo`, `ctxKey`) +- Unexported types for internal data: lowercase (e.g. `thirdPartyInfo`, `ctxKey`) ## Functional options and Config structs diff --git a/contrib/claude/graphql.md b/contrib/claude/graphql.md index 9ac33287a..5b727be4e 100644 --- a/contrib/claude/graphql.md +++ b/contrib/claude/graphql.md @@ -7,9 +7,9 @@ Schema-first GraphQL using [gqlgen](https://gqlgen.com/). The schema is hand-wri Each API's schema lives in `pkg/server/api/{api}/v1/graphql/` as multiple `.graphql` files, one per coredata model: - `base.graphql` — directives, scalars, Node interface, PageInfo, OrderDirection, root Query/Mutation/Organization types -- Entity files (e.g., `vendor.graphql`, `control.graphql`) — use `extend type Mutation` to add their mutations. +- Entity files (e.g., `thirdParty.graphql`, `control.graphql`) — use `extend type Mutation` to add their mutations. -gqlgen's `follow-schema` layout generates one resolver file per schema file (e.g., `vendor.resolvers.go`). Types that get extended across files (Organization, Mutation, Viewer, TrustCenter) must be defined in `base.graphql`. +gqlgen's `follow-schema` layout generates one resolver file per schema file (e.g., `thirdParty.resolvers.go`). Types that get extended across files (Organization, Mutation, Viewer, TrustCenter) must be defined in `base.graphql`. ### `extend type` restrictions @@ -20,18 +20,18 @@ gqlgen's `follow-schema` layout generates one resolver file per schema file (e.g **Always define a custom Go type for connection types** using the `@goModel` directive. The model path points to the `types` package for the relevant API. The `totalCount` field must use `@goField(forceResolver: true)`. Edge types do not need `@goModel`. ```graphql -type VendorConnection +type ThirdPartyConnection @goModel( - model: "go.probo.inc/probo/pkg/server/api/console/v1/types.VendorConnection" + model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ThirdPartyConnection" ) { totalCount: Int! @goField(forceResolver: true) - edges: [VendorEdge!]! + edges: [ThirdPartyEdge!]! pageInfo: PageInfo! } -type VendorEdge { +type ThirdPartyEdge { cursor: CursorKey! - node: Vendor! + node: ThirdParty! } ``` @@ -42,12 +42,12 @@ Without `@goModel`, gqlgen generates a default struct that lacks the custom fiel Map GraphQL enums to existing Go types using `@goModel` on the enum and `@goEnum` on each value: ```graphql -enum VendorOrderField - @goModel(model: "go.probo.inc/probo/pkg/coredata.VendorOrderField") { +enum ThirdPartyOrderField + @goModel(model: "go.probo.inc/probo/pkg/coredata.ThirdPartyOrderField") { CREATED_AT - @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorOrderFieldCreatedAt") + @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyOrderFieldCreatedAt") NAME - @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorOrderFieldName") + @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyOrderFieldName") } ``` @@ -88,14 +88,14 @@ Connection fields on parent types use standard Relay arguments: ```graphql type Organization { - vendors( + thirdParties( first: Int after: CursorKey last: Int before: CursorKey - orderBy: VendorOrder - filter: VendorFilter - ): VendorConnection! + orderBy: ThirdPartyOrder + filter: ThirdPartyFilter + ): ThirdPartyConnection! } ``` @@ -105,11 +105,11 @@ Each connection type lives in `types/*_connection.go` and follows this structure ```go type ( - VendorOrderBy OrderBy[coredata.VendorOrderField] + ThirdPartyOrderBy OrderBy[coredata.ThirdPartyOrderField] - VendorConnection struct { + ThirdPartyConnection struct { TotalCount int - Edges []*VendorEdge + Edges []*ThirdPartyEdge PageInfo PageInfo Resolver any @@ -117,17 +117,17 @@ type ( } ) -func NewVendorConnection( - p *page.Page[*coredata.Vendor, coredata.VendorOrderField], +func NewThirdPartyConnection( + p *page.Page[*coredata.ThirdParty, coredata.ThirdPartyOrderField], parentType any, parentID gid.GID, -) *VendorConnection { - edges := make([]*VendorEdge, len(p.Data)) +) *ThirdPartyConnection { + edges := make([]*ThirdPartyEdge, len(p.Data)) for i, v := range p.Data { - edges[i] = NewVendorEdge(v, p.Cursor.OrderBy.Field) + edges[i] = NewThirdPartyEdge(v, p.Cursor.OrderBy.Field) } - return &VendorConnection{ + return &ThirdPartyConnection{ Edges: edges, PageInfo: *NewPageInfo(p), @@ -136,13 +136,13 @@ func NewVendorConnection( } } -func NewVendorEdge( - v *coredata.Vendor, - orderBy coredata.VendorOrderField, -) *VendorEdge { - return &VendorEdge{ +func NewThirdPartyEdge( + v *coredata.ThirdParty, + orderBy coredata.ThirdPartyOrderField, +) *ThirdPartyEdge { + return &ThirdPartyEdge{ Cursor: v.CursorKey(orderBy), - Node: NewVendor(v), + Node: NewThirdParty(v), } } ``` diff --git a/contrib/claude/mcp.md b/contrib/claude/mcp.md index 7fe4efbb6..1341f85e1 100644 --- a/contrib/claude/mcp.md +++ b/contrib/claude/mcp.md @@ -8,7 +8,7 @@ MCP tools are defined in `pkg/server/api/mcp/v1/specification.yaml` and generate - `specification.yaml` — tool definitions, input/output schemas, component schemas - `resolver.go` — `Resolver` struct, `MustAuthorize`, service accessors - `helpers.go` — pagination helpers, `UnwrapOmittable` -- `types/*.go` (except `types/types.go`) — type conversion helpers (`NewVendor()`, `NewListVendorsOutput()`, etc.) +- `types/*.go` (except `types/types.go`) — type conversion helpers (`NewThirdParty()`, `NewListThirdPartiesOutput()`, etc.) - `schema.resolvers.go` — tool implementation bodies (stubs generated, you edit the bodies) **Generated** (do not edit): @@ -24,16 +24,16 @@ go generate ./pkg/server/api/mcp/v1 ```yaml tools: - - name: listVendors - description: List all vendors for the organization + - name: listThirdParties + description: List all thirdParties for the organization hints: readonly: true idempotent: true destructive: false inputSchema: - $ref: "#/components/schemas/ListVendorsInput" + $ref: "#/components/schemas/ListThirdPartiesInput" outputSchema: - $ref: "#/components/schemas/ListVendorsOutput" + $ref: "#/components/schemas/ListThirdPartiesOutput" ``` Input/output schemas reference `components/schemas`. Map custom Go types with the `go.probo.inc/mcpgen/type` extension: @@ -51,11 +51,11 @@ components: Generated stubs follow this pattern: ```go -func (r *Resolver) ListVendorsTool( +func (r *Resolver) ListThirdPartiesTool( ctx context.Context, req *mcp.CallToolRequest, - input *types.ListVendorsInput, -) (*mcp.CallToolResult, types.ListVendorsOutput, error) + input *types.ListThirdPartiesInput, +) (*mcp.CallToolResult, types.ListThirdPartiesOutput, error) ``` First return is always `nil`. Errors are either returned (for recoverable) or panicked (for authorization and unexpected failures). @@ -65,24 +65,24 @@ First return is always `nil`. Errors are either returned (for recoverable) or pa Use `MustAuthorize` which panics on failure (caught by middleware): ```go -r.MustAuthorize(ctx, input.OrganizationID, probo.ActionVendorList) +r.MustAuthorize(ctx, input.OrganizationID, probo.ActionThirdPartyList) ``` ## Common resolver patterns **List with pagination:** ```go -func (r *Resolver) ListVendorsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListVendorsInput) (*mcp.CallToolResult, types.ListVendorsOutput, error) { - r.MustAuthorize(ctx, input.OrganizationID, probo.ActionVendorList) +func (r *Resolver) ListThirdPartiesTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListThirdPartiesInput) (*mcp.CallToolResult, types.ListThirdPartiesOutput, error) { + r.MustAuthorize(ctx, input.OrganizationID, probo.ActionThirdPartyList) prb := r.ProboService(ctx, input.OrganizationID) - pageOrderBy := page.OrderBy[coredata.VendorOrderField]{ - Field: coredata.VendorOrderFieldCreatedAt, + pageOrderBy := page.OrderBy[coredata.ThirdPartyOrderField]{ + Field: coredata.ThirdPartyOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, } if input.OrderBy != nil { - pageOrderBy = page.OrderBy[coredata.VendorOrderField]{ + pageOrderBy = page.OrderBy[coredata.ThirdPartyOrderField]{ Field: input.OrderBy.Field, Direction: input.OrderBy.Direction, } @@ -90,12 +90,12 @@ func (r *Resolver) ListVendorsTool(ctx context.Context, req *mcp.CallToolRequest cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) - page, err := prb.Vendors.ListForOrganizationID(ctx, input.OrganizationID, cursor, coredata.NewVendorFilter(nil, nil)) + page, err := prb.ThirdParties.ListForOrganizationID(ctx, input.OrganizationID, cursor, coredata.NewThirdPartyFilter(nil, nil)) if err != nil { - panic(fmt.Errorf("cannot list vendors: %w", err)) + panic(fmt.Errorf("cannot list thirdParties: %w", err)) } - return nil, types.NewListVendorsOutput(page), nil + return nil, types.NewListThirdPartiesOutput(page), nil } ``` @@ -156,8 +156,8 @@ Description: UnwrapOmittable(input.Description), Live in `types/*.go` (not the generated `types/types.go`). One file per entity: ```go -func NewVendor(v *coredata.Vendor) *Vendor { - return &Vendor{ +func NewThirdParty(v *coredata.ThirdParty) *ThirdParty { + return &ThirdParty{ ID: v.ID, OrganizationID: v.OrganizationID, Name: v.Name, @@ -166,21 +166,21 @@ func NewVendor(v *coredata.Vendor) *Vendor { } } -func NewListVendorsOutput(vendorPage *page.Page[*coredata.Vendor, coredata.VendorOrderField]) ListVendorsOutput { - vendors := make([]*Vendor, 0, len(vendorPage.Data)) - for _, v := range vendorPage.Data { - vendors = append(vendors, NewVendor(v)) +func NewListThirdPartiesOutput(thirdPartyPage *page.Page[*coredata.ThirdParty, coredata.ThirdPartyOrderField]) ListThirdPartiesOutput { + thirdParties := make([]*ThirdParty, 0, len(thirdPartyPage.Data)) + for _, v := range thirdPartyPage.Data { + thirdParties = append(thirdParties, NewThirdParty(v)) } var nextCursor *page.CursorKey - if len(vendorPage.Data) > 0 { - cursorKey := vendorPage.Data[len(vendorPage.Data)-1].CursorKey(vendorPage.Cursor.OrderBy.Field) + if len(thirdPartyPage.Data) > 0 { + cursorKey := thirdPartyPage.Data[len(thirdPartyPage.Data)-1].CursorKey(thirdPartyPage.Cursor.OrderBy.Field) nextCursor = &cursorKey } - return ListVendorsOutput{ + return ListThirdPartiesOutput{ NextCursor: nextCursor, - Vendors: vendors, + ThirdParties: thirdParties, } } ``` diff --git a/contrib/claude/react-components.md b/contrib/claude/react-components.md index 614395c8a..1acf0803a 100644 --- a/contrib/claude/react-components.md +++ b/contrib/claude/react-components.md @@ -79,10 +79,10 @@ export function UserCard({ name }: UserCardProps) { ```tsx // Good — destructure in body when parameter-level destructuring would exceed the line-length limit -export function VendorComplianceOverviewPanel( - props: VendorComplianceOverviewPanelProps, +export function ThirdPartyComplianceOverviewPanel( + props: ThirdPartyComplianceOverviewPanelProps, ) { - const { className, vendorKey, onStatusChange } = props; + const { className, thirdPartyKey, onStatusChange } = props; // … } ``` @@ -139,11 +139,11 @@ export function Thing({ label }: ThingProps) { ```tsx // Good — rare exception: route entry default export (names still clear in module) -type VendorsPageProps = { - queryRef: PreloadedQuery; +type ThirdPartiesPageProps = { + queryRef: PreloadedQuery; }; -export default function VendorsPage({ queryRef }: VendorsPageProps) { +export default function ThirdPartiesPage({ queryRef }: ThirdPartiesPageProps) { // … } ``` @@ -204,7 +204,7 @@ Use props for: ### Hooks for data and URL-derived identity - **Fetched data:** Colocate Relay fragments and queries per [`contrib/claude/relay.md`](relay.md) (`useFragment`, `useLazyLoadQuery`, `usePreloadedQuery`, etc.) in the component that needs the data. -- **Route parameters:** Call `useParams()` (or a small `useOrganizationId()`-style hook) **inside** the component that needs the id — avoid drilling `organizationId` / `vendorId` from a parent that only read the URL to pass them down. +- **Route parameters:** Call `useParams()` (or a small `useOrganizationId()`-style hook) **inside** the component that needs the id — avoid drilling `organizationId` / `thirdPartyId` from a parent that only read the URL to pass them down. ### Relay: framework wiring is not “business data props” @@ -214,36 +214,36 @@ Relay sometimes requires **opaque handles** on props: e.g. **`queryRef`** for `u ```tsx // Bad — parent only needed the param to pass it down -function VendorLayout() { - const { vendorId } = useParams(); +function ThirdPartyLayout() { + const { thirdPartyId } = useParams(); return (
- +
); } -function VendorSummary({ vendorId }: { vendorId: string }) { +function ThirdPartySummary({ thirdPartyId }: { thirdPartyId: string }) { return
{/* … */}
; } ``` ```tsx // Good — component that needs the id reads it (or uses a dedicated hook) -function VendorLayout() { +function ThirdPartyLayout() { return (
- +
); } -function VendorSummary() { - const { vendorId } = useParams(); - if (vendorId == null) { +function ThirdPartySummary() { + const { thirdPartyId } = useParams(); + if (thirdPartyId == null) { return null; } - return
{/* use vendorId in a hook / query … */}
; + return
{/* use thirdPartyId in a hook / query … */}
; } ``` @@ -251,13 +251,13 @@ function VendorSummary() { ```tsx // Bad — parent loaded data and passes fields as props -function VendorPage() { - const vendor = useLazyLoadQuery(/* … */); +function ThirdPartyPage() { + const thirdParty = useLazyLoadQuery(/* … */); return ( - ); } @@ -265,24 +265,24 @@ function VendorPage() { ```tsx // Good — header colocates its fragment and reads via useFragment -const vendorHeaderFragment = graphql` - fragment VendorHeader_vendor on Vendor { +const thirdPartyHeaderFragment = graphql` + fragment ThirdPartyHeader_thirdParty on ThirdParty { name riskScore updatedAt } `; -interface VendorHeaderProps { +interface ThirdPartyHeaderProps { className?: string; - vendorKey: VendorHeader_vendor$key; + thirdPartyKey: ThirdPartyHeader_thirdParty$key; } -export function VendorHeader({ className, vendorKey }: VendorHeaderProps) { - const vendor = useFragment(vendorHeaderFragment, vendorKey); +export function ThirdPartyHeader({ className, thirdPartyKey }: ThirdPartyHeaderProps) { + const thirdParty = useFragment(thirdPartyHeaderFragment, thirdPartyKey); return (
- {/* render from vendor … */} + {/* render from thirdParty … */}
); } diff --git a/contrib/claude/relay.md b/contrib/claude/relay.md index e5dae5997..fd0f9a6db 100644 --- a/contrib/claude/relay.md +++ b/contrib/claude/relay.md @@ -191,7 +191,7 @@ Fragments colocate data requirements with the component that reads them: ```tsx const contactFragment = graphql` - fragment ContactRow_contactFragment on VendorContact { + fragment ContactRow_contactFragment on ThirdPartyContact { id fullName email @@ -199,8 +199,8 @@ const contactFragment = graphql` role createdAt updatedAt - canUpdate: permission(action: "core:vendor-contact:update") - canDelete: permission(action: "core:vendor-contact:delete") + canUpdate: permission(action: "core:thirdParty-contact:update") + canDelete: permission(action: "core:thirdParty-contact:delete") } `; @@ -215,12 +215,12 @@ function ContactRow(props: { contactKey: ContactRow_contactFragment$key }) { For lists that support sorting and pagination, use `@refetchable` with `@argumentDefinitions`: ```tsx -const vendorContactsFragment = graphql` - fragment VendorContactsTabFragment on Vendor - @refetchable(queryName: "VendorContactsListQuery") +const thirdPartyContactsFragment = graphql` + fragment ThirdPartyContactsTabFragment on ThirdParty + @refetchable(queryName: "ThirdPartyContactsListQuery") @argumentDefinitions( first: { type: "Int", defaultValue: 50 } - order: { type: "VendorContactOrder", defaultValue: null } + order: { type: "ThirdPartyContactOrder", defaultValue: null } after: { type: "CursorKey", defaultValue: null } before: { type: "CursorKey", defaultValue: null } last: { type: "Int", defaultValue: null } @@ -231,18 +231,18 @@ const vendorContactsFragment = graphql` last: $last before: $before orderBy: $order - ) @connection(key: "VendorContactsTabFragment_contacts") { + ) @connection(key: "ThirdPartyContactsTabFragment_contacts") { __id edges { node { - ...VendorContactsTabFragment_contact + ...ThirdPartyContactsTabFragment_contact } } } } `; -const [data, refetch] = useRefetchableFragment(vendorContactsFragment, vendor); +const [data, refetch] = useRefetchableFragment(thirdPartyContactsFragment, thirdParty); const connectionId = data.contacts.__id; ``` @@ -251,9 +251,9 @@ const connectionId = data.contacts.__id; Use `usePaginationFragment` for cursor-based Relay pagination: ```tsx -const pagination = usePaginationFragment(paginatedVendorsFragment, data.node); -const vendors = pagination.data.vendors?.edges.map(edge => edge.node); -const connectionId = pagination.data.vendors.__id; +const pagination = usePaginationFragment(paginatedThirdPartiesFragment, data.node); +const thirdParties = pagination.data.thirdParties?.edges.map(edge => edge.node); +const connectionId = pagination.data.thirdParties.__id; ``` The `@connection(key: "...", filters: [...])` directive on the fragment tells Relay how to manage the paginated list in the store. The `filters` array controls which variables affect the connection identity. @@ -294,7 +294,7 @@ createCookieBanner({ variables: { ... } }); #### Examples ```tsx -const [deleteVendor] = useMutation(deleteVendorMutation); +const [deleteThirdParty] = useMutation(deleteThirdPartyMutation); ``` For mutations with user feedback, combine with `useToast` and use `onCompleted`/`onError` callbacks: @@ -415,9 +415,9 @@ This is useful for dialogs, drawers, or other components rendered outside the su ```tsx // Add new edge to a connection const createMutation = graphql` - mutation CreateVendorMutation($input: CreateVendorInput!, $connections: [ID!]!) { - createVendor(input: $input) { - vendorEdge @prependEdge(connections: $connections) { + mutation CreateThirdPartyMutation($input: CreateThirdPartyInput!, $connections: [ID!]!) { + createThirdParty(input: $input) { + thirdPartyEdge @prependEdge(connections: $connections) { node { id name @@ -429,19 +429,19 @@ const createMutation = graphql` // Remove an edge from a connection const deleteMutation = graphql` - mutation DeleteVendorMutation($input: DeleteVendorInput!, $connections: [ID!]!) { - deleteVendor(input: $input) { - deletedVendorId @deleteEdge(connections: $connections) + mutation DeleteThirdPartyMutation($input: DeleteThirdPartyInput!, $connections: [ID!]!) { + deleteThirdParty(input: $input) { + deletedThirdPartyId @deleteEdge(connections: $connections) } } `; // Update in-place (Relay matches by id — no directive needed) const updateMutation = graphql` - mutation UpdateContactMutation($input: UpdateVendorContactInput!) { - updateVendorContact(input: $input) { - vendorContact { - ...VendorContactsTabFragment_contact + mutation UpdateContactMutation($input: UpdateThirdPartyContactInput!) { + updateThirdPartyContact(input: $input) { + thirdPartyContact { + ...ThirdPartyContactsTabFragment_contact } } } @@ -505,15 +505,15 @@ Destructive mutations (delete) are wrapped with a confirmation dialog: ```tsx const confirm = useConfirm(); -const [deleteVendor] = useMutation(deleteVendorMutation); +const [deleteThirdParty] = useMutation(deleteThirdPartyMutation); return () => { confirm( () => new Promise((resolve) => { - deleteVendor({ + deleteThirdParty({ variables: { - input: { vendorId: vendor.id! }, + input: { thirdPartyId: thirdParty.id! }, connections: [connectionId], }, onCompleted() { @@ -534,14 +534,14 @@ return () => { GraphQL operations are colocated with the components that use them. See [`contrib/claude/app-arborescence.md`](app-arborescence.md) for the full folder layout. ``` -pages/organizations/vendors/ - VendorsPage.tsx # query + pagination fragment +pages/organizations/third-parties/ + ThirdPartiesPage.tsx # query + pagination fragment _components/ CreateContactDialog.tsx # create mutation EditContactDialog.tsx # update mutation tabs/ - VendorContactsTab.tsx # refetchable fragment + item fragment - VendorComplianceTab.tsx + ThirdPartyContactsTab.tsx # refetchable fragment + item fragment + ThirdPartyComplianceTab.tsx ``` Component-specific operations (queries, fragments, mutations) are defined inline in the component file that uses them. Shared sub-components live in `_components/` next to the page (scoped to the nearest common ancestor). \ No newline at end of file diff --git a/contrib/claude/validation.md b/contrib/claude/validation.md index c6d2b7aed..26df18904 100644 --- a/contrib/claude/validation.md +++ b/contrib/claude/validation.md @@ -7,7 +7,7 @@ Custom fluent validation API in `pkg/validator/`. Used in every service method t Create a validator, chain `Check()` calls for each field, then call `Error()` to get accumulated errors: ```go -func (req *CreateVendorRequest) Validate() error { +func (req *CreateThirdPartyRequest) Validate() error { v := validator.New() v.Check(req.OrganizationID, "organization_id", @@ -19,7 +19,7 @@ func (req *CreateVendorRequest) Validate() error { validator.SafeTextNoNewLine(TitleMaxLength), ) v.Check(req.Category, "category", - validator.OneOfSlice(coredata.VendorCategories()), + validator.OneOfSlice(coredata.ThirdPartyCategories()), ) return v.Error() @@ -81,7 +81,7 @@ v.CheckEach(ids, "ids", func(index int, item any) { gidValue := item.(gid.GID) v.Check(gidValue, fmt.Sprintf("ids[%d]", index), validator.Required(), - validator.GID(coredata.VendorEntityType), + validator.GID(coredata.ThirdPartyEntityType), ) }) ``` @@ -135,7 +135,7 @@ Validation errors flow naturally through Go's error interface: 3. GraphQL/HTTP handlers convert `ValidationErrors` to appropriate response format ```go -func (s *Service) CreateVendor(ctx context.Context, req CreateVendorRequest) (*coredata.Vendor, error) { +func (s *Service) CreateThirdParty(ctx context.Context, req CreateThirdPartyRequest) (*coredata.ThirdParty, error) { if err := req.Validate(); err != nil { return nil, err } diff --git a/contrib/seed.sh b/contrib/seed.sh index 7997a38a7..4c30ce70c 100755 --- a/contrib/seed.sh +++ b/contrib/seed.sh @@ -480,7 +480,7 @@ create_risk \ SECURITY MITIGATED 1 5 create_risk \ - "Third-party SaaS vendor data breach" \ + "Third-party SaaS data breach" \ OPERATIONAL TRANSFERRED 3 4 create_risk \ "Cloud region outage causing service disruption" \ @@ -514,7 +514,7 @@ create_risk \ "Breach notification deadline missed" \ COMPLIANCE MITIGATED 1 5 create_risk \ - "Inadequate data processing agreements with vendors" \ + "Inadequate data processing agreements with third parties" \ COMPLIANCE MITIGATED 3 3 create_risk \ "Employee data retained beyond legal period" \ @@ -553,17 +553,17 @@ create_risk \ echo " 35 risks created" -echo " Creating vendors..." +echo " Creating third parties..." -create_vendor() { +create_third_party() { local name="$1" local description="$2" local resp - resp=$(prb_api "createVendor: $name" ' - mutation($input: CreateVendorInput!) { - createVendor(input: $input) { - vendorEdge { + resp=$(prb_api "createThirdParty: $name" ' + mutation($input: CreateThirdPartyInput!) { + createThirdParty(input: $input) { + thirdPartyEdge { node { id } } } @@ -574,55 +574,55 @@ create_vendor() { description="$description" \ )") local id - id=$(echo "$resp" | jq -r '.data.createVendor.vendorEdge.node.id // empty') + id=$(echo "$resp" | jq -r '.data.createThirdParty.thirdPartyEdge.node.id // empty') if [ -z "$id" ]; then - echo "ERROR (createVendor: $name): no vendor id in response" >&2 + echo "ERROR (createThirdParty: $name): no third party id in response" >&2 exit 1 fi } -create_vendor "Amazon Web Services" \ +create_third_party "Amazon Web Services" \ "Cloud infrastructure and compute" -create_vendor "Google Cloud Platform" \ +create_third_party "Google Cloud Platform" \ "BigQuery analytics and AI services" -create_vendor "Google Workspace" \ +create_third_party "Google Workspace" \ "Email, calendar, and productivity suite" -create_vendor "Microsoft 365" \ +create_third_party "Microsoft 365" \ "Office productivity and collaboration" -create_vendor "Datadog" \ +create_third_party "Datadog" \ "Application monitoring and observability" -create_vendor "PagerDuty" \ +create_third_party "PagerDuty" \ "Incident management and on-call scheduling" -create_vendor "Slack" \ +create_third_party "Slack" \ "Team communication and messaging" -create_vendor "GitHub" \ +create_third_party "GitHub" \ "Source code management and CI/CD" -create_vendor "Stripe" \ +create_third_party "Stripe" \ "Payment processing and billing" -create_vendor "Salesforce" \ +create_third_party "Salesforce" \ "Customer relationship management" -create_vendor "HubSpot" \ +create_third_party "HubSpot" \ "Marketing automation and CRM" -create_vendor "Notion" \ +create_third_party "Notion" \ "Documentation and knowledge management" -create_vendor "1Password" \ +create_third_party "1Password" \ "Enterprise password management" -create_vendor "Okta" \ +create_third_party "Okta" \ "Identity and access management" -create_vendor "CrowdStrike" \ +create_third_party "CrowdStrike" \ "Endpoint protection and threat intelligence" -create_vendor "Vanta" \ +create_third_party "Vanta" \ "Compliance automation and monitoring" -create_vendor "Jira" \ +create_third_party "Jira" \ "Project management and issue tracking" -create_vendor "Cloudflare" \ +create_third_party "Cloudflare" \ "CDN, DNS, and DDoS protection" -create_vendor "Twilio SendGrid" \ +create_third_party "Twilio SendGrid" \ "Transactional email delivery" -create_vendor "Snowflake" \ +create_third_party "Snowflake" \ "Cloud data warehouse" -echo " 20 vendors created" +echo " 20 third parties created" echo " Creating measures..." @@ -696,7 +696,7 @@ echo "" echo " Created:" echo " 3 frameworks, 43 controls" echo " 35 risks" -echo " 20 vendors" +echo " 20 third parties" echo " 15 measures" echo " 8 people" echo "" diff --git a/e2e/console/audit_log_test.go b/e2e/console/audit_log_test.go index 71c5d71fc..7e2ffc85b 100644 --- a/e2e/console/audit_log_test.go +++ b/e2e/console/audit_log_test.go @@ -27,8 +27,8 @@ func TestAuditLog_List(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - // Create a vendor to generate an audit log entry. - factory.NewVendor(owner).WithName(factory.SafeName("AuditVendor")).Create() + // Create a thirdParty to generate an audit log entry. + factory.NewThirdParty(owner).WithName(factory.SafeName("AuditThirdParty")).Create() const query = ` query($orgId: ID!) { @@ -78,20 +78,20 @@ func TestAuditLog_List(t *testing.T) { require.NoError(t, err) assert.GreaterOrEqual(t, result.Node.AuditLogEntries.TotalCount, 1) - // Find the vendor create entry. + // Find the thirdParty create entry. found := false for _, edge := range result.Node.AuditLogEntries.Edges { - if edge.Node.Action == "core:vendor:create" { + if edge.Node.Action == "core:thirdParty:create" { found = true assert.Equal(t, "USER", edge.Node.ActorType) - assert.Equal(t, "Vendor", edge.Node.ResourceType) + assert.Equal(t, "ThirdParty", edge.Node.ResourceType) assert.NotEmpty(t, edge.Node.ActorID) assert.NotEmpty(t, edge.Node.ResourceID) assert.NotEmpty(t, edge.Node.CreatedAt) break } } - assert.True(t, found, "expected to find core:vendor:create audit log entry") + assert.True(t, found, "expected to find core:thirdParty:create audit log entry") } func TestAuditLog_Filter(t *testing.T) { @@ -99,7 +99,7 @@ func TestAuditLog_Filter(t *testing.T) { owner := testutil.NewClient(t, testutil.RoleOwner) // Create different resources to generate different audit log entries. - factory.NewVendor(owner).WithName(factory.SafeName("FilterVendor")).Create() + factory.NewThirdParty(owner).WithName(factory.SafeName("FilterThirdParty")).Create() const query = ` query($orgId: ID!, $filter: AuditLogEntryFilter) { @@ -139,12 +139,12 @@ func TestAuditLog_Filter(t *testing.T) { err := owner.Execute(query, map[string]any{ "orgId": owner.GetOrganizationID().String(), - "filter": map[string]any{"action": "core:vendor:create"}, + "filter": map[string]any{"action": "core:thirdParty:create"}, }, &result) require.NoError(t, err) assert.GreaterOrEqual(t, result.Node.AuditLogEntries.TotalCount, 1) for _, edge := range result.Node.AuditLogEntries.Edges { - assert.Equal(t, "core:vendor:create", edge.Node.Action) + assert.Equal(t, "core:thirdParty:create", edge.Node.Action) } }) @@ -167,12 +167,12 @@ func TestAuditLog_Filter(t *testing.T) { err := owner.Execute(query, map[string]any{ "orgId": owner.GetOrganizationID().String(), - "filter": map[string]any{"resourceType": "Vendor"}, + "filter": map[string]any{"resourceType": "ThirdParty"}, }, &result) require.NoError(t, err) assert.GreaterOrEqual(t, result.Node.AuditLogEntries.TotalCount, 1) for _, edge := range result.Node.AuditLogEntries.Edges { - assert.Equal(t, "Vendor", edge.Node.ResourceType) + assert.Equal(t, "ThirdParty", edge.Node.ResourceType) } }) } @@ -182,7 +182,7 @@ func TestAuditLog_RBAC(t *testing.T) { owner := testutil.NewClient(t, testutil.RoleOwner) // Generate an audit log entry. - factory.NewVendor(owner).WithName(factory.SafeName("RBACVendor")).Create() + factory.NewThirdParty(owner).WithName(factory.SafeName("RBACThirdParty")).Create() const query = ` query($orgId: ID!) { @@ -253,8 +253,8 @@ func TestAuditLog_TenantIsolation(t *testing.T) { org1Owner := testutil.NewClient(t, testutil.RoleOwner) org2Owner := testutil.NewClient(t, testutil.RoleOwner) - // Create a vendor in org1 to generate audit log entries. - factory.NewVendor(org1Owner).WithName(factory.SafeName("IsoVendor")).Create() + // Create a thirdParty in org1 to generate audit log entries. + factory.NewThirdParty(org1Owner).WithName(factory.SafeName("IsoThirdParty")).Create() const query = ` query($orgId: ID!) { @@ -275,7 +275,7 @@ func TestAuditLog_TenantIsolation(t *testing.T) { } ` - // org2 should not see org1's audit log entries about vendors. + // org2 should not see org1's audit log entries about thirdParties. var result struct { Node struct { AuditLogEntries struct { @@ -298,9 +298,9 @@ func TestAuditLog_TenantIsolation(t *testing.T) { for _, edge := range result.Node.AuditLogEntries.Edges { // org2 may have its own audit log entries (from user/org creation), - // but should never see org1's vendor entries. - if edge.Node.ResourceType == "Vendor" { - t.Fatalf("org2 should not see org1's vendor audit log entries, but found: %s", edge.Node.Action) + // but should never see org1's thirdParty entries. + if edge.Node.ResourceType == "ThirdParty" { + t.Fatalf("org2 should not see org1's thirdParty audit log entries, but found: %s", edge.Node.Action) } } } diff --git a/e2e/console/rbac_test.go b/e2e/console/rbac_test.go index 8a64602a9..eecef3c3b 100644 --- a/e2e/console/rbac_test.go +++ b/e2e/console/rbac_test.go @@ -174,32 +174,32 @@ const ( } }` - createVendorMutation = ` - mutation CreateVendor($input: CreateVendorInput!) { - createVendor(input: $input) { - vendorEdge { node { id } } + createThirdPartyMutation = ` + mutation CreateThirdParty($input: CreateThirdPartyInput!) { + createThirdParty(input: $input) { + thirdPartyEdge { node { id } } } }` - updateVendorMutation = ` - mutation UpdateVendor($input: UpdateVendorInput!) { - updateVendor(input: $input) { - vendor { id } + updateThirdPartyMutation = ` + mutation UpdateThirdParty($input: UpdateThirdPartyInput!) { + updateThirdParty(input: $input) { + thirdParty { id } } }` - deleteVendorMutation = ` - mutation DeleteVendor($input: DeleteVendorInput!) { - deleteVendor(input: $input) { - deletedVendorId + deleteThirdPartyMutation = ` + mutation DeleteThirdParty($input: DeleteThirdPartyInput!) { + deleteThirdParty(input: $input) { + deletedThirdPartyId } }` - listVendorsQuery = ` - query GetVendors($id: ID!) { + listThirdPartiesQuery = ` + query GetThirdParties($id: ID!) { node(id: $id) { ... on Organization { - vendors(first: 10) { totalCount } + thirdParties(first: 10) { totalCount } } } }` @@ -304,7 +304,7 @@ func TestRBAC(t *testing.T) { measureID := factory.NewMeasure(owner).WithName("RBAC Test Measure").Create() taskID := factory.NewTask(owner, measureID).WithName("RBAC Test Task").Create() riskID := factory.NewRisk(owner).WithName("RBAC Test Risk").Create() - vendorID := factory.NewVendor(owner).WithName("RBAC Test Vendor").Create() + thirdPartyID := factory.NewThirdParty(owner).WithName("RBAC Test ThirdParty").Create() accessSourceID := factory.NewAccessSource(owner, owner.GetOrganizationID().String()).WithName("RBAC Test Source").Create() accessReviewCampaignID := factory.NewAccessReviewCampaign(owner, owner.GetOrganizationID().String()).WithName("RBAC Test Campaign").Create() @@ -936,123 +936,123 @@ func TestRBAC(t *testing.T) { shouldAllow: true, }, { - name: "owner can create vendor", + name: "owner can create thirdParty", role: "owner", client: owner, - query: createVendorMutation, + query: createThirdPartyMutation, variables: func() map[string]any { - return map[string]any{"input": map[string]any{"organizationId": owner.GetOrganizationID().String(), "name": factory.SafeName("Vendor")}} + return map[string]any{"input": map[string]any{"organizationId": owner.GetOrganizationID().String(), "name": factory.SafeName("ThirdParty")}} }, shouldAllow: true, }, { - name: "admin can create vendor", + name: "admin can create thirdParty", role: "admin", client: admin, - query: createVendorMutation, + query: createThirdPartyMutation, variables: func() map[string]any { - return map[string]any{"input": map[string]any{"organizationId": owner.GetOrganizationID().String(), "name": factory.SafeName("Vendor")}} + return map[string]any{"input": map[string]any{"organizationId": owner.GetOrganizationID().String(), "name": factory.SafeName("ThirdParty")}} }, shouldAllow: true, }, { - name: "viewer cannot create vendor", + name: "viewer cannot create thirdParty", role: "viewer", client: viewer, - query: createVendorMutation, + query: createThirdPartyMutation, variables: func() map[string]any { - return map[string]any{"input": map[string]any{"organizationId": owner.GetOrganizationID().String(), "name": factory.SafeName("Vendor")}} + return map[string]any{"input": map[string]any{"organizationId": owner.GetOrganizationID().String(), "name": factory.SafeName("ThirdParty")}} }, shouldAllow: false, }, { - name: "owner can update vendor", + name: "owner can update thirdParty", role: "owner", client: owner, - query: updateVendorMutation, + query: updateThirdPartyMutation, variables: func() map[string]any { - return map[string]any{"input": map[string]any{"id": vendorID, "name": factory.SafeName("Updated Vendor")}} + return map[string]any{"input": map[string]any{"id": thirdPartyID, "name": factory.SafeName("Updated ThirdParty")}} }, shouldAllow: true, }, { - name: "admin can update vendor", + name: "admin can update thirdParty", role: "admin", client: admin, - query: updateVendorMutation, + query: updateThirdPartyMutation, variables: func() map[string]any { - return map[string]any{"input": map[string]any{"id": vendorID, "name": factory.SafeName("Updated Vendor")}} + return map[string]any{"input": map[string]any{"id": thirdPartyID, "name": factory.SafeName("Updated ThirdParty")}} }, shouldAllow: true, }, { - name: "viewer cannot update vendor", + name: "viewer cannot update thirdParty", role: "viewer", client: viewer, - query: updateVendorMutation, + query: updateThirdPartyMutation, variables: func() map[string]any { - return map[string]any{"input": map[string]any{"id": vendorID, "name": factory.SafeName("Updated Vendor")}} + return map[string]any{"input": map[string]any{"id": thirdPartyID, "name": factory.SafeName("Updated ThirdParty")}} }, shouldAllow: false, }, { - name: "owner can delete vendor", + name: "owner can delete thirdParty", role: "owner", client: owner, - query: deleteVendorMutation, + query: deleteThirdPartyMutation, variables: func() map[string]any { - id := factory.NewVendor(owner).WithName(factory.SafeName("ToDelete")).Create() - return map[string]any{"input": map[string]any{"vendorId": id}} + id := factory.NewThirdParty(owner).WithName(factory.SafeName("ToDelete")).Create() + return map[string]any{"input": map[string]any{"thirdPartyId": id}} }, shouldAllow: true, }, { - name: "admin can delete vendor", + name: "admin can delete thirdParty", role: "admin", client: admin, - query: deleteVendorMutation, + query: deleteThirdPartyMutation, variables: func() map[string]any { - id := factory.NewVendor(owner).WithName(factory.SafeName("ToDelete")).Create() - return map[string]any{"input": map[string]any{"vendorId": id}} + id := factory.NewThirdParty(owner).WithName(factory.SafeName("ToDelete")).Create() + return map[string]any{"input": map[string]any{"thirdPartyId": id}} }, shouldAllow: true, }, { - name: "viewer cannot delete vendor", + name: "viewer cannot delete thirdParty", role: "viewer", client: viewer, - query: deleteVendorMutation, + query: deleteThirdPartyMutation, variables: func() map[string]any { - id := factory.NewVendor(owner).WithName(factory.SafeName("ToDelete")).Create() - return map[string]any{"input": map[string]any{"vendorId": id}} + id := factory.NewThirdParty(owner).WithName(factory.SafeName("ToDelete")).Create() + return map[string]any{"input": map[string]any{"thirdPartyId": id}} }, shouldAllow: false, }, { - name: "owner can list vendors", + name: "owner can list third parties", role: "owner", client: owner, - query: listVendorsQuery, + query: listThirdPartiesQuery, variables: func() map[string]any { return map[string]any{"id": owner.GetOrganizationID().String()} }, shouldAllow: true, }, { - name: "admin can list vendors", + name: "admin can list third parties", role: "admin", client: admin, - query: listVendorsQuery, + query: listThirdPartiesQuery, variables: func() map[string]any { return map[string]any{"id": owner.GetOrganizationID().String()} }, shouldAllow: true, }, { - name: "viewer can list vendors", + name: "viewer can list third parties", role: "viewer", client: viewer, - query: listVendorsQuery, + query: listThirdPartiesQuery, variables: func() map[string]any { return map[string]any{"id": owner.GetOrganizationID().String()} }, diff --git a/e2e/console/vendor_compliance_report_test.go b/e2e/console/third_party_compliance_report_test.go similarity index 59% rename from e2e/console/vendor_compliance_report_test.go rename to e2e/console/third_party_compliance_report_test.go index 5721e01c1..de0221f6d 100644 --- a/e2e/console/vendor_compliance_report_test.go +++ b/e2e/console/third_party_compliance_report_test.go @@ -23,15 +23,15 @@ import ( "go.probo.inc/probo/e2e/internal/testutil" ) -func TestVendorComplianceReport_Upload(t *testing.T) { +func TestThirdPartyComplianceReport_Upload(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - vendorID := factory.NewVendor(owner).WithName("Compliance Report Upload Vendor").Create() + thirdPartyID := factory.NewThirdParty(owner).WithName("Compliance Report Upload ThirdParty").Create() const query = ` - mutation UploadVendorComplianceReport($input: UploadVendorComplianceReportInput!) { - uploadVendorComplianceReport(input: $input) { - vendorComplianceReportEdge { + mutation UploadThirdPartyComplianceReport($input: UploadThirdPartyComplianceReportInput!) { + uploadThirdPartyComplianceReport(input: $input) { + thirdPartyComplianceReportEdge { node { id reportName @@ -46,27 +46,27 @@ func TestVendorComplianceReport_Upload(t *testing.T) { pdfContent := []byte("%PDF-1.4\n1 0 obj\n<< /Type /Catalog >>\nendobj\ntrailer\n<< /Root 1 0 R >>\n%%EOF") var result struct { - UploadVendorComplianceReport struct { - VendorComplianceReportEdge struct { + UploadThirdPartyComplianceReport struct { + ThirdPartyComplianceReportEdge struct { Node struct { ID string `json:"id"` ReportName string `json:"reportName"` ReportDate string `json:"reportDate"` ValidUntil *string `json:"validUntil"` } `json:"node"` - } `json:"vendorComplianceReportEdge"` - } `json:"uploadVendorComplianceReport"` + } `json:"thirdPartyComplianceReportEdge"` + } `json:"uploadThirdPartyComplianceReport"` } err := owner.ExecuteWithFile( query, map[string]any{ "input": map[string]any{ - "vendorId": vendorID, - "reportName": "SOC 2 Type II", - "reportDate": "2024-01-01T00:00:00Z", - "validUntil": "2025-01-01T00:00:00Z", - "file": nil, + "thirdPartyId": thirdPartyID, + "reportName": "SOC 2 Type II", + "reportDate": "2024-01-01T00:00:00Z", + "validUntil": "2025-01-01T00:00:00Z", + "file": nil, }, }, "input.file", testutil.UploadFile{ Filename: "soc2-report.pdf", @@ -77,23 +77,23 @@ func TestVendorComplianceReport_Upload(t *testing.T) { ) require.NoError(t, err) - node := result.UploadVendorComplianceReport.VendorComplianceReportEdge.Node + node := result.UploadThirdPartyComplianceReport.ThirdPartyComplianceReportEdge.Node assert.NotEmpty(t, node.ID) assert.Equal(t, "SOC 2 Type II", node.ReportName) assert.NotEmpty(t, node.ReportDate) assert.NotNil(t, node.ValidUntil) } -func TestVendorComplianceReport_List(t *testing.T) { +func TestThirdPartyComplianceReport_List(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - vendorID := factory.NewVendor(owner).WithName("Compliance Report List Vendor").Create() + thirdPartyID := factory.NewThirdParty(owner).WithName("Compliance Report List ThirdParty").Create() pdfContent := []byte("%PDF-1.4\n1 0 obj\n<< /Type /Catalog >>\nendobj\ntrailer\n<< /Root 1 0 R >>\n%%EOF") uploadQuery := ` - mutation UploadVendorComplianceReport($input: UploadVendorComplianceReportInput!) { - uploadVendorComplianceReport(input: $input) { - vendorComplianceReportEdge { + mutation UploadThirdPartyComplianceReport($input: UploadThirdPartyComplianceReportInput!) { + uploadThirdPartyComplianceReport(input: $input) { + thirdPartyComplianceReportEdge { node { id } } } @@ -101,23 +101,23 @@ func TestVendorComplianceReport_List(t *testing.T) { ` var uploadResult struct { - UploadVendorComplianceReport struct { - VendorComplianceReportEdge struct { + UploadThirdPartyComplianceReport struct { + ThirdPartyComplianceReportEdge struct { Node struct { ID string `json:"id"` } `json:"node"` - } `json:"vendorComplianceReportEdge"` - } `json:"uploadVendorComplianceReport"` + } `json:"thirdPartyComplianceReportEdge"` + } `json:"uploadThirdPartyComplianceReport"` } err := owner.ExecuteWithFile( uploadQuery, map[string]any{ "input": map[string]any{ - "vendorId": vendorID, - "reportName": "ISO 27001", - "reportDate": "2024-06-01T00:00:00Z", - "file": nil, + "thirdPartyId": thirdPartyID, + "reportName": "ISO 27001", + "reportDate": "2024-06-01T00:00:00Z", + "file": nil, }, }, "input.file", testutil.UploadFile{ Filename: "iso27001.pdf", @@ -128,13 +128,13 @@ func TestVendorComplianceReport_List(t *testing.T) { ) require.NoError(t, err) - reportID := uploadResult.UploadVendorComplianceReport.VendorComplianceReportEdge.Node.ID + reportID := uploadResult.UploadThirdPartyComplianceReport.ThirdPartyComplianceReportEdge.Node.ID require.NotEmpty(t, reportID) const listQuery = ` query($id: ID!) { node(id: $id) { - ... on Vendor { + ... on ThirdParty { id complianceReports(first: 10) { edges { @@ -165,7 +165,7 @@ func TestVendorComplianceReport_List(t *testing.T) { } `json:"node"` } - err = owner.Execute(listQuery, map[string]any{"id": vendorID}, &listResult) + err = owner.Execute(listQuery, map[string]any{"id": thirdPartyID}, &listResult) require.NoError(t, err) require.Len(t, listResult.Node.ComplianceReports.Edges, 1) @@ -173,17 +173,17 @@ func TestVendorComplianceReport_List(t *testing.T) { assert.Equal(t, "ISO 27001", listResult.Node.ComplianceReports.Edges[0].Node.ReportName) } -func TestVendorComplianceReport_Delete(t *testing.T) { +func TestThirdPartyComplianceReport_Delete(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - vendorID := factory.NewVendor(owner).WithName("Compliance Report Delete Vendor").Create() + thirdPartyID := factory.NewThirdParty(owner).WithName("Compliance Report Delete ThirdParty").Create() pdfContent := []byte("%PDF-1.4\n1 0 obj\n<< /Type /Catalog >>\nendobj\ntrailer\n<< /Root 1 0 R >>\n%%EOF") uploadQuery := ` - mutation UploadVendorComplianceReport($input: UploadVendorComplianceReportInput!) { - uploadVendorComplianceReport(input: $input) { - vendorComplianceReportEdge { + mutation UploadThirdPartyComplianceReport($input: UploadThirdPartyComplianceReportInput!) { + uploadThirdPartyComplianceReport(input: $input) { + thirdPartyComplianceReportEdge { node { id } } } @@ -191,21 +191,21 @@ func TestVendorComplianceReport_Delete(t *testing.T) { ` var uploadResult struct { - UploadVendorComplianceReport struct { - VendorComplianceReportEdge struct { + UploadThirdPartyComplianceReport struct { + ThirdPartyComplianceReportEdge struct { Node struct { ID string `json:"id"` } `json:"node"` - } `json:"vendorComplianceReportEdge"` - } `json:"uploadVendorComplianceReport"` + } `json:"thirdPartyComplianceReportEdge"` + } `json:"uploadThirdPartyComplianceReport"` } err := owner.ExecuteWithFile(uploadQuery, map[string]any{ "input": map[string]any{ - "vendorId": vendorID, - "reportName": "PCI DSS", - "reportDate": "2024-03-01T00:00:00Z", - "file": nil, + "thirdPartyId": thirdPartyID, + "reportName": "PCI DSS", + "reportDate": "2024-03-01T00:00:00Z", + "file": nil, }, }, "input.file", testutil.UploadFile{ Filename: "pci-dss.pdf", @@ -214,21 +214,21 @@ func TestVendorComplianceReport_Delete(t *testing.T) { }, &uploadResult) require.NoError(t, err) - reportID := uploadResult.UploadVendorComplianceReport.VendorComplianceReportEdge.Node.ID + reportID := uploadResult.UploadThirdPartyComplianceReport.ThirdPartyComplianceReportEdge.Node.ID require.NotEmpty(t, reportID) const deleteQuery = ` - mutation DeleteVendorComplianceReport($input: DeleteVendorComplianceReportInput!) { - deleteVendorComplianceReport(input: $input) { - deletedVendorComplianceReportId + mutation DeleteThirdPartyComplianceReport($input: DeleteThirdPartyComplianceReportInput!) { + deleteThirdPartyComplianceReport(input: $input) { + deletedThirdPartyComplianceReportId } } ` var deleteResult struct { - DeleteVendorComplianceReport struct { - DeletedVendorComplianceReportID string `json:"deletedVendorComplianceReportId"` - } `json:"deleteVendorComplianceReport"` + DeleteThirdPartyComplianceReport struct { + DeletedThirdPartyComplianceReportID string `json:"deletedThirdPartyComplianceReportId"` + } `json:"deleteThirdPartyComplianceReport"` } err = owner.Execute( @@ -241,5 +241,5 @@ func TestVendorComplianceReport_Delete(t *testing.T) { &deleteResult, ) require.NoError(t, err) - assert.Equal(t, reportID, deleteResult.DeleteVendorComplianceReport.DeletedVendorComplianceReportID) + assert.Equal(t, reportID, deleteResult.DeleteThirdPartyComplianceReport.DeletedThirdPartyComplianceReportID) } diff --git a/e2e/console/vendor_contact_test.go b/e2e/console/third_party_contact_test.go similarity index 52% rename from e2e/console/vendor_contact_test.go rename to e2e/console/third_party_contact_test.go index fe1dec8ef..55927b5fb 100644 --- a/e2e/console/vendor_contact_test.go +++ b/e2e/console/third_party_contact_test.go @@ -25,17 +25,17 @@ import ( "go.probo.inc/probo/e2e/internal/testutil" ) -func TestVendorContact_Create(t *testing.T) { +func TestThirdPartyContact_Create(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - // Create a vendor first - vendorID := factory.NewVendor(owner).WithName("Contact Test Vendor").Create() + // Create a thirdParty first + thirdPartyID := factory.NewThirdParty(owner).WithName("Contact Test ThirdParty").Create() query := ` - mutation CreateVendorContact($input: CreateVendorContactInput!) { - createVendorContact(input: $input) { - vendorContactEdge { + mutation CreateThirdPartyContact($input: CreateThirdPartyContactInput!) { + createThirdPartyContact(input: $input) { + thirdPartyContactEdge { node { id fullName @@ -49,8 +49,8 @@ func TestVendorContact_Create(t *testing.T) { ` var result struct { - CreateVendorContact struct { - VendorContactEdge struct { + CreateThirdPartyContact struct { + ThirdPartyContactEdge struct { Node struct { ID string `json:"id"` FullName string `json:"fullName"` @@ -58,39 +58,39 @@ func TestVendorContact_Create(t *testing.T) { Phone string `json:"phone"` Role string `json:"role"` } `json:"node"` - } `json:"vendorContactEdge"` - } `json:"createVendorContact"` + } `json:"thirdPartyContactEdge"` + } `json:"createThirdPartyContact"` } err := owner.Execute(query, map[string]any{ "input": map[string]any{ - "vendorId": vendorID, - "fullName": "John Doe", - "email": fmt.Sprintf("john.doe.%d@vendor.com", time.Now().UnixNano()), - "phone": "+1-555-123-4567", - "role": "Account Manager", + "thirdPartyId": thirdPartyID, + "fullName": "John Doe", + "email": fmt.Sprintf("john.doe.%d@thirdParty.com", time.Now().UnixNano()), + "phone": "+1-555-123-4567", + "role": "Account Manager", }, }, &result) require.NoError(t, err) - contact := result.CreateVendorContact.VendorContactEdge.Node + contact := result.CreateThirdPartyContact.ThirdPartyContactEdge.Node assert.NotEmpty(t, contact.ID) assert.Equal(t, "John Doe", contact.FullName) assert.Equal(t, "+1-555-123-4567", contact.Phone) assert.Equal(t, "Account Manager", contact.Role) } -func TestVendorContact_Update(t *testing.T) { +func TestThirdPartyContact_Update(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - // Create a vendor and contact - vendorID := factory.NewVendor(owner).WithName("Update Contact Vendor").Create() + // Create a thirdParty and contact + thirdPartyID := factory.NewThirdParty(owner).WithName("Update Contact ThirdParty").Create() createQuery := ` - mutation CreateVendorContact($input: CreateVendorContactInput!) { - createVendorContact(input: $input) { - vendorContactEdge { + mutation CreateThirdPartyContact($input: CreateThirdPartyContactInput!) { + createThirdPartyContact(input: $input) { + thirdPartyContactEdge { node { id } @@ -100,30 +100,30 @@ func TestVendorContact_Update(t *testing.T) { ` var createResult struct { - CreateVendorContact struct { - VendorContactEdge struct { + CreateThirdPartyContact struct { + ThirdPartyContactEdge struct { Node struct { ID string `json:"id"` } `json:"node"` - } `json:"vendorContactEdge"` - } `json:"createVendorContact"` + } `json:"thirdPartyContactEdge"` + } `json:"createThirdPartyContact"` } err := owner.Execute(createQuery, map[string]any{ "input": map[string]any{ - "vendorId": vendorID, - "fullName": "Initial Name", - "email": fmt.Sprintf("initial.%d@vendor.com", time.Now().UnixNano()), + "thirdPartyId": thirdPartyID, + "fullName": "Initial Name", + "email": fmt.Sprintf("initial.%d@thirdParty.com", time.Now().UnixNano()), }, }, &createResult) require.NoError(t, err) - contactID := createResult.CreateVendorContact.VendorContactEdge.Node.ID + contactID := createResult.CreateThirdPartyContact.ThirdPartyContactEdge.Node.ID query := ` - mutation UpdateVendorContact($input: UpdateVendorContactInput!) { - updateVendorContact(input: $input) { - vendorContact { + mutation UpdateThirdPartyContact($input: UpdateThirdPartyContactInput!) { + updateThirdPartyContact(input: $input) { + thirdPartyContact { id fullName phone @@ -134,14 +134,14 @@ func TestVendorContact_Update(t *testing.T) { ` var result struct { - UpdateVendorContact struct { - VendorContact struct { + UpdateThirdPartyContact struct { + ThirdPartyContact struct { ID string `json:"id"` FullName string `json:"fullName"` Phone string `json:"phone"` Role string `json:"role"` - } `json:"vendorContact"` - } `json:"updateVendorContact"` + } `json:"thirdPartyContact"` + } `json:"updateThirdPartyContact"` } err = owner.Execute(query, map[string]any{ @@ -154,23 +154,23 @@ func TestVendorContact_Update(t *testing.T) { }, &result) require.NoError(t, err) - assert.Equal(t, contactID, result.UpdateVendorContact.VendorContact.ID) - assert.Equal(t, "Updated Name", result.UpdateVendorContact.VendorContact.FullName) - assert.Equal(t, "+1-555-999-8888", result.UpdateVendorContact.VendorContact.Phone) - assert.Equal(t, "Senior Account Manager", result.UpdateVendorContact.VendorContact.Role) + assert.Equal(t, contactID, result.UpdateThirdPartyContact.ThirdPartyContact.ID) + assert.Equal(t, "Updated Name", result.UpdateThirdPartyContact.ThirdPartyContact.FullName) + assert.Equal(t, "+1-555-999-8888", result.UpdateThirdPartyContact.ThirdPartyContact.Phone) + assert.Equal(t, "Senior Account Manager", result.UpdateThirdPartyContact.ThirdPartyContact.Role) } -func TestVendorContact_Delete(t *testing.T) { +func TestThirdPartyContact_Delete(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - vendorID := factory.NewVendor(owner).WithName("Delete Contact Vendor").Create() + thirdPartyID := factory.NewThirdParty(owner).WithName("Delete Contact ThirdParty").Create() // Create a contact to delete createQuery := ` - mutation CreateVendorContact($input: CreateVendorContactInput!) { - createVendorContact(input: $input) { - vendorContactEdge { + mutation CreateThirdPartyContact($input: CreateThirdPartyContactInput!) { + createThirdPartyContact(input: $input) { + thirdPartyContactEdge { node { id } @@ -180,61 +180,61 @@ func TestVendorContact_Delete(t *testing.T) { ` var createResult struct { - CreateVendorContact struct { - VendorContactEdge struct { + CreateThirdPartyContact struct { + ThirdPartyContactEdge struct { Node struct { ID string `json:"id"` } `json:"node"` - } `json:"vendorContactEdge"` - } `json:"createVendorContact"` + } `json:"thirdPartyContactEdge"` + } `json:"createThirdPartyContact"` } err := owner.Execute(createQuery, map[string]any{ "input": map[string]any{ - "vendorId": vendorID, - "fullName": fmt.Sprintf("Contact to Delete %d", time.Now().UnixNano()), - "email": fmt.Sprintf("delete.%d@vendor.com", time.Now().UnixNano()), + "thirdPartyId": thirdPartyID, + "fullName": fmt.Sprintf("Contact to Delete %d", time.Now().UnixNano()), + "email": fmt.Sprintf("delete.%d@thirdParty.com", time.Now().UnixNano()), }, }, &createResult) require.NoError(t, err) - contactID := createResult.CreateVendorContact.VendorContactEdge.Node.ID + contactID := createResult.CreateThirdPartyContact.ThirdPartyContactEdge.Node.ID deleteQuery := ` - mutation DeleteVendorContact($input: DeleteVendorContactInput!) { - deleteVendorContact(input: $input) { - deletedVendorContactId + mutation DeleteThirdPartyContact($input: DeleteThirdPartyContactInput!) { + deleteThirdPartyContact(input: $input) { + deletedThirdPartyContactId } } ` var result struct { - DeleteVendorContact struct { - DeletedVendorContactID string `json:"deletedVendorContactId"` - } `json:"deleteVendorContact"` + DeleteThirdPartyContact struct { + DeletedThirdPartyContactID string `json:"deletedThirdPartyContactId"` + } `json:"deleteThirdPartyContact"` } err = owner.Execute(deleteQuery, map[string]any{ "input": map[string]any{ - "vendorContactId": contactID, + "thirdPartyContactId": contactID, }, }, &result) require.NoError(t, err) - assert.Equal(t, contactID, result.DeleteVendorContact.DeletedVendorContactID) + assert.Equal(t, contactID, result.DeleteThirdPartyContact.DeletedThirdPartyContactID) } -func TestVendorContact_List(t *testing.T) { +func TestThirdPartyContact_List(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - vendorID := factory.NewVendor(owner).WithName("List Contacts Vendor").Create() + thirdPartyID := factory.NewThirdParty(owner).WithName("List Contacts ThirdParty").Create() // Create multiple contacts for i := range 3 { query := ` - mutation CreateVendorContact($input: CreateVendorContactInput!) { - createVendorContact(input: $input) { - vendorContactEdge { + mutation CreateThirdPartyContact($input: CreateThirdPartyContactInput!) { + createThirdPartyContact(input: $input) { + thirdPartyContactEdge { node { id } @@ -245,18 +245,18 @@ func TestVendorContact_List(t *testing.T) { _, err := owner.Do(query, map[string]any{ "input": map[string]any{ - "vendorId": vendorID, - "fullName": fmt.Sprintf("Contact %d", i), - "email": fmt.Sprintf("contact.%d.%d@vendor.com", i, time.Now().UnixNano()), + "thirdPartyId": thirdPartyID, + "fullName": fmt.Sprintf("Contact %d", i), + "email": fmt.Sprintf("contact.%d.%d@thirdParty.com", i, time.Now().UnixNano()), }, }) require.NoError(t, err) } query := ` - query GetVendorContacts($id: ID!) { + query GetThirdPartyContacts($id: ID!) { node(id: $id) { - ... on Vendor { + ... on ThirdParty { contacts(first: 10) { edges { node { @@ -286,7 +286,7 @@ func TestVendorContact_List(t *testing.T) { } err := owner.Execute(query, map[string]any{ - "id": vendorID, + "id": thirdPartyID, }, &result) require.NoError(t, err) assert.GreaterOrEqual(t, len(result.Node.Contacts.Edges), 3) diff --git a/e2e/console/vendor_publish_test.go b/e2e/console/third_party_publish_test.go similarity index 75% rename from e2e/console/vendor_publish_test.go rename to e2e/console/third_party_publish_test.go index 4f0515b12..459353c1a 100644 --- a/e2e/console/vendor_publish_test.go +++ b/e2e/console/third_party_publish_test.go @@ -23,7 +23,7 @@ import ( "go.probo.inc/probo/e2e/internal/testutil" ) -func TestVendor_PublishVendorList(t *testing.T) { +func TestThirdParty_PublishThirdPartyList(t *testing.T) { t.Parallel() t.Run( @@ -32,11 +32,11 @@ func TestVendor_PublishVendorList(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - factory.CreateVendor(owner, factory.Attrs{"name": "Test Vendor"}) + factory.CreateThirdParty(owner, factory.Attrs{"name": "Test ThirdParty"}) const query = ` - mutation($input: PublishVendorListInput!) { - publishVendorList(input: $input) { + mutation($input: PublishThirdPartyListInput!) { + publishThirdPartyList(input: $input) { documentEdge { node { id @@ -60,7 +60,7 @@ func TestVendor_PublishVendorList(t *testing.T) { ` var result struct { - PublishVendorList struct { + PublishThirdPartyList struct { DocumentEdge struct { Node struct { ID string `json:"id"` @@ -79,7 +79,7 @@ func TestVendor_PublishVendorList(t *testing.T) { Content string `json:"content"` } `json:"node"` } `json:"documentVersionEdge"` - } `json:"publishVendorList"` + } `json:"publishThirdPartyList"` } err := owner.Execute( @@ -94,12 +94,12 @@ func TestVendor_PublishVendorList(t *testing.T) { ) require.NoError(t, err) - doc := result.PublishVendorList.DocumentEdge.Node + doc := result.PublishThirdPartyList.DocumentEdge.Node assert.NotEmpty(t, doc.ID) assert.Equal(t, "GENERATED", doc.WriteMode) assert.Equal(t, "ACTIVE", doc.Status) - ver := result.PublishVendorList.DocumentVersionEdge.Node + ver := result.PublishThirdPartyList.DocumentVersionEdge.Node assert.NotEmpty(t, ver.ID) assert.Equal(t, "REGISTER", ver.DocumentType) assert.Equal(t, "PUBLISHED", ver.Status) @@ -117,8 +117,8 @@ func TestVendor_PublishVendorList(t *testing.T) { owner := testutil.NewClient(t, testutil.RoleOwner) const query = ` - mutation($input: PublishVendorListInput!) { - publishVendorList(input: $input) { + mutation($input: PublishThirdPartyListInput!) { + publishThirdPartyList(input: $input) { documentEdge { node { id writeMode } } @@ -130,7 +130,7 @@ func TestVendor_PublishVendorList(t *testing.T) { ` var result struct { - PublishVendorList struct { + PublishThirdPartyList struct { DocumentEdge struct { Node struct { ID string `json:"id"` @@ -144,7 +144,7 @@ func TestVendor_PublishVendorList(t *testing.T) { Major int `json:"major"` } `json:"node"` } `json:"documentVersionEdge"` - } `json:"publishVendorList"` + } `json:"publishThirdPartyList"` } err := owner.Execute( @@ -160,11 +160,11 @@ func TestVendor_PublishVendorList(t *testing.T) { ) require.NoError(t, err) - doc := result.PublishVendorList.DocumentEdge.Node + doc := result.PublishThirdPartyList.DocumentEdge.Node assert.NotEmpty(t, doc.ID) assert.Equal(t, "GENERATED", doc.WriteMode) - ver := result.PublishVendorList.DocumentVersionEdge.Node + ver := result.PublishThirdPartyList.DocumentVersionEdge.Node assert.NotEmpty(t, ver.ID) assert.Equal(t, "PENDING_APPROVAL", ver.Status) }, @@ -176,11 +176,11 @@ func TestVendor_PublishVendorList(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - factory.CreateVendor(owner, factory.Attrs{"name": "Reuse Vendor"}) + factory.CreateThirdParty(owner, factory.Attrs{"name": "Reuse ThirdParty"}) const query = ` - mutation($input: PublishVendorListInput!) { - publishVendorList(input: $input) { + mutation($input: PublishThirdPartyListInput!) { + publishThirdPartyList(input: $input) { documentEdge { node { id } } documentVersionEdge { node { id major } } } @@ -188,7 +188,7 @@ func TestVendor_PublishVendorList(t *testing.T) { ` var r1, r2 struct { - PublishVendorList struct { + PublishThirdPartyList struct { DocumentEdge struct { Node struct { ID string `json:"id"` @@ -200,7 +200,7 @@ func TestVendor_PublishVendorList(t *testing.T) { Major int `json:"major"` } `json:"node"` } `json:"documentVersionEdge"` - } `json:"publishVendorList"` + } `json:"publishThirdPartyList"` } input := map[string]any{ @@ -217,26 +217,26 @@ func TestVendor_PublishVendorList(t *testing.T) { require.NoError(t, err) assert.Equal(t, - r1.PublishVendorList.DocumentEdge.Node.ID, - r2.PublishVendorList.DocumentEdge.Node.ID, + r1.PublishThirdPartyList.DocumentEdge.Node.ID, + r2.PublishThirdPartyList.DocumentEdge.Node.ID, "should reuse same document", ) - assert.Equal(t, 1, r1.PublishVendorList.DocumentVersionEdge.Node.Major) - assert.Equal(t, 2, r2.PublishVendorList.DocumentVersionEdge.Node.Major) + assert.Equal(t, 1, r1.PublishThirdPartyList.DocumentVersionEdge.Node.Major) + assert.Equal(t, 2, r2.PublishThirdPartyList.DocumentVersionEdge.Node.Major) }, ) t.Run( - "organization vendorsDocument links to published document", + "organization thirdPartiesDocument links to published document", func(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - factory.CreateVendor(owner, factory.Attrs{"name": "Linked Vendor"}) + factory.CreateThirdParty(owner, factory.Attrs{"name": "Linked ThirdParty"}) const publishQuery = ` - mutation($input: PublishVendorListInput!) { - publishVendorList(input: $input) { + mutation($input: PublishThirdPartyListInput!) { + publishThirdPartyList(input: $input) { documentEdge { node { id } } documentVersionEdge { node { id } } } @@ -244,7 +244,7 @@ func TestVendor_PublishVendorList(t *testing.T) { ` var publishResult struct { - PublishVendorList struct { + PublishThirdPartyList struct { DocumentEdge struct { Node struct { ID string `json:"id"` @@ -255,7 +255,7 @@ func TestVendor_PublishVendorList(t *testing.T) { ID string `json:"id"` } `json:"node"` } `json:"documentVersionEdge"` - } `json:"publishVendorList"` + } `json:"publishThirdPartyList"` } err := owner.Execute( @@ -270,14 +270,14 @@ func TestVendor_PublishVendorList(t *testing.T) { ) require.NoError(t, err) - docID := publishResult.PublishVendorList.DocumentEdge.Node.ID + docID := publishResult.PublishThirdPartyList.DocumentEdge.Node.ID const orgQuery = ` query($id: ID!) { node(id: $id) { ... on Organization { id - vendorsDocument { id } + thirdPartiesDocument { id } } } } @@ -285,10 +285,10 @@ func TestVendor_PublishVendorList(t *testing.T) { var orgResult struct { Node struct { - ID string `json:"id"` - VendorsDocument *struct { + ID string `json:"id"` + ThirdPartiesDocument *struct { ID string `json:"id"` - } `json:"vendorsDocument"` + } `json:"thirdPartiesDocument"` } `json:"node"` } @@ -298,23 +298,23 @@ func TestVendor_PublishVendorList(t *testing.T) { &orgResult, ) require.NoError(t, err) - require.NotNil(t, orgResult.Node.VendorsDocument) - assert.Equal(t, docID, orgResult.Node.VendorsDocument.ID) + require.NotNil(t, orgResult.Node.ThirdPartiesDocument) + assert.Equal(t, docID, orgResult.Node.ThirdPartiesDocument.ID) }, ) } -func TestVendor_PublishVendorList_RBAC(t *testing.T) { +func TestThirdParty_PublishThirdPartyList_RBAC(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) viewer := testutil.NewClientInOrg(t, testutil.RoleViewer, owner) - factory.CreateVendor(owner, factory.Attrs{"name": "RBAC Vendor"}) + factory.CreateThirdParty(owner, factory.Attrs{"name": "RBAC ThirdParty"}) const query = ` - mutation($input: PublishVendorListInput!) { - publishVendorList(input: $input) { + mutation($input: PublishThirdPartyListInput!) { + publishThirdPartyList(input: $input) { documentEdge { node { id } } documentVersionEdge { node { id } } } @@ -322,7 +322,7 @@ func TestVendor_PublishVendorList_RBAC(t *testing.T) { ` t.Run( - "viewer cannot publish vendor list", + "viewer cannot publish thirdParty list", func(t *testing.T) { t.Parallel() diff --git a/e2e/console/vendor_service_test.go b/e2e/console/third_party_service_test.go similarity index 56% rename from e2e/console/vendor_service_test.go rename to e2e/console/third_party_service_test.go index bd08ba641..40ea6b76e 100644 --- a/e2e/console/vendor_service_test.go +++ b/e2e/console/third_party_service_test.go @@ -22,15 +22,15 @@ import ( "go.probo.inc/probo/e2e/internal/testutil" ) -func TestVendorService_Create(t *testing.T) { +func TestThirdPartyService_Create(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - // Create a vendor first - createVendorMutation := ` - mutation CreateVendor($input: CreateVendorInput!) { - createVendor(input: $input) { - vendorEdge { + // Create a thirdParty first + createThirdPartyMutation := ` + mutation CreateThirdParty($input: CreateThirdPartyInput!) { + createThirdParty(input: $input) { + thirdPartyEdge { node { id } @@ -39,129 +39,129 @@ func TestVendorService_Create(t *testing.T) { } ` - var createVendorResult struct { - CreateVendor struct { - VendorEdge struct { + var createThirdPartyResult struct { + CreateThirdParty struct { + ThirdPartyEdge struct { Node struct { ID string `json:"id"` } `json:"node"` - } `json:"vendorEdge"` - } `json:"createVendor"` + } `json:"thirdPartyEdge"` + } `json:"createThirdParty"` } - err := owner.Execute(createVendorMutation, map[string]any{ + err := owner.Execute(createThirdPartyMutation, map[string]any{ "input": map[string]any{ "organizationId": owner.GetOrganizationID().String(), "name": "AWS", "category": "CLOUD_PROVIDER", }, - }, &createVendorResult) + }, &createThirdPartyResult) require.NoError(t, err) - vendorID := createVendorResult.CreateVendor.VendorEdge.Node.ID + thirdPartyID := createThirdPartyResult.CreateThirdParty.ThirdPartyEdge.Node.ID tests := []struct { name string role testutil.TestRole variables func() map[string]any check func(t *testing.T, err error, m *struct { - CreateVendorService struct { - VendorServiceEdge struct { + CreateThirdPartyService struct { + ThirdPartyServiceEdge struct { Node struct { ID string `json:"id"` Name string `json:"name"` Description *string `json:"description"` } `json:"node"` - } `json:"vendorServiceEdge"` - } `json:"createVendorService"` + } `json:"thirdPartyServiceEdge"` + } `json:"createThirdPartyService"` }) }{ { - name: "Owner can create vendor service", + name: "Owner can create thirdParty service", role: testutil.RoleOwner, variables: func() map[string]any { return map[string]any{ "input": map[string]any{ - "vendorId": vendorID, - "name": "Amazon S3", - "description": "Simple Storage Service", + "thirdPartyId": thirdPartyID, + "name": "Amazon S3", + "description": "Simple Storage Service", }, } }, check: func(t *testing.T, err error, m *struct { - CreateVendorService struct { - VendorServiceEdge struct { + CreateThirdPartyService struct { + ThirdPartyServiceEdge struct { Node struct { ID string `json:"id"` Name string `json:"name"` Description *string `json:"description"` } `json:"node"` - } `json:"vendorServiceEdge"` - } `json:"createVendorService"` + } `json:"thirdPartyServiceEdge"` + } `json:"createThirdPartyService"` }) { require.NoError(t, err) - assert.NotEmpty(t, m.CreateVendorService.VendorServiceEdge.Node.ID) - assert.Equal(t, "Amazon S3", m.CreateVendorService.VendorServiceEdge.Node.Name) - assert.Equal(t, "Simple Storage Service", *m.CreateVendorService.VendorServiceEdge.Node.Description) + assert.NotEmpty(t, m.CreateThirdPartyService.ThirdPartyServiceEdge.Node.ID) + assert.Equal(t, "Amazon S3", m.CreateThirdPartyService.ThirdPartyServiceEdge.Node.Name) + assert.Equal(t, "Simple Storage Service", *m.CreateThirdPartyService.ThirdPartyServiceEdge.Node.Description) }, }, { - name: "Admin can create vendor service", + name: "Admin can create thirdParty service", role: testutil.RoleAdmin, variables: func() map[string]any { return map[string]any{ "input": map[string]any{ - "vendorId": vendorID, - "name": "Amazon EC2", - "description": "Elastic Compute Cloud", + "thirdPartyId": thirdPartyID, + "name": "Amazon EC2", + "description": "Elastic Compute Cloud", }, } }, check: func(t *testing.T, err error, m *struct { - CreateVendorService struct { - VendorServiceEdge struct { + CreateThirdPartyService struct { + ThirdPartyServiceEdge struct { Node struct { ID string `json:"id"` Name string `json:"name"` Description *string `json:"description"` } `json:"node"` - } `json:"vendorServiceEdge"` - } `json:"createVendorService"` + } `json:"thirdPartyServiceEdge"` + } `json:"createThirdPartyService"` }) { require.NoError(t, err) }, }, { - name: "Viewer cannot create vendor service", + name: "Viewer cannot create thirdParty service", role: testutil.RoleViewer, variables: func() map[string]any { return map[string]any{ "input": map[string]any{ - "vendorId": vendorID, - "name": "Should Fail", + "thirdPartyId": thirdPartyID, + "name": "Should Fail", }, } }, check: func(t *testing.T, err error, m *struct { - CreateVendorService struct { - VendorServiceEdge struct { + CreateThirdPartyService struct { + ThirdPartyServiceEdge struct { Node struct { ID string `json:"id"` Name string `json:"name"` Description *string `json:"description"` } `json:"node"` - } `json:"vendorServiceEdge"` - } `json:"createVendorService"` + } `json:"thirdPartyServiceEdge"` + } `json:"createThirdPartyService"` }) { - require.Error(t, err, "Viewer should not be able to create vendor service") + require.Error(t, err, "Viewer should not be able to create thirdParty service") }, }, } - createVendorServiceMutation := ` - mutation CreateVendorService($input: CreateVendorServiceInput!) { - createVendorService(input: $input) { - vendorServiceEdge { + createThirdPartyServiceMutation := ` + mutation CreateThirdPartyService($input: CreateThirdPartyServiceInput!) { + createThirdPartyService(input: $input) { + thirdPartyServiceEdge { node { id name @@ -182,32 +182,32 @@ func TestVendorService_Create(t *testing.T) { } var m struct { - CreateVendorService struct { - VendorServiceEdge struct { + CreateThirdPartyService struct { + ThirdPartyServiceEdge struct { Node struct { ID string `json:"id"` Name string `json:"name"` Description *string `json:"description"` } `json:"node"` - } `json:"vendorServiceEdge"` - } `json:"createVendorService"` + } `json:"thirdPartyServiceEdge"` + } `json:"createThirdPartyService"` } - err := client.Execute(createVendorServiceMutation, tt.variables(), &m) + err := client.Execute(createThirdPartyServiceMutation, tt.variables(), &m) tt.check(t, err, &m) }) } } -func TestVendorService_Update(t *testing.T) { +func TestThirdPartyService_Update(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - // Create a vendor first - createVendorMutation := ` - mutation CreateVendor($input: CreateVendorInput!) { - createVendor(input: $input) { - vendorEdge { + // Create a thirdParty first + createThirdPartyMutation := ` + mutation CreateThirdParty($input: CreateThirdPartyInput!) { + createThirdParty(input: $input) { + thirdPartyEdge { node { id } @@ -216,32 +216,32 @@ func TestVendorService_Update(t *testing.T) { } ` - var createVendorResult struct { - CreateVendor struct { - VendorEdge struct { + var createThirdPartyResult struct { + CreateThirdParty struct { + ThirdPartyEdge struct { Node struct { ID string `json:"id"` } `json:"node"` - } `json:"vendorEdge"` - } `json:"createVendor"` + } `json:"thirdPartyEdge"` + } `json:"createThirdParty"` } - err := owner.Execute(createVendorMutation, map[string]any{ + err := owner.Execute(createThirdPartyMutation, map[string]any{ "input": map[string]any{ "organizationId": owner.GetOrganizationID().String(), "name": "Google Cloud", "category": "CLOUD_PROVIDER", }, - }, &createVendorResult) + }, &createThirdPartyResult) require.NoError(t, err) - vendorID := createVendorResult.CreateVendor.VendorEdge.Node.ID + thirdPartyID := createThirdPartyResult.CreateThirdParty.ThirdPartyEdge.Node.ID - // Create a vendor service + // Create a thirdParty service createServiceMutation := ` - mutation CreateVendorService($input: CreateVendorServiceInput!) { - createVendorService(input: $input) { - vendorServiceEdge { + mutation CreateThirdPartyService($input: CreateThirdPartyServiceInput!) { + createThirdPartyService(input: $input) { + thirdPartyServiceEdge { node { id } @@ -251,42 +251,42 @@ func TestVendorService_Update(t *testing.T) { ` var createServiceResult struct { - CreateVendorService struct { - VendorServiceEdge struct { + CreateThirdPartyService struct { + ThirdPartyServiceEdge struct { Node struct { ID string `json:"id"` } `json:"node"` - } `json:"vendorServiceEdge"` - } `json:"createVendorService"` + } `json:"thirdPartyServiceEdge"` + } `json:"createThirdPartyService"` } err = owner.Execute(createServiceMutation, map[string]any{ "input": map[string]any{ - "vendorId": vendorID, - "name": "Cloud Storage", - "description": "Initial description", + "thirdPartyId": thirdPartyID, + "name": "Cloud Storage", + "description": "Initial description", }, }, &createServiceResult) require.NoError(t, err) - serviceID := createServiceResult.CreateVendorService.VendorServiceEdge.Node.ID + serviceID := createServiceResult.CreateThirdPartyService.ThirdPartyServiceEdge.Node.ID tests := []struct { name string role testutil.TestRole variables func() map[string]any check func(t *testing.T, err error, m *struct { - UpdateVendorService struct { - VendorService struct { + UpdateThirdPartyService struct { + ThirdPartyService struct { ID string `json:"id"` Name string `json:"name"` Description *string `json:"description"` - } `json:"vendorService"` - } `json:"updateVendorService"` + } `json:"thirdPartyService"` + } `json:"updateThirdPartyService"` }) }{ { - name: "Owner can update vendor service", + name: "Owner can update thirdParty service", role: testutil.RoleOwner, variables: func() map[string]any { return map[string]any{ @@ -298,22 +298,22 @@ func TestVendorService_Update(t *testing.T) { } }, check: func(t *testing.T, err error, m *struct { - UpdateVendorService struct { - VendorService struct { + UpdateThirdPartyService struct { + ThirdPartyService struct { ID string `json:"id"` Name string `json:"name"` Description *string `json:"description"` - } `json:"vendorService"` - } `json:"updateVendorService"` + } `json:"thirdPartyService"` + } `json:"updateThirdPartyService"` }) { require.NoError(t, err) - assert.Equal(t, serviceID, m.UpdateVendorService.VendorService.ID) - assert.Equal(t, "Updated Cloud Storage", m.UpdateVendorService.VendorService.Name) - assert.Equal(t, "Updated description", *m.UpdateVendorService.VendorService.Description) + assert.Equal(t, serviceID, m.UpdateThirdPartyService.ThirdPartyService.ID) + assert.Equal(t, "Updated Cloud Storage", m.UpdateThirdPartyService.ThirdPartyService.Name) + assert.Equal(t, "Updated description", *m.UpdateThirdPartyService.ThirdPartyService.Description) }, }, { - name: "Admin can update vendor service", + name: "Admin can update thirdParty service", role: testutil.RoleAdmin, variables: func() map[string]any { return map[string]any{ @@ -324,19 +324,19 @@ func TestVendorService_Update(t *testing.T) { } }, check: func(t *testing.T, err error, m *struct { - UpdateVendorService struct { - VendorService struct { + UpdateThirdPartyService struct { + ThirdPartyService struct { ID string `json:"id"` Name string `json:"name"` Description *string `json:"description"` - } `json:"vendorService"` - } `json:"updateVendorService"` + } `json:"thirdPartyService"` + } `json:"updateThirdPartyService"` }) { require.NoError(t, err) }, }, { - name: "Viewer cannot update vendor service", + name: "Viewer cannot update thirdParty service", role: testutil.RoleViewer, variables: func() map[string]any { return map[string]any{ @@ -347,23 +347,23 @@ func TestVendorService_Update(t *testing.T) { } }, check: func(t *testing.T, err error, m *struct { - UpdateVendorService struct { - VendorService struct { + UpdateThirdPartyService struct { + ThirdPartyService struct { ID string `json:"id"` Name string `json:"name"` Description *string `json:"description"` - } `json:"vendorService"` - } `json:"updateVendorService"` + } `json:"thirdPartyService"` + } `json:"updateThirdPartyService"` }) { - require.Error(t, err, "Viewer should not be able to update vendor service") + require.Error(t, err, "Viewer should not be able to update thirdParty service") }, }, } - updateVendorServiceMutation := ` - mutation UpdateVendorService($input: UpdateVendorServiceInput!) { - updateVendorService(input: $input) { - vendorService { + updateThirdPartyServiceMutation := ` + mutation UpdateThirdPartyService($input: UpdateThirdPartyServiceInput!) { + updateThirdPartyService(input: $input) { + thirdPartyService { id name description @@ -382,30 +382,30 @@ func TestVendorService_Update(t *testing.T) { } var m struct { - UpdateVendorService struct { - VendorService struct { + UpdateThirdPartyService struct { + ThirdPartyService struct { ID string `json:"id"` Name string `json:"name"` Description *string `json:"description"` - } `json:"vendorService"` - } `json:"updateVendorService"` + } `json:"thirdPartyService"` + } `json:"updateThirdPartyService"` } - err := client.Execute(updateVendorServiceMutation, tt.variables(), &m) + err := client.Execute(updateThirdPartyServiceMutation, tt.variables(), &m) tt.check(t, err, &m) }) } } -func TestVendorService_Delete(t *testing.T) { +func TestThirdPartyService_Delete(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - // Create a vendor first - createVendorMutation := ` - mutation CreateVendor($input: CreateVendorInput!) { - createVendor(input: $input) { - vendorEdge { + // Create a thirdParty first + createThirdPartyMutation := ` + mutation CreateThirdParty($input: CreateThirdPartyInput!) { + createThirdParty(input: $input) { + thirdPartyEdge { node { id } @@ -414,32 +414,32 @@ func TestVendorService_Delete(t *testing.T) { } ` - var createVendorResult struct { - CreateVendor struct { - VendorEdge struct { + var createThirdPartyResult struct { + CreateThirdParty struct { + ThirdPartyEdge struct { Node struct { ID string `json:"id"` } `json:"node"` - } `json:"vendorEdge"` - } `json:"createVendor"` + } `json:"thirdPartyEdge"` + } `json:"createThirdParty"` } - err := owner.Execute(createVendorMutation, map[string]any{ + err := owner.Execute(createThirdPartyMutation, map[string]any{ "input": map[string]any{ "organizationId": owner.GetOrganizationID().String(), "name": "Azure", "category": "CLOUD_PROVIDER", }, - }, &createVendorResult) + }, &createThirdPartyResult) require.NoError(t, err) - vendorID := createVendorResult.CreateVendor.VendorEdge.Node.ID + thirdPartyID := createThirdPartyResult.CreateThirdParty.ThirdPartyEdge.Node.ID createService := func() string { createServiceMutation := ` - mutation CreateVendorService($input: CreateVendorServiceInput!) { - createVendorService(input: $input) { - vendorServiceEdge { + mutation CreateThirdPartyService($input: CreateThirdPartyServiceInput!) { + createThirdPartyService(input: $input) { + thirdPartyServiceEdge { node { id } @@ -449,24 +449,24 @@ func TestVendorService_Delete(t *testing.T) { ` var m struct { - CreateVendorService struct { - VendorServiceEdge struct { + CreateThirdPartyService struct { + ThirdPartyServiceEdge struct { Node struct { ID string `json:"id"` } `json:"node"` - } `json:"vendorServiceEdge"` - } `json:"createVendorService"` + } `json:"thirdPartyServiceEdge"` + } `json:"createThirdPartyService"` } err := owner.Execute(createServiceMutation, map[string]any{ "input": map[string]any{ - "vendorId": vendorID, - "name": "Service to delete", + "thirdPartyId": thirdPartyID, + "name": "Service to delete", }, }, &m) require.NoError(t, err) - return m.CreateVendorService.VendorServiceEdge.Node.ID + return m.CreateThirdPartyService.ThirdPartyServiceEdge.Node.ID } tests := []struct { @@ -474,73 +474,73 @@ func TestVendorService_Delete(t *testing.T) { role testutil.TestRole variables func(serviceID string) map[string]any check func(t *testing.T, err error, serviceID string, m *struct { - DeleteVendorService struct { - DeletedVendorServiceID string `json:"deletedVendorServiceId"` - } `json:"deleteVendorService"` + DeleteThirdPartyService struct { + DeletedThirdPartyServiceID string `json:"deletedThirdPartyServiceId"` + } `json:"deleteThirdPartyService"` }) }{ { - name: "Viewer cannot delete vendor service", + name: "Viewer cannot delete thirdParty service", role: testutil.RoleViewer, variables: func(serviceID string) map[string]any { return map[string]any{ "input": map[string]any{ - "vendorServiceId": serviceID, + "thirdPartyServiceId": serviceID, }, } }, check: func(t *testing.T, err error, serviceID string, m *struct { - DeleteVendorService struct { - DeletedVendorServiceID string `json:"deletedVendorServiceId"` - } `json:"deleteVendorService"` + DeleteThirdPartyService struct { + DeletedThirdPartyServiceID string `json:"deletedThirdPartyServiceId"` + } `json:"deleteThirdPartyService"` }) { - require.Error(t, err, "Viewer should not be able to delete vendor service") + require.Error(t, err, "Viewer should not be able to delete thirdParty service") }, }, { - name: "Admin can delete vendor service", + name: "Admin can delete thirdParty service", role: testutil.RoleAdmin, variables: func(serviceID string) map[string]any { return map[string]any{ "input": map[string]any{ - "vendorServiceId": serviceID, + "thirdPartyServiceId": serviceID, }, } }, check: func(t *testing.T, err error, serviceID string, m *struct { - DeleteVendorService struct { - DeletedVendorServiceID string `json:"deletedVendorServiceId"` - } `json:"deleteVendorService"` + DeleteThirdPartyService struct { + DeletedThirdPartyServiceID string `json:"deletedThirdPartyServiceId"` + } `json:"deleteThirdPartyService"` }) { require.NoError(t, err) - assert.Equal(t, serviceID, m.DeleteVendorService.DeletedVendorServiceID) + assert.Equal(t, serviceID, m.DeleteThirdPartyService.DeletedThirdPartyServiceID) }, }, { - name: "Owner can delete vendor service", + name: "Owner can delete thirdParty service", role: testutil.RoleOwner, variables: func(serviceID string) map[string]any { return map[string]any{ "input": map[string]any{ - "vendorServiceId": serviceID, + "thirdPartyServiceId": serviceID, }, } }, check: func(t *testing.T, err error, serviceID string, m *struct { - DeleteVendorService struct { - DeletedVendorServiceID string `json:"deletedVendorServiceId"` - } `json:"deleteVendorService"` + DeleteThirdPartyService struct { + DeletedThirdPartyServiceID string `json:"deletedThirdPartyServiceId"` + } `json:"deleteThirdPartyService"` }) { require.NoError(t, err) - assert.Equal(t, serviceID, m.DeleteVendorService.DeletedVendorServiceID) + assert.Equal(t, serviceID, m.DeleteThirdPartyService.DeletedThirdPartyServiceID) }, }, } - deleteVendorServiceMutation := ` - mutation DeleteVendorService($input: DeleteVendorServiceInput!) { - deleteVendorService(input: $input) { - deletedVendorServiceId + deleteThirdPartyServiceMutation := ` + mutation DeleteThirdPartyService($input: DeleteThirdPartyServiceInput!) { + deleteThirdPartyService(input: $input) { + deletedThirdPartyServiceId } } ` @@ -557,26 +557,26 @@ func TestVendorService_Delete(t *testing.T) { } var m struct { - DeleteVendorService struct { - DeletedVendorServiceID string `json:"deletedVendorServiceId"` - } `json:"deleteVendorService"` + DeleteThirdPartyService struct { + DeletedThirdPartyServiceID string `json:"deletedThirdPartyServiceId"` + } `json:"deleteThirdPartyService"` } - err := client.Execute(deleteVendorServiceMutation, tt.variables(serviceID), &m) + err := client.Execute(deleteThirdPartyServiceMutation, tt.variables(serviceID), &m) tt.check(t, err, serviceID, &m) }) } } -func TestVendorService_List(t *testing.T) { +func TestThirdPartyService_List(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - // Create a vendor first - createVendorMutation := ` - mutation CreateVendor($input: CreateVendorInput!) { - createVendor(input: $input) { - vendorEdge { + // Create a thirdParty first + createThirdPartyMutation := ` + mutation CreateThirdParty($input: CreateThirdPartyInput!) { + createThirdParty(input: $input) { + thirdPartyEdge { node { id } @@ -585,32 +585,32 @@ func TestVendorService_List(t *testing.T) { } ` - var createVendorResult struct { - CreateVendor struct { - VendorEdge struct { + var createThirdPartyResult struct { + CreateThirdParty struct { + ThirdPartyEdge struct { Node struct { ID string `json:"id"` } `json:"node"` - } `json:"vendorEdge"` - } `json:"createVendor"` + } `json:"thirdPartyEdge"` + } `json:"createThirdParty"` } - err := owner.Execute(createVendorMutation, map[string]any{ + err := owner.Execute(createThirdPartyMutation, map[string]any{ "input": map[string]any{ "organizationId": owner.GetOrganizationID().String(), - "name": "Vendor for Services", + "name": "ThirdParty for Services", "category": "CLOUD_PROVIDER", }, - }, &createVendorResult) + }, &createThirdPartyResult) require.NoError(t, err) - vendorID := createVendorResult.CreateVendor.VendorEdge.Node.ID + thirdPartyID := createThirdPartyResult.CreateThirdParty.ThirdPartyEdge.Node.ID // Create multiple services createServiceMutation := ` - mutation CreateVendorService($input: CreateVendorServiceInput!) { - createVendorService(input: $input) { - vendorServiceEdge { + mutation CreateThirdPartyService($input: CreateThirdPartyServiceInput!) { + createThirdPartyService(input: $input) { + thirdPartyServiceEdge { node { id } @@ -622,19 +622,19 @@ func TestVendorService_List(t *testing.T) { services := []string{"Service A", "Service B", "Service C"} for _, name := range services { var m struct { - CreateVendorService struct { - VendorServiceEdge struct { + CreateThirdPartyService struct { + ThirdPartyServiceEdge struct { Node struct { ID string `json:"id"` } `json:"node"` - } `json:"vendorServiceEdge"` - } `json:"createVendorService"` + } `json:"thirdPartyServiceEdge"` + } `json:"createThirdPartyService"` } err := owner.Execute(createServiceMutation, map[string]any{ "input": map[string]any{ - "vendorId": vendorID, - "name": name, + "thirdPartyId": thirdPartyID, + "name": name, }, }, &m) require.NoError(t, err) @@ -659,11 +659,11 @@ func TestVendorService_List(t *testing.T) { }) }{ { - name: "Owner can list vendor services", + name: "Owner can list thirdParty services", role: testutil.RoleOwner, variables: func() map[string]any { return map[string]any{ - "id": vendorID, + "id": thirdPartyID, } }, check: func(t *testing.T, err error, q *struct { @@ -684,11 +684,11 @@ func TestVendorService_List(t *testing.T) { }, }, { - name: "Viewer can list vendor services", + name: "Viewer can list thirdParty services", role: testutil.RoleViewer, variables: func() map[string]any { return map[string]any{ - "id": vendorID, + "id": thirdPartyID, } }, check: func(t *testing.T, err error, q *struct { @@ -709,10 +709,10 @@ func TestVendorService_List(t *testing.T) { }, } - listVendorServicesQuery := ` - query ListVendorServices($id: ID!) { + listThirdPartyServicesQuery := ` + query ListThirdPartyServices($id: ID!) { node(id: $id) { - ... on Vendor { + ... on ThirdParty { id services(first: 10) { edges { @@ -750,7 +750,7 @@ func TestVendorService_List(t *testing.T) { } `json:"node"` } - err := client.Execute(listVendorServicesQuery, tt.variables(), &q) + err := client.Execute(listThirdPartyServicesQuery, tt.variables(), &q) tt.check(t, err, &q) }) } diff --git a/e2e/console/vendor_test.go b/e2e/console/third_party_test.go similarity index 61% rename from e2e/console/vendor_test.go rename to e2e/console/third_party_test.go index 128771066..e7e8c35c9 100644 --- a/e2e/console/vendor_test.go +++ b/e2e/console/third_party_test.go @@ -24,15 +24,15 @@ import ( "go.probo.inc/probo/e2e/internal/testutil" ) -func TestVendor_Create(t *testing.T) { +func TestThirdParty_Create(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) t.Run("with full details", func(t *testing.T) { const query = ` - mutation($input: CreateVendorInput!) { - createVendor(input: $input) { - vendorEdge { + mutation($input: CreateThirdPartyInput!) { + createThirdParty(input: $input) { + thirdPartyEdge { node { id name @@ -44,15 +44,15 @@ func TestVendor_Create(t *testing.T) { ` var result struct { - CreateVendor struct { - VendorEdge struct { + CreateThirdParty struct { + ThirdPartyEdge struct { Node struct { ID string `json:"id"` Name string `json:"name"` Description *string `json:"description"` } `json:"node"` - } `json:"vendorEdge"` - } `json:"createVendor"` + } `json:"thirdPartyEdge"` + } `json:"createThirdParty"` } err := owner.Execute(query, map[string]any{ @@ -65,16 +65,16 @@ func TestVendor_Create(t *testing.T) { }, &result) require.NoError(t, err) - assert.NotEmpty(t, result.CreateVendor.VendorEdge.Node.ID) - assert.Equal(t, "AWS", result.CreateVendor.VendorEdge.Node.Name) - assert.Equal(t, "Amazon Web Services - Cloud Provider", *result.CreateVendor.VendorEdge.Node.Description) + assert.NotEmpty(t, result.CreateThirdParty.ThirdPartyEdge.Node.ID) + assert.Equal(t, "AWS", result.CreateThirdParty.ThirdPartyEdge.Node.Name) + assert.Equal(t, "Amazon Web Services - Cloud Provider", *result.CreateThirdParty.ThirdPartyEdge.Node.Description) }) t.Run("with all optional fields", func(t *testing.T) { const query = ` - mutation($input: CreateVendorInput!) { - createVendor(input: $input) { - vendorEdge { + mutation($input: CreateThirdPartyInput!) { + createThirdParty(input: $input) { + thirdPartyEdge { node { id name @@ -90,8 +90,8 @@ func TestVendor_Create(t *testing.T) { ` var result struct { - CreateVendor struct { - VendorEdge struct { + CreateThirdParty struct { + ThirdPartyEdge struct { Node struct { ID string `json:"id"` Name string `json:"name"` @@ -101,8 +101,8 @@ func TestVendor_Create(t *testing.T) { TermsOfServiceUrl *string `json:"termsOfServiceUrl"` Certifications []string `json:"certifications"` } `json:"node"` - } `json:"vendorEdge"` - } `json:"createVendor"` + } `json:"thirdPartyEdge"` + } `json:"createThirdParty"` } err := owner.Execute(query, map[string]any{ @@ -118,24 +118,24 @@ func TestVendor_Create(t *testing.T) { }, &result) require.NoError(t, err) - assert.Equal(t, "Stripe", result.CreateVendor.VendorEdge.Node.Name) - assert.Equal(t, "Stripe, Inc.", *result.CreateVendor.VendorEdge.Node.LegalName) - assert.Contains(t, result.CreateVendor.VendorEdge.Node.Certifications, "SOC 2") + assert.Equal(t, "Stripe", result.CreateThirdParty.ThirdPartyEdge.Node.Name) + assert.Equal(t, "Stripe, Inc.", *result.CreateThirdParty.ThirdPartyEdge.Node.LegalName) + assert.Contains(t, result.CreateThirdParty.ThirdPartyEdge.Node.Certifications, "SOC 2") }) } -func TestVendor_Update(t *testing.T) { +func TestThirdParty_Update(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - vendorID := factory.CreateVendor(owner, factory.Attrs{ - "name": "Vendor to Update", + thirdPartyID := factory.CreateThirdParty(owner, factory.Attrs{ + "name": "ThirdParty to Update", "description": "Original description", }) const query = ` - mutation($input: UpdateVendorInput!) { - updateVendor(input: $input) { - vendor { + mutation($input: UpdateThirdPartyInput!) { + updateThirdParty(input: $input) { + thirdParty { id name } @@ -144,72 +144,72 @@ func TestVendor_Update(t *testing.T) { ` var result struct { - UpdateVendor struct { - Vendor struct { + UpdateThirdParty struct { + ThirdParty struct { ID string `json:"id"` Name string `json:"name"` - } `json:"vendor"` - } `json:"updateVendor"` + } `json:"thirdParty"` + } `json:"updateThirdParty"` } err := owner.Execute(query, map[string]any{ "input": map[string]any{ - "id": vendorID, - "name": "Updated Vendor Name", + "id": thirdPartyID, + "name": "Updated ThirdParty Name", "description": "Updated description", }, }, &result) require.NoError(t, err) - assert.Equal(t, vendorID, result.UpdateVendor.Vendor.ID) - assert.Equal(t, "Updated Vendor Name", result.UpdateVendor.Vendor.Name) + assert.Equal(t, thirdPartyID, result.UpdateThirdParty.ThirdParty.ID) + assert.Equal(t, "Updated ThirdParty Name", result.UpdateThirdParty.ThirdParty.Name) } -func TestVendor_Delete(t *testing.T) { +func TestThirdParty_Delete(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - vendorID := factory.CreateVendor(owner, factory.Attrs{ - "name": "Vendor to Delete", + thirdPartyID := factory.CreateThirdParty(owner, factory.Attrs{ + "name": "ThirdParty to Delete", }) const query = ` - mutation($input: DeleteVendorInput!) { - deleteVendor(input: $input) { - deletedVendorId + mutation($input: DeleteThirdPartyInput!) { + deleteThirdParty(input: $input) { + deletedThirdPartyId } } ` var result struct { - DeleteVendor struct { - DeletedVendorID string `json:"deletedVendorId"` - } `json:"deleteVendor"` + DeleteThirdParty struct { + DeletedThirdPartyID string `json:"deletedThirdPartyId"` + } `json:"deleteThirdParty"` } err := owner.Execute(query, map[string]any{ "input": map[string]any{ - "vendorId": vendorID, + "thirdPartyId": thirdPartyID, }, }, &result) require.NoError(t, err) - assert.Equal(t, vendorID, result.DeleteVendor.DeletedVendorID) + assert.Equal(t, thirdPartyID, result.DeleteThirdParty.DeletedThirdPartyID) } -func TestVendor_List(t *testing.T) { +func TestThirdParty_List(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - // Create multiple vendors - vendorNames := []string{"GitHub", "Slack", "Datadog"} - for _, name := range vendorNames { - factory.CreateVendor(owner, factory.Attrs{"name": name}) + // Create multiple thirdParties + thirdPartyNames := []string{"GitHub", "Slack", "Datadog"} + for _, name := range thirdPartyNames { + factory.CreateThirdParty(owner, factory.Attrs{"name": name}) } const query = ` query($orgId: ID!) { node(id: $orgId) { ... on Organization { - vendors(first: 10) { + thirdParties(first: 10) { edges { node { id @@ -225,7 +225,7 @@ func TestVendor_List(t *testing.T) { var result struct { Node struct { - Vendors struct { + ThirdParties struct { Edges []struct { Node struct { ID string `json:"id"` @@ -233,7 +233,7 @@ func TestVendor_List(t *testing.T) { } `json:"node"` } `json:"edges"` TotalCount int `json:"totalCount"` - } `json:"vendors"` + } `json:"thirdParties"` } `json:"node"` } @@ -241,18 +241,18 @@ func TestVendor_List(t *testing.T) { "orgId": owner.GetOrganizationID().String(), }, &result) require.NoError(t, err) - assert.GreaterOrEqual(t, result.Node.Vendors.TotalCount, 3) + assert.GreaterOrEqual(t, result.Node.ThirdParties.TotalCount, 3) } -func TestVendor_CreateContact(t *testing.T) { +func TestThirdParty_CreateContact(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - vendorID := factory.CreateVendor(owner, factory.Attrs{"name": "Vendor With Contact"}) + thirdPartyID := factory.CreateThirdParty(owner, factory.Attrs{"name": "ThirdParty With Contact"}) const query = ` - mutation($input: CreateVendorContactInput!) { - createVendorContact(input: $input) { - vendorContactEdge { + mutation($input: CreateThirdPartyContactInput!) { + createThirdPartyContact(input: $input) { + thirdPartyContactEdge { node { id fullName @@ -265,33 +265,33 @@ func TestVendor_CreateContact(t *testing.T) { ` var result struct { - CreateVendorContact struct { - VendorContactEdge struct { + CreateThirdPartyContact struct { + ThirdPartyContactEdge struct { Node struct { ID string `json:"id"` FullName string `json:"fullName"` Email string `json:"email"` Role *string `json:"role"` } `json:"node"` - } `json:"vendorContactEdge"` - } `json:"createVendorContact"` + } `json:"thirdPartyContactEdge"` + } `json:"createThirdPartyContact"` } err := owner.Execute(query, map[string]any{ "input": map[string]any{ - "vendorId": vendorID, - "fullName": "John Contact", - "email": "john@vendor.com", - "role": "Account Manager", + "thirdPartyId": thirdPartyID, + "fullName": "John Contact", + "email": "john@thirdParty.com", + "role": "Account Manager", }, }, &result) require.NoError(t, err) - assert.NotEmpty(t, result.CreateVendorContact.VendorContactEdge.Node.ID) - assert.Equal(t, "John Contact", result.CreateVendorContact.VendorContactEdge.Node.FullName) + assert.NotEmpty(t, result.CreateThirdPartyContact.ThirdPartyContactEdge.Node.ID) + assert.Equal(t, "John Contact", result.CreateThirdPartyContact.ThirdPartyContactEdge.Node.FullName) } -func TestVendor_RequiredFields(t *testing.T) { +func TestThirdParty_RequiredFields(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) @@ -304,7 +304,7 @@ func TestVendor_RequiredFields(t *testing.T) { { name: "missing organizationId", input: map[string]any{ - "name": "Test Vendor", + "name": "Test ThirdParty", }, skipOrganization: true, wantErrorContains: "organizationId", @@ -326,9 +326,9 @@ func TestVendor_RequiredFields(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { query := ` - mutation CreateVendor($input: CreateVendorInput!) { - createVendor(input: $input) { - vendorEdge { + mutation CreateThirdParty($input: CreateThirdPartyInput!) { + createThirdParty(input: $input) { + thirdPartyEdge { node { id } @@ -350,7 +350,7 @@ func TestVendor_RequiredFields(t *testing.T) { } } -func TestVendor_CategoryEnum(t *testing.T) { +func TestThirdParty_CategoryEnum(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) @@ -362,7 +362,7 @@ func TestVendor_CategoryEnum(t *testing.T) { for _, category := range categories { t.Run("create with category "+category, func(t *testing.T) { - vendorID := factory.NewVendor(owner). + thirdPartyID := factory.NewThirdParty(owner). WithName("Category Test " + category). WithCategory(category). Create() @@ -370,7 +370,7 @@ func TestVendor_CategoryEnum(t *testing.T) { query := ` query($id: ID!) { node(id: $id) { - ... on Vendor { + ... on ThirdParty { id category } @@ -385,7 +385,7 @@ func TestVendor_CategoryEnum(t *testing.T) { } `json:"node"` } - err := owner.Execute(query, map[string]any{"id": vendorID}, &result) + err := owner.Execute(query, map[string]any{"id": thirdPartyID}, &result) require.NoError(t, err) require.NotNil(t, result.Node.Category) assert.Equal(t, category, *result.Node.Category) @@ -393,19 +393,19 @@ func TestVendor_CategoryEnum(t *testing.T) { } } -func TestVendor_SubResolvers(t *testing.T) { +func TestThirdParty_SubResolvers(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - vendorID := factory.NewVendor(owner). - WithName("SubResolver Test Vendor"). + thirdPartyID := factory.NewThirdParty(owner). + WithName("SubResolver Test ThirdParty"). Create() - t.Run("vendor node query", func(t *testing.T) { + t.Run("thirdParty node query", func(t *testing.T) { query := ` - query GetVendor($id: ID!) { + query GetThirdParty($id: ID!) { node(id: $id) { - ... on Vendor { + ... on ThirdParty { id name description @@ -424,17 +424,17 @@ func TestVendor_SubResolvers(t *testing.T) { } `json:"node"` } - err := owner.Execute(query, map[string]any{"id": vendorID}, &result) + err := owner.Execute(query, map[string]any{"id": thirdPartyID}, &result) require.NoError(t, err) - assert.Equal(t, vendorID, result.Node.ID) - assert.Equal(t, "SubResolver Test Vendor", result.Node.Name) + assert.Equal(t, thirdPartyID, result.Node.ID) + assert.Equal(t, "SubResolver Test ThirdParty", result.Node.Name) }) t.Run("organization sub-resolver", func(t *testing.T) { query := ` query($id: ID!) { node(id: $id) { - ... on Vendor { + ... on ThirdParty { id organization { id @@ -455,7 +455,7 @@ func TestVendor_SubResolvers(t *testing.T) { } `json:"node"` } - err := owner.Execute(query, map[string]any{"id": vendorID}, &result) + err := owner.Execute(query, map[string]any{"id": thirdPartyID}, &result) require.NoError(t, err) assert.Equal(t, owner.GetOrganizationID().String(), result.Node.Organization.ID) assert.NotEmpty(t, result.Node.Organization.Name) @@ -465,7 +465,7 @@ func TestVendor_SubResolvers(t *testing.T) { query := ` query($id: ID!) { node(id: $id) { - ... on Vendor { + ... on ThirdParty { id services(first: 10) { edges { @@ -494,7 +494,7 @@ func TestVendor_SubResolvers(t *testing.T) { } `json:"node"` } - err := owner.Execute(query, map[string]any{"id": vendorID}, &result) + err := owner.Execute(query, map[string]any{"id": thirdPartyID}, &result) require.NoError(t, err) assert.NotNil(t, result.Node.Services.Edges) }) @@ -503,7 +503,7 @@ func TestVendor_SubResolvers(t *testing.T) { query := ` query($id: ID!) { node(id: $id) { - ... on Vendor { + ... on ThirdParty { id businessOwner { id @@ -524,7 +524,7 @@ func TestVendor_SubResolvers(t *testing.T) { } `json:"node"` } - err := owner.Execute(query, map[string]any{"id": vendorID}, &result) + err := owner.Execute(query, map[string]any{"id": thirdPartyID}, &result) require.NoError(t, err) assert.Nil(t, result.Node.BusinessOwner) }) @@ -533,7 +533,7 @@ func TestVendor_SubResolvers(t *testing.T) { query := ` query($id: ID!) { node(id: $id) { - ... on Vendor { + ... on ThirdParty { id securityOwner { id @@ -554,21 +554,21 @@ func TestVendor_SubResolvers(t *testing.T) { } `json:"node"` } - err := owner.Execute(query, map[string]any{"id": vendorID}, &result) + err := owner.Execute(query, map[string]any{"id": thirdPartyID}, &result) require.NoError(t, err) assert.Nil(t, result.Node.SecurityOwner) }) } -func TestVendor_InvalidID(t *testing.T) { +func TestThirdParty_InvalidID(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) t.Run("update with invalid ID", func(t *testing.T) { query := ` - mutation UpdateVendor($input: UpdateVendorInput!) { - updateVendor(input: $input) { - vendor { + mutation UpdateThirdParty($input: UpdateThirdPartyInput!) { + updateThirdParty(input: $input) { + thirdParty { id } } @@ -587,16 +587,16 @@ func TestVendor_InvalidID(t *testing.T) { t.Run("delete with invalid ID", func(t *testing.T) { query := ` - mutation DeleteVendor($input: DeleteVendorInput!) { - deleteVendor(input: $input) { - deletedVendorId + mutation DeleteThirdParty($input: DeleteThirdPartyInput!) { + deleteThirdParty(input: $input) { + deletedThirdPartyId } } ` _, err := owner.Do(query, map[string]any{ "input": map[string]any{ - "vendorId": "invalid-id-format", + "thirdPartyId": "invalid-id-format", }, }) require.Error(t, err) @@ -605,9 +605,9 @@ func TestVendor_InvalidID(t *testing.T) { t.Run("query with non-existent ID", func(t *testing.T) { query := ` - query GetVendor($id: ID!) { + query GetThirdParty($id: ID!) { node(id: $id) { - ... on Vendor { + ... on ThirdParty { id name } @@ -622,20 +622,20 @@ func TestVendor_InvalidID(t *testing.T) { }) } -func TestVendor_OmittableDescription(t *testing.T) { +func TestThirdParty_OmittableDescription(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - vendorID := factory.NewVendor(owner). - WithName("Description Test Vendor"). + thirdPartyID := factory.NewThirdParty(owner). + WithName("Description Test ThirdParty"). WithDescription("Initial description"). Create() t.Run("set description", func(t *testing.T) { query := ` - mutation UpdateVendor($input: UpdateVendorInput!) { - updateVendor(input: $input) { - vendor { + mutation UpdateThirdParty($input: UpdateThirdPartyInput!) { + updateThirdParty(input: $input) { + thirdParty { id description } @@ -644,30 +644,30 @@ func TestVendor_OmittableDescription(t *testing.T) { ` var result struct { - UpdateVendor struct { - Vendor struct { + UpdateThirdParty struct { + ThirdParty struct { ID string `json:"id"` Description *string `json:"description"` - } `json:"vendor"` - } `json:"updateVendor"` + } `json:"thirdParty"` + } `json:"updateThirdParty"` } err := owner.Execute(query, map[string]any{ "input": map[string]any{ - "id": vendorID, + "id": thirdPartyID, "description": "Updated description", }, }, &result) require.NoError(t, err) - require.NotNil(t, result.UpdateVendor.Vendor.Description) - assert.Equal(t, "Updated description", *result.UpdateVendor.Vendor.Description) + require.NotNil(t, result.UpdateThirdParty.ThirdParty.Description) + assert.Equal(t, "Updated description", *result.UpdateThirdParty.ThirdParty.Description) }) t.Run("clear description with null", func(t *testing.T) { query := ` - mutation UpdateVendor($input: UpdateVendorInput!) { - updateVendor(input: $input) { - vendor { + mutation UpdateThirdParty($input: UpdateThirdPartyInput!) { + updateThirdParty(input: $input) { + thirdParty { id description } @@ -676,30 +676,30 @@ func TestVendor_OmittableDescription(t *testing.T) { ` var result struct { - UpdateVendor struct { - Vendor struct { + UpdateThirdParty struct { + ThirdParty struct { ID string `json:"id"` Description *string `json:"description"` - } `json:"vendor"` - } `json:"updateVendor"` + } `json:"thirdParty"` + } `json:"updateThirdParty"` } err := owner.Execute(query, map[string]any{ "input": map[string]any{ - "id": vendorID, + "id": thirdPartyID, "description": nil, }, }, &result) require.NoError(t, err) - assert.Nil(t, result.UpdateVendor.Vendor.Description) + assert.Nil(t, result.UpdateThirdParty.ThirdParty.Description) }) t.Run("update without description preserves value", func(t *testing.T) { // First set a description setQuery := ` - mutation UpdateVendor($input: UpdateVendorInput!) { - updateVendor(input: $input) { - vendor { + mutation UpdateThirdParty($input: UpdateThirdPartyInput!) { + updateThirdParty(input: $input) { + thirdParty { id } } @@ -708,7 +708,7 @@ func TestVendor_OmittableDescription(t *testing.T) { err := owner.Execute(setQuery, map[string]any{ "input": map[string]any{ - "id": vendorID, + "id": thirdPartyID, "description": "Should persist", }, }, nil) @@ -716,9 +716,9 @@ func TestVendor_OmittableDescription(t *testing.T) { // Update only name query := ` - mutation UpdateVendor($input: UpdateVendorInput!) { - updateVendor(input: $input) { - vendor { + mutation UpdateThirdParty($input: UpdateThirdPartyInput!) { + updateThirdParty(input: $input) { + thirdParty { id name description @@ -728,42 +728,42 @@ func TestVendor_OmittableDescription(t *testing.T) { ` var result struct { - UpdateVendor struct { - Vendor struct { + UpdateThirdParty struct { + ThirdParty struct { ID string `json:"id"` Name string `json:"name"` Description *string `json:"description"` - } `json:"vendor"` - } `json:"updateVendor"` + } `json:"thirdParty"` + } `json:"updateThirdParty"` } err = owner.Execute(query, map[string]any{ "input": map[string]any{ - "id": vendorID, + "id": thirdPartyID, "name": "Updated Name", }, }, &result) require.NoError(t, err) - require.NotNil(t, result.UpdateVendor.Vendor.Description) - assert.Equal(t, "Should persist", *result.UpdateVendor.Vendor.Description) + require.NotNil(t, result.UpdateThirdParty.ThirdParty.Description) + assert.Equal(t, "Should persist", *result.UpdateThirdParty.ThirdParty.Description) }) } -func TestVendor_OmittableBusinessOwner(t *testing.T) { +func TestThirdParty_OmittableBusinessOwner(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) // Create a profile for owner assignment profileID := factory.CreateUser(owner) - vendorID := factory.NewVendor(owner). - WithName("BusinessOwner Test Vendor"). + thirdPartyID := factory.NewThirdParty(owner). + WithName("BusinessOwner Test ThirdParty"). Create() t.Run("set business owner", func(t *testing.T) { query := ` - mutation UpdateVendor($input: UpdateVendorInput!) { - updateVendor(input: $input) { - vendor { + mutation UpdateThirdParty($input: UpdateThirdPartyInput!) { + updateThirdParty(input: $input) { + thirdParty { id businessOwner { id @@ -775,32 +775,32 @@ func TestVendor_OmittableBusinessOwner(t *testing.T) { ` var result struct { - UpdateVendor struct { - Vendor struct { + UpdateThirdParty struct { + ThirdParty struct { ID string `json:"id"` BusinessOwner struct { ID string `json:"id"` FullName string `json:"fullName"` } `json:"businessOwner"` - } `json:"vendor"` - } `json:"updateVendor"` + } `json:"thirdParty"` + } `json:"updateThirdParty"` } err := owner.Execute(query, map[string]any{ "input": map[string]any{ - "id": vendorID, + "id": thirdPartyID, "businessOwnerId": profileID, }, }, &result) require.NoError(t, err) - assert.Equal(t, profileID, result.UpdateVendor.Vendor.BusinessOwner.ID) + assert.Equal(t, profileID, result.UpdateThirdParty.ThirdParty.BusinessOwner.ID) }) t.Run("clear business owner with null", func(t *testing.T) { query := ` - mutation UpdateVendor($input: UpdateVendorInput!) { - updateVendor(input: $input) { - vendor { + mutation UpdateThirdParty($input: UpdateThirdPartyInput!) { + updateThirdParty(input: $input) { + thirdParty { id businessOwner { id @@ -811,40 +811,40 @@ func TestVendor_OmittableBusinessOwner(t *testing.T) { ` var result struct { - UpdateVendor struct { - Vendor struct { + UpdateThirdParty struct { + ThirdParty struct { ID string `json:"id"` BusinessOwner *struct { ID string `json:"id"` } `json:"businessOwner"` - } `json:"vendor"` - } `json:"updateVendor"` + } `json:"thirdParty"` + } `json:"updateThirdParty"` } err := owner.Execute(query, map[string]any{ "input": map[string]any{ - "id": vendorID, + "id": thirdPartyID, "businessOwnerId": nil, }, }, &result) require.NoError(t, err) - assert.Nil(t, result.UpdateVendor.Vendor.BusinessOwner) + assert.Nil(t, result.UpdateThirdParty.ThirdParty.BusinessOwner) }) } -func TestVendor_OmittableSecurityOwner(t *testing.T) { +func TestThirdParty_OmittableSecurityOwner(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) // Create a profile for owner assignment profileID := factory.CreateUser(owner) - vendorID := factory.NewVendor(owner).WithName("SecurityOwner Test Vendor").Create() + thirdPartyID := factory.NewThirdParty(owner).WithName("SecurityOwner Test ThirdParty").Create() t.Run("set security owner", func(t *testing.T) { query := ` - mutation UpdateVendor($input: UpdateVendorInput!) { - updateVendor(input: $input) { - vendor { + mutation UpdateThirdParty($input: UpdateThirdPartyInput!) { + updateThirdParty(input: $input) { + thirdParty { id securityOwner { id @@ -856,32 +856,32 @@ func TestVendor_OmittableSecurityOwner(t *testing.T) { ` var result struct { - UpdateVendor struct { - Vendor struct { + UpdateThirdParty struct { + ThirdParty struct { ID string `json:"id"` SecurityOwner struct { ID string `json:"id"` FullName string `json:"fullName"` } `json:"securityOwner"` - } `json:"vendor"` - } `json:"updateVendor"` + } `json:"thirdParty"` + } `json:"updateThirdParty"` } err := owner.Execute(query, map[string]any{ "input": map[string]any{ - "id": vendorID, + "id": thirdPartyID, "securityOwnerId": profileID, }, }, &result) require.NoError(t, err) - assert.Equal(t, profileID, result.UpdateVendor.Vendor.SecurityOwner.ID) + assert.Equal(t, profileID, result.UpdateThirdParty.ThirdParty.SecurityOwner.ID) }) t.Run("clear security owner with null", func(t *testing.T) { query := ` - mutation UpdateVendor($input: UpdateVendorInput!) { - updateVendor(input: $input) { - vendor { + mutation UpdateThirdParty($input: UpdateThirdPartyInput!) { + updateThirdParty(input: $input) { + thirdParty { id securityOwner { id @@ -892,41 +892,41 @@ func TestVendor_OmittableSecurityOwner(t *testing.T) { ` var result struct { - UpdateVendor struct { - Vendor struct { + UpdateThirdParty struct { + ThirdParty struct { ID string `json:"id"` SecurityOwner *struct { ID string `json:"id"` } `json:"securityOwner"` - } `json:"vendor"` - } `json:"updateVendor"` + } `json:"thirdParty"` + } `json:"updateThirdParty"` } err := owner.Execute(query, map[string]any{ "input": map[string]any{ - "id": vendorID, + "id": thirdPartyID, "securityOwnerId": nil, }, }, &result) require.NoError(t, err) - assert.Nil(t, result.UpdateVendor.Vendor.SecurityOwner) + assert.Nil(t, result.UpdateThirdParty.ThirdParty.SecurityOwner) }) } -func TestVendor_OmittableWebsiteUrl(t *testing.T) { +func TestThirdParty_OmittableWebsiteUrl(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - vendorID := factory.NewVendor(owner). - WithName("WebsiteUrl Test Vendor"). + thirdPartyID := factory.NewThirdParty(owner). + WithName("WebsiteUrl Test ThirdParty"). WithWebsiteUrl("https://example.com"). Create() t.Run("set websiteUrl", func(t *testing.T) { query := ` - mutation UpdateVendor($input: UpdateVendorInput!) { - updateVendor(input: $input) { - vendor { + mutation UpdateThirdParty($input: UpdateThirdPartyInput!) { + updateThirdParty(input: $input) { + thirdParty { id websiteUrl } @@ -935,30 +935,30 @@ func TestVendor_OmittableWebsiteUrl(t *testing.T) { ` var result struct { - UpdateVendor struct { - Vendor struct { + UpdateThirdParty struct { + ThirdParty struct { ID string `json:"id"` WebsiteUrl *string `json:"websiteUrl"` - } `json:"vendor"` - } `json:"updateVendor"` + } `json:"thirdParty"` + } `json:"updateThirdParty"` } err := owner.Execute(query, map[string]any{ "input": map[string]any{ - "id": vendorID, + "id": thirdPartyID, "websiteUrl": "https://updated.example.com", }, }, &result) require.NoError(t, err) - require.NotNil(t, result.UpdateVendor.Vendor.WebsiteUrl) - assert.Equal(t, "https://updated.example.com", *result.UpdateVendor.Vendor.WebsiteUrl) + require.NotNil(t, result.UpdateThirdParty.ThirdParty.WebsiteUrl) + assert.Equal(t, "https://updated.example.com", *result.UpdateThirdParty.ThirdParty.WebsiteUrl) }) t.Run("clear websiteUrl with null", func(t *testing.T) { query := ` - mutation UpdateVendor($input: UpdateVendorInput!) { - updateVendor(input: $input) { - vendor { + mutation UpdateThirdParty($input: UpdateThirdPartyInput!) { + updateThirdParty(input: $input) { + thirdParty { id websiteUrl } @@ -967,38 +967,38 @@ func TestVendor_OmittableWebsiteUrl(t *testing.T) { ` var result struct { - UpdateVendor struct { - Vendor struct { + UpdateThirdParty struct { + ThirdParty struct { ID string `json:"id"` WebsiteUrl *string `json:"websiteUrl"` - } `json:"vendor"` - } `json:"updateVendor"` + } `json:"thirdParty"` + } `json:"updateThirdParty"` } err := owner.Execute(query, map[string]any{ "input": map[string]any{ - "id": vendorID, + "id": thirdPartyID, "websiteUrl": nil, }, }, &result) require.NoError(t, err) - assert.Nil(t, result.UpdateVendor.Vendor.WebsiteUrl) + assert.Nil(t, result.UpdateThirdParty.ThirdParty.WebsiteUrl) }) } -// TestVendor_Assess exercises the assessVendor mutation through authorization +// TestThirdParty_Assess exercises the assessThirdParty mutation through authorization // and tenant-isolation paths without running the real LLM/browser pipeline. -// The e2e config deliberately omits `llm.vendor-assessor.provider`, so an -// authorized call reaches DisabledVendorAssessor and surfaces a stable +// The e2e config deliberately omits `llm.third-party-assessor.provider`, so an +// authorized call reaches DisabledThirdPartyAssessor and surfaces a stable // UNAVAILABLE error. Happy-path payload shape is covered by unit tests in // pkg/probo. -func TestVendor_Assess(t *testing.T) { +func TestThirdParty_Assess(t *testing.T) { t.Parallel() const query = ` - mutation AssessVendor($input: AssessVendorInput!) { - assessVendor(input: $input) { - vendor { + mutation AssessThirdParty($input: AssessThirdPartyInput!) { + assessThirdParty(input: $input) { + thirdParty { id } } @@ -1006,24 +1006,24 @@ func TestVendor_Assess(t *testing.T) { ` type resultShape struct { - AssessVendor struct { - Vendor struct { + AssessThirdParty struct { + ThirdParty struct { ID string `json:"id"` - } `json:"vendor"` - } `json:"assessVendor"` + } `json:"thirdParty"` + } `json:"assessThirdParty"` } t.Run("owner call surfaces the disabled error", func(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - vendorID := factory.NewVendor(owner).WithName("Unconfigured assess").Create() + thirdPartyID := factory.NewThirdParty(owner).WithName("Unconfigured assess").Create() var result resultShape err := owner.Execute(query, map[string]any{ "input": map[string]any{ - "id": vendorID, - "websiteUrl": "https://vendor.example.com", + "id": thirdPartyID, + "websiteUrl": "https://thirdParty.example.com", }, }, &result) testutil.RequireErrorCode(t, err, "UNAVAILABLE") @@ -1034,62 +1034,62 @@ func TestVendor_Assess(t *testing.T) { owner := testutil.NewClient(t, testutil.RoleOwner) admin := testutil.NewClientInOrg(t, testutil.RoleAdmin, owner) - vendorID := factory.NewVendor(owner).WithName("Admin-assessed vendor").Create() + thirdPartyID := factory.NewThirdParty(owner).WithName("Admin-assessed thirdParty").Create() var result resultShape err := admin.Execute(query, map[string]any{ "input": map[string]any{ - "id": vendorID, + "id": thirdPartyID, "websiteUrl": "https://admin.example.com", }, }, &result) testutil.RequireErrorCode(t, err, "UNAVAILABLE") }) - t.Run("viewer cannot assess a vendor", func(t *testing.T) { + t.Run("viewer cannot assess a thirdParty", func(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) viewer := testutil.NewClientInOrg(t, testutil.RoleViewer, owner) - vendorID := factory.NewVendor(owner).WithName("Viewer attempt").Create() + thirdPartyID := factory.NewThirdParty(owner).WithName("Viewer attempt").Create() var result resultShape err := viewer.Execute(query, map[string]any{ "input": map[string]any{ - "id": vendorID, + "id": thirdPartyID, "websiteUrl": "https://viewer.example.com", }, }, &result) testutil.RequireForbiddenError(t, err) }) - t.Run("cannot assess vendor from another organization", func(t *testing.T) { + t.Run("cannot assess thirdParty from another organization", func(t *testing.T) { t.Parallel() org1Owner := testutil.NewClient(t, testutil.RoleOwner) org2Owner := testutil.NewClient(t, testutil.RoleOwner) - vendorID := factory.NewVendor(org1Owner).WithName("Org1 vendor").Create() + thirdPartyID := factory.NewThirdParty(org1Owner).WithName("Org1 thirdParty").Create() var result resultShape err := org2Owner.Execute(query, map[string]any{ "input": map[string]any{ - "id": vendorID, + "id": thirdPartyID, "websiteUrl": "https://cross-tenant.example.com", }, }, &result) - require.Error(t, err, "vendor assess must not cross tenant boundaries") + require.Error(t, err, "thirdParty assess must not cross tenant boundaries") }) t.Run("procedure is accepted on the input", func(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) - vendorID := factory.NewVendor(owner).WithName("Procedure test").Create() + thirdPartyID := factory.NewThirdParty(owner).WithName("Procedure test").Create() var result resultShape err := owner.Execute(query, map[string]any{ "input": map[string]any{ - "id": vendorID, + "id": thirdPartyID, "websiteUrl": "https://procedure.example.com", "procedure": "Focus on SOC 2 controls and data residency", }, @@ -1098,19 +1098,19 @@ func TestVendor_Assess(t *testing.T) { }) } -func TestVendor_TenantIsolation(t *testing.T) { +func TestThirdParty_TenantIsolation(t *testing.T) { t.Parallel() org1Owner := testutil.NewClient(t, testutil.RoleOwner) org2Owner := testutil.NewClient(t, testutil.RoleOwner) - vendorID := factory.NewVendor(org1Owner).WithName("Org1 Vendor").Create() + thirdPartyID := factory.NewThirdParty(org1Owner).WithName("Org1 ThirdParty").Create() - t.Run("cannot read vendor from another organization", func(t *testing.T) { + t.Run("cannot read thirdParty from another organization", func(t *testing.T) { query := ` query($id: ID!) { node(id: $id) { - ... on Vendor { + ... on ThirdParty { id name } @@ -1125,42 +1125,42 @@ func TestVendor_TenantIsolation(t *testing.T) { } `json:"node"` } - err := org2Owner.Execute(query, map[string]any{"id": vendorID}, &result) - testutil.AssertNodeNotAccessible(t, err, result.Node == nil, "vendor") + err := org2Owner.Execute(query, map[string]any{"id": thirdPartyID}, &result) + testutil.AssertNodeNotAccessible(t, err, result.Node == nil, "thirdParty") }) - t.Run("cannot update vendor from another organization", func(t *testing.T) { + t.Run("cannot update thirdParty from another organization", func(t *testing.T) { query := ` - mutation UpdateVendor($input: UpdateVendorInput!) { - updateVendor(input: $input) { - vendor { id } + mutation UpdateThirdParty($input: UpdateThirdPartyInput!) { + updateThirdParty(input: $input) { + thirdParty { id } } } ` _, err := org2Owner.Do(query, map[string]any{ "input": map[string]any{ - "id": vendorID, - "name": "Hijacked Vendor", + "id": thirdPartyID, + "name": "Hijacked ThirdParty", }, }) - require.Error(t, err, "Should not be able to update vendor from another org") + require.Error(t, err, "Should not be able to update thirdParty from another org") }) - t.Run("cannot delete vendor from another organization", func(t *testing.T) { + t.Run("cannot delete thirdParty from another organization", func(t *testing.T) { query := ` - mutation DeleteVendor($input: DeleteVendorInput!) { - deleteVendor(input: $input) { - deletedVendorId + mutation DeleteThirdParty($input: DeleteThirdPartyInput!) { + deleteThirdParty(input: $input) { + deletedThirdPartyId } } ` _, err := org2Owner.Do(query, map[string]any{ "input": map[string]any{ - "vendorId": vendorID, + "thirdPartyId": thirdPartyID, }, }) - require.Error(t, err, "Should not be able to delete vendor from another org") + require.Error(t, err, "Should not be able to delete thirdParty from another org") }) } diff --git a/e2e/internal/factory/factory.go b/e2e/internal/factory/factory.go index baffd5f33..77ab29883 100644 --- a/e2e/internal/factory/factory.go +++ b/e2e/internal/factory/factory.go @@ -142,7 +142,7 @@ func CreateUser(c *testutil.Client, attrs ...Attrs) string { return result.CreateUser.ProfileEdge.Node.ID } -func CreateVendor(c *testutil.Client, attrs ...Attrs) string { +func CreateThirdParty(c *testutil.Client, attrs ...Attrs) string { c.T.Helper() var a Attrs @@ -151,9 +151,9 @@ func CreateVendor(c *testutil.Client, attrs ...Attrs) string { } const query = ` - mutation($input: CreateVendorInput!) { - createVendor(input: $input) { - vendorEdge { + mutation($input: CreateThirdPartyInput!) { + createThirdParty(input: $input) { + thirdPartyEdge { node { id } } } @@ -162,7 +162,7 @@ func CreateVendor(c *testutil.Client, attrs ...Attrs) string { input := map[string]any{ "organizationId": c.GetOrganizationID().String(), - "name": a.getString("name", SafeName("Vendor")), + "name": a.getString("name", SafeName("ThirdParty")), } if desc := a.getStringPtr("description"); desc != nil { input["description"] = *desc @@ -175,19 +175,19 @@ func CreateVendor(c *testutil.Client, attrs ...Attrs) string { } var result struct { - CreateVendor struct { - VendorEdge struct { + CreateThirdParty struct { + ThirdPartyEdge struct { Node struct { ID string `json:"id"` } `json:"node"` - } `json:"vendorEdge"` - } `json:"createVendor"` + } `json:"thirdPartyEdge"` + } `json:"createThirdParty"` } err := c.Execute(query, map[string]any{"input": input}, &result) - require.NoError(c.T, err, "createVendor mutation failed") + require.NoError(c.T, err, "createThirdParty mutation failed") - return result.CreateVendor.VendorEdge.Node.ID + return result.CreateThirdParty.ThirdPartyEdge.Node.ID } func CreateFramework(c *testutil.Client, attrs ...Attrs) string { @@ -414,37 +414,37 @@ func CreateRisk(c *testutil.Client, attrs ...Attrs) string { return result.CreateRisk.RiskEdge.Node.ID } -type VendorBuilder struct { +type ThirdPartyBuilder struct { client *testutil.Client attrs Attrs } -func NewVendor(c *testutil.Client) *VendorBuilder { - return &VendorBuilder{client: c, attrs: Attrs{}} +func NewThirdParty(c *testutil.Client) *ThirdPartyBuilder { + return &ThirdPartyBuilder{client: c, attrs: Attrs{}} } -func (b *VendorBuilder) WithName(name string) *VendorBuilder { +func (b *ThirdPartyBuilder) WithName(name string) *ThirdPartyBuilder { b.attrs["name"] = name return b } -func (b *VendorBuilder) WithDescription(desc string) *VendorBuilder { +func (b *ThirdPartyBuilder) WithDescription(desc string) *ThirdPartyBuilder { b.attrs["description"] = desc return b } -func (b *VendorBuilder) WithWebsiteUrl(url string) *VendorBuilder { +func (b *ThirdPartyBuilder) WithWebsiteUrl(url string) *ThirdPartyBuilder { b.attrs["websiteUrl"] = url return b } -func (b *VendorBuilder) WithCategory(category string) *VendorBuilder { +func (b *ThirdPartyBuilder) WithCategory(category string) *ThirdPartyBuilder { b.attrs["category"] = category return b } -func (b *VendorBuilder) Create() string { - return CreateVendor(b.client, b.attrs) +func (b *ThirdPartyBuilder) Create() string { + return CreateThirdParty(b.client, b.attrs) } type FrameworkBuilder struct { diff --git a/e2e/mcp/audit_test.go b/e2e/mcp/audit_test.go index cda7603f5..d50499c2e 100644 --- a/e2e/mcp/audit_test.go +++ b/e2e/mcp/audit_test.go @@ -95,7 +95,7 @@ func TestMCP_AuditLog(t *testing.T) { orgID := owner.GetOrganizationID().String() // Creating something generates audit log entries - factory.CreateVendor(owner) + factory.CreateThirdParty(owner) var listResult struct { AuditLogEntries []struct { diff --git a/e2e/mcp/third_party_contact_test.go b/e2e/mcp/third_party_contact_test.go new file mode 100644 index 000000000..41105c075 --- /dev/null +++ b/e2e/mcp/third_party_contact_test.go @@ -0,0 +1,142 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package mcp_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.probo.inc/probo/e2e/internal/factory" + "go.probo.inc/probo/e2e/internal/testutil" +) + +type thirdPartyContact struct { + ID string `json:"id"` + Name string `json:"name"` + Email *string `json:"email"` + Phone *string `json:"phone"` + Role *string `json:"role"` +} + +func TestMCP_AddThirdPartyContact(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + mc := testutil.NewMCPClient(t, owner) + thirdPartyID := factory.CreateThirdParty(owner) + + var result struct { + ThirdPartyContact thirdPartyContact `json:"thirdPartyContact"` + } + mc.CallToolInto("addThirdPartyContact", map[string]any{ + "thirdPartyId": thirdPartyID, + "name": "Alice Smith", + "email": "alice@example.com", + "phone": "+1-555-0100", + "role": "Account Manager", + }, &result) + + assert.NotEmpty(t, result.ThirdPartyContact.ID) + assert.Equal(t, "Alice Smith", result.ThirdPartyContact.Name) +} + +func TestMCP_UpdateThirdPartyContact(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + mc := testutil.NewMCPClient(t, owner) + thirdPartyID := factory.CreateThirdParty(owner) + + // Create + var addResult struct { + ThirdPartyContact thirdPartyContact `json:"thirdPartyContact"` + } + mc.CallToolInto("addThirdPartyContact", map[string]any{ + "thirdPartyId": thirdPartyID, + "name": "Bob Jones", + "email": "bob@example.com", + }, &addResult) + require.NotEmpty(t, addResult.ThirdPartyContact.ID) + + // Update + var updateResult struct { + ThirdPartyContact thirdPartyContact `json:"thirdPartyContact"` + } + mc.CallToolInto("updateThirdPartyContact", map[string]any{ + "id": addResult.ThirdPartyContact.ID, + "name": "Robert Jones", + "role": "CTO", + }, &updateResult) + + assert.Equal(t, addResult.ThirdPartyContact.ID, updateResult.ThirdPartyContact.ID) + assert.Equal(t, "Robert Jones", updateResult.ThirdPartyContact.Name) +} + +func TestMCP_DeleteThirdPartyContact(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + mc := testutil.NewMCPClient(t, owner) + thirdPartyID := factory.CreateThirdParty(owner) + + // Create + var addResult struct { + ThirdPartyContact thirdPartyContact `json:"thirdPartyContact"` + } + mc.CallToolInto("addThirdPartyContact", map[string]any{ + "thirdPartyId": thirdPartyID, + "name": "Contact to delete", + }, &addResult) + require.NotEmpty(t, addResult.ThirdPartyContact.ID) + + // Delete + var deleteResult struct { + DeletedThirdPartyContactID string `json:"deletedThirdPartyContactId"` + } + mc.CallToolInto("deleteThirdPartyContact", map[string]any{ + "id": addResult.ThirdPartyContact.ID, + }, &deleteResult) + + assert.Equal(t, addResult.ThirdPartyContact.ID, deleteResult.DeletedThirdPartyContactID) +} + +func TestMCP_ListThirdPartyContacts(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + mc := testutil.NewMCPClient(t, owner) + thirdPartyID := factory.CreateThirdParty(owner) + + // Create contacts + for i := range 3 { + var result struct { + ThirdPartyContact thirdPartyContact `json:"thirdPartyContact"` + } + mc.CallToolInto("addThirdPartyContact", map[string]any{ + "thirdPartyId": thirdPartyID, + "name": factory.SafeName("Contact"), + "email": factory.SafeEmail(), + }, &result) + require.NotEmpty(t, result.ThirdPartyContact.ID) + _ = i + } + + // List + var listResult struct { + ThirdPartyContacts []thirdPartyContact `json:"thirdPartyContacts"` + } + mc.CallToolInto("listThirdPartyContacts", map[string]any{ + "thirdPartyId": thirdPartyID, + }, &listResult) + + assert.GreaterOrEqual(t, len(listResult.ThirdPartyContacts), 3) +} diff --git a/e2e/mcp/third_party_service_test.go b/e2e/mcp/third_party_service_test.go new file mode 100644 index 000000000..82c6a7027 --- /dev/null +++ b/e2e/mcp/third_party_service_test.go @@ -0,0 +1,135 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package mcp_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.probo.inc/probo/e2e/internal/factory" + "go.probo.inc/probo/e2e/internal/testutil" +) + +type thirdPartyService struct { + ID string `json:"id"` + Name string `json:"name"` + Description *string `json:"description"` +} + +func TestMCP_AddThirdPartyService(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + mc := testutil.NewMCPClient(t, owner) + thirdPartyID := factory.CreateThirdParty(owner) + + var result struct { + ThirdPartyService thirdPartyService `json:"thirdPartyService"` + } + mc.CallToolInto("addThirdPartyService", map[string]any{ + "thirdPartyId": thirdPartyID, + "name": "Cloud Storage", + "description": "Object storage service", + }, &result) + + assert.NotEmpty(t, result.ThirdPartyService.ID) + assert.Equal(t, "Cloud Storage", result.ThirdPartyService.Name) +} + +func TestMCP_UpdateThirdPartyService(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + mc := testutil.NewMCPClient(t, owner) + thirdPartyID := factory.CreateThirdParty(owner) + + // Create + var addResult struct { + ThirdPartyService thirdPartyService `json:"thirdPartyService"` + } + mc.CallToolInto("addThirdPartyService", map[string]any{ + "thirdPartyId": thirdPartyID, + "name": "Original Service", + }, &addResult) + require.NotEmpty(t, addResult.ThirdPartyService.ID) + + // Update + var updateResult struct { + ThirdPartyService thirdPartyService `json:"thirdPartyService"` + } + mc.CallToolInto("updateThirdPartyService", map[string]any{ + "id": addResult.ThirdPartyService.ID, + "name": "Updated Service", + }, &updateResult) + + assert.Equal(t, addResult.ThirdPartyService.ID, updateResult.ThirdPartyService.ID) + assert.Equal(t, "Updated Service", updateResult.ThirdPartyService.Name) +} + +func TestMCP_DeleteThirdPartyService(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + mc := testutil.NewMCPClient(t, owner) + thirdPartyID := factory.CreateThirdParty(owner) + + // Create + var addResult struct { + ThirdPartyService thirdPartyService `json:"thirdPartyService"` + } + mc.CallToolInto("addThirdPartyService", map[string]any{ + "thirdPartyId": thirdPartyID, + "name": "Service to delete", + }, &addResult) + require.NotEmpty(t, addResult.ThirdPartyService.ID) + + // Delete + var deleteResult struct { + DeletedThirdPartyServiceID string `json:"deletedThirdPartyServiceId"` + } + mc.CallToolInto("deleteThirdPartyService", map[string]any{ + "id": addResult.ThirdPartyService.ID, + }, &deleteResult) + + assert.Equal(t, addResult.ThirdPartyService.ID, deleteResult.DeletedThirdPartyServiceID) +} + +func TestMCP_ListThirdPartyServices(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + mc := testutil.NewMCPClient(t, owner) + thirdPartyID := factory.CreateThirdParty(owner) + + // Create services + for i := range 3 { + var result struct { + ThirdPartyService thirdPartyService `json:"thirdPartyService"` + } + mc.CallToolInto("addThirdPartyService", map[string]any{ + "thirdPartyId": thirdPartyID, + "name": factory.SafeName("Service"), + }, &result) + require.NotEmpty(t, result.ThirdPartyService.ID) + _ = i + } + + // List + var listResult struct { + ThirdPartyServices []thirdPartyService `json:"thirdPartyServices"` + } + mc.CallToolInto("listThirdPartyServices", map[string]any{ + "thirdPartyId": thirdPartyID, + }, &listResult) + + assert.GreaterOrEqual(t, len(listResult.ThirdPartyServices), 3) +} diff --git a/e2e/mcp/vendor_test.go b/e2e/mcp/third_party_test.go similarity index 63% rename from e2e/mcp/vendor_test.go rename to e2e/mcp/third_party_test.go index 35c1efdb5..080e11267 100644 --- a/e2e/mcp/vendor_test.go +++ b/e2e/mcp/third_party_test.go @@ -23,7 +23,7 @@ import ( "go.probo.inc/probo/e2e/internal/testutil" ) -func TestMCP_Vendor_CRUD(t *testing.T) { +func TestMCP_ThirdParty_CRUD(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) mc := testutil.NewMCPClient(t, owner) @@ -31,67 +31,67 @@ func TestMCP_Vendor_CRUD(t *testing.T) { // Create var addResult struct { - Vendor struct { + ThirdParty struct { ID string `json:"id"` Name string `json:"name"` - } `json:"vendor"` + } `json:"third_party"` } - name := factory.SafeName("Vendor") - mc.CallToolInto("addVendor", map[string]any{ + name := factory.SafeName("ThirdParty") + mc.CallToolInto("addThirdParty", map[string]any{ "organizationId": orgID, "name": name, }, &addResult) - require.NotEmpty(t, addResult.Vendor.ID) - assert.Equal(t, name, addResult.Vendor.Name) + require.NotEmpty(t, addResult.ThirdParty.ID) + assert.Equal(t, name, addResult.ThirdParty.Name) // Update var updateResult struct { - Vendor struct { + ThirdParty struct { ID string `json:"id"` Name string `json:"name"` - } `json:"vendor"` + } `json:"third_party"` } - mc.CallToolInto("updateVendor", map[string]any{ - "id": addResult.Vendor.ID, - "name": "Updated Vendor", + mc.CallToolInto("updateThirdParty", map[string]any{ + "id": addResult.ThirdParty.ID, + "name": "Updated ThirdParty", }, &updateResult) - assert.Equal(t, "Updated Vendor", updateResult.Vendor.Name) + assert.Equal(t, "Updated ThirdParty", updateResult.ThirdParty.Name) // List var listResult struct { - Vendors []struct { + ThirdParties []struct { ID string `json:"id"` - } `json:"vendors"` + } `json:"third_parties"` } - mc.CallToolInto("listVendors", map[string]any{ + mc.CallToolInto("listThirdParties", map[string]any{ "organizationId": orgID, }, &listResult) - assert.NotEmpty(t, listResult.Vendors) + assert.NotEmpty(t, listResult.ThirdParties) // Delete var deleteResult struct { - DeletedVendorID string `json:"deletedVendorId"` + DeletedThirdPartyID string `json:"deletedThirdPartyId"` } - mc.CallToolInto("deleteVendor", map[string]any{ - "id": addResult.Vendor.ID, + mc.CallToolInto("deleteThirdParty", map[string]any{ + "id": addResult.ThirdParty.ID, }, &deleteResult) - assert.Equal(t, addResult.Vendor.ID, deleteResult.DeletedVendorID) + assert.Equal(t, addResult.ThirdParty.ID, deleteResult.DeletedThirdPartyID) - // Update deleted vendor returns sanitized not-found error - msg := mc.CallToolExpectToolError("updateVendor", map[string]any{ - "id": addResult.Vendor.ID, + // Update deleted thirdParty returns sanitized not-found error + msg := mc.CallToolExpectToolError("updateThirdParty", map[string]any{ + "id": addResult.ThirdParty.ID, "name": "Should Fail", }) assert.Equal(t, "resource not found", msg) } -func TestMCP_Vendor_ValidationError(t *testing.T) { +func TestMCP_ThirdParty_ValidationError(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) mc := testutil.NewMCPClient(t, owner) orgID := owner.GetOrganizationID().String() - msg := mc.CallToolExpectToolError("addVendor", map[string]any{ + msg := mc.CallToolExpectToolError("addThirdParty", map[string]any{ "organizationId": orgID, "name": "", }) @@ -100,16 +100,16 @@ func TestMCP_Vendor_ValidationError(t *testing.T) { assert.NotContains(t, msg, "sql:") } -func TestMCP_Vendor_PermissionDenied(t *testing.T) { +func TestMCP_ThirdParty_PermissionDenied(t *testing.T) { t.Parallel() owner := testutil.NewClient(t, testutil.RoleOwner) orgID := owner.GetOrganizationID().String() viewer := testutil.NewClientInOrg(t, testutil.RoleViewer, owner) viewerMC := testutil.NewMCPClient(t, viewer) - msg := viewerMC.CallToolExpectToolError("addVendor", map[string]any{ + msg := viewerMC.CallToolExpectToolError("addThirdParty", map[string]any{ "organizationId": orgID, - "name": factory.SafeName("Vendor"), + "name": factory.SafeName("ThirdParty"), }) assert.Contains(t, msg, "permission denied") assert.NotContains(t, msg, "pq:") diff --git a/e2e/mcp/vendor_contact_test.go b/e2e/mcp/vendor_contact_test.go deleted file mode 100644 index 2f545d37d..000000000 --- a/e2e/mcp/vendor_contact_test.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) 2025-2026 Probo Inc . -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -package mcp_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.probo.inc/probo/e2e/internal/factory" - "go.probo.inc/probo/e2e/internal/testutil" -) - -type vendorContact struct { - ID string `json:"id"` - Name string `json:"name"` - Email *string `json:"email"` - Phone *string `json:"phone"` - Role *string `json:"role"` -} - -func TestMCP_AddVendorContact(t *testing.T) { - t.Parallel() - owner := testutil.NewClient(t, testutil.RoleOwner) - mc := testutil.NewMCPClient(t, owner) - vendorID := factory.CreateVendor(owner) - - var result struct { - VendorContact vendorContact `json:"vendorContact"` - } - mc.CallToolInto("addVendorContact", map[string]any{ - "vendorId": vendorID, - "name": "Alice Smith", - "email": "alice@example.com", - "phone": "+1-555-0100", - "role": "Account Manager", - }, &result) - - assert.NotEmpty(t, result.VendorContact.ID) - assert.Equal(t, "Alice Smith", result.VendorContact.Name) -} - -func TestMCP_UpdateVendorContact(t *testing.T) { - t.Parallel() - owner := testutil.NewClient(t, testutil.RoleOwner) - mc := testutil.NewMCPClient(t, owner) - vendorID := factory.CreateVendor(owner) - - // Create - var addResult struct { - VendorContact vendorContact `json:"vendorContact"` - } - mc.CallToolInto("addVendorContact", map[string]any{ - "vendorId": vendorID, - "name": "Bob Jones", - "email": "bob@example.com", - }, &addResult) - require.NotEmpty(t, addResult.VendorContact.ID) - - // Update - var updateResult struct { - VendorContact vendorContact `json:"vendorContact"` - } - mc.CallToolInto("updateVendorContact", map[string]any{ - "id": addResult.VendorContact.ID, - "name": "Robert Jones", - "role": "CTO", - }, &updateResult) - - assert.Equal(t, addResult.VendorContact.ID, updateResult.VendorContact.ID) - assert.Equal(t, "Robert Jones", updateResult.VendorContact.Name) -} - -func TestMCP_DeleteVendorContact(t *testing.T) { - t.Parallel() - owner := testutil.NewClient(t, testutil.RoleOwner) - mc := testutil.NewMCPClient(t, owner) - vendorID := factory.CreateVendor(owner) - - // Create - var addResult struct { - VendorContact vendorContact `json:"vendorContact"` - } - mc.CallToolInto("addVendorContact", map[string]any{ - "vendorId": vendorID, - "name": "Contact to delete", - }, &addResult) - require.NotEmpty(t, addResult.VendorContact.ID) - - // Delete - var deleteResult struct { - DeletedVendorContactID string `json:"deletedVendorContactId"` - } - mc.CallToolInto("deleteVendorContact", map[string]any{ - "id": addResult.VendorContact.ID, - }, &deleteResult) - - assert.Equal(t, addResult.VendorContact.ID, deleteResult.DeletedVendorContactID) -} - -func TestMCP_ListVendorContacts(t *testing.T) { - t.Parallel() - owner := testutil.NewClient(t, testutil.RoleOwner) - mc := testutil.NewMCPClient(t, owner) - vendorID := factory.CreateVendor(owner) - - // Create contacts - for i := range 3 { - var result struct { - VendorContact vendorContact `json:"vendorContact"` - } - mc.CallToolInto("addVendorContact", map[string]any{ - "vendorId": vendorID, - "name": factory.SafeName("Contact"), - "email": factory.SafeEmail(), - }, &result) - require.NotEmpty(t, result.VendorContact.ID) - _ = i - } - - // List - var listResult struct { - VendorContacts []vendorContact `json:"vendorContacts"` - } - mc.CallToolInto("listVendorContacts", map[string]any{ - "vendorId": vendorID, - }, &listResult) - - assert.GreaterOrEqual(t, len(listResult.VendorContacts), 3) -} diff --git a/e2e/mcp/vendor_service_test.go b/e2e/mcp/vendor_service_test.go deleted file mode 100644 index 84aeb3b67..000000000 --- a/e2e/mcp/vendor_service_test.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) 2025-2026 Probo Inc . -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -package mcp_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.probo.inc/probo/e2e/internal/factory" - "go.probo.inc/probo/e2e/internal/testutil" -) - -type vendorService struct { - ID string `json:"id"` - Name string `json:"name"` - Description *string `json:"description"` -} - -func TestMCP_AddVendorService(t *testing.T) { - t.Parallel() - owner := testutil.NewClient(t, testutil.RoleOwner) - mc := testutil.NewMCPClient(t, owner) - vendorID := factory.CreateVendor(owner) - - var result struct { - VendorService vendorService `json:"vendorService"` - } - mc.CallToolInto("addVendorService", map[string]any{ - "vendorId": vendorID, - "name": "Cloud Storage", - "description": "Object storage service", - }, &result) - - assert.NotEmpty(t, result.VendorService.ID) - assert.Equal(t, "Cloud Storage", result.VendorService.Name) -} - -func TestMCP_UpdateVendorService(t *testing.T) { - t.Parallel() - owner := testutil.NewClient(t, testutil.RoleOwner) - mc := testutil.NewMCPClient(t, owner) - vendorID := factory.CreateVendor(owner) - - // Create - var addResult struct { - VendorService vendorService `json:"vendorService"` - } - mc.CallToolInto("addVendorService", map[string]any{ - "vendorId": vendorID, - "name": "Original Service", - }, &addResult) - require.NotEmpty(t, addResult.VendorService.ID) - - // Update - var updateResult struct { - VendorService vendorService `json:"vendorService"` - } - mc.CallToolInto("updateVendorService", map[string]any{ - "id": addResult.VendorService.ID, - "name": "Updated Service", - }, &updateResult) - - assert.Equal(t, addResult.VendorService.ID, updateResult.VendorService.ID) - assert.Equal(t, "Updated Service", updateResult.VendorService.Name) -} - -func TestMCP_DeleteVendorService(t *testing.T) { - t.Parallel() - owner := testutil.NewClient(t, testutil.RoleOwner) - mc := testutil.NewMCPClient(t, owner) - vendorID := factory.CreateVendor(owner) - - // Create - var addResult struct { - VendorService vendorService `json:"vendorService"` - } - mc.CallToolInto("addVendorService", map[string]any{ - "vendorId": vendorID, - "name": "Service to delete", - }, &addResult) - require.NotEmpty(t, addResult.VendorService.ID) - - // Delete - var deleteResult struct { - DeletedVendorServiceID string `json:"deletedVendorServiceId"` - } - mc.CallToolInto("deleteVendorService", map[string]any{ - "id": addResult.VendorService.ID, - }, &deleteResult) - - assert.Equal(t, addResult.VendorService.ID, deleteResult.DeletedVendorServiceID) -} - -func TestMCP_ListVendorServices(t *testing.T) { - t.Parallel() - owner := testutil.NewClient(t, testutil.RoleOwner) - mc := testutil.NewMCPClient(t, owner) - vendorID := factory.CreateVendor(owner) - - // Create services - for i := range 3 { - var result struct { - VendorService vendorService `json:"vendorService"` - } - mc.CallToolInto("addVendorService", map[string]any{ - "vendorId": vendorID, - "name": factory.SafeName("Service"), - }, &result) - require.NotEmpty(t, result.VendorService.ID) - _ = i - } - - // List - var listResult struct { - VendorServices []vendorService `json:"vendorServices"` - } - mc.CallToolInto("listVendorServices", map[string]any{ - "vendorId": vendorID, - }, &listResult) - - assert.GreaterOrEqual(t, len(listResult.VendorServices), 3) -} diff --git a/package-lock.json b/package-lock.json index b320c9c17..09048fa43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,8 +35,8 @@ "@probo/react-lazy": "1.0.0", "@probo/relay": "^1.0.0", "@probo/routes": "^1.0.0", + "@probo/third-parties": "0.0.1", "@probo/ui": "1.0.0", - "@probo/vendors": "0.0.1", "@tanstack/react-query": "^5.76.1", "clsx": "^2.1.1", "react": "^19.1.0", @@ -3246,6 +3246,10 @@ "resolved": "packages/routes", "link": true }, + "node_modules/@probo/third-parties": { + "resolved": "packages/third-parties", + "link": true + }, "node_modules/@probo/trust": { "resolved": "apps/trust", "link": true @@ -3258,10 +3262,6 @@ "resolved": "packages/ui", "link": true }, - "node_modules/@probo/vendors": { - "resolved": "packages/vendors", - "link": true - }, "node_modules/@radix-ui/number": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", @@ -19633,6 +19633,11 @@ "relay-runtime": "^20.1.1" } }, + "packages/third-parties": { + "name": "@probo/third-parties", + "version": "0.0.1", + "license": "CC BY-SA 4.0" + }, "packages/tsconfig": { "name": "@probo/tsconfig", "version": "0.0.1", @@ -20010,11 +20015,6 @@ "optional": true } } - }, - "packages/vendors": { - "name": "@probo/vendors", - "version": "0.0.1", - "license": "CC BY-SA 4.0" } } } diff --git a/packages/n8n-node/nodes/Probo/Probo.node.ts b/packages/n8n-node/nodes/Probo/Probo.node.ts index 78d4b60bc..e2a374274 100644 --- a/packages/n8n-node/nodes/Probo/Probo.node.ts +++ b/packages/n8n-node/nodes/Probo/Probo.node.ts @@ -196,6 +196,11 @@ export class Probo implements INodeType { value: 'task', description: 'Manage tasks', }, + { + name: 'Third Party', + value: 'thirdParty', + description: 'Manage third parties', + }, { name: 'TIA', value: 'tia', @@ -216,11 +221,6 @@ export class Probo implements INodeType { value: 'user', description: 'Manage organization users (profiles)', }, - { - name: 'Vendor', - value: 'vendor', - description: 'Manage vendors', - }, { name: 'Webhook', value: 'webhook', diff --git a/packages/n8n-node/nodes/Probo/actions/asset/create.operation.ts b/packages/n8n-node/nodes/Probo/actions/asset/create.operation.ts index 7ce0352f0..5e8c47051 100644 --- a/packages/n8n-node/nodes/Probo/actions/asset/create.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/asset/create.operation.ts @@ -111,8 +111,8 @@ export const description: INodeProperties[] = [ required: true, }, { - displayName: 'Vendor IDs', - name: 'vendorIds', + displayName: 'ThirdParty IDs', + name: 'thirdPartyIds', type: 'string', displayOptions: { show: { @@ -121,7 +121,7 @@ export const description: INodeProperties[] = [ }, }, default: '', - description: 'Comma-separated list of vendor IDs', + description: 'Comma-separated list of thirdParty IDs', }, { displayName: 'Options', @@ -144,11 +144,11 @@ export const description: INodeProperties[] = [ description: 'Whether to include owner details in the response', }, { - displayName: 'Include Vendors', - name: 'includeVendors', + displayName: 'Include ThirdParties', + name: 'includeThirdParties', type: 'boolean', default: false, - description: 'Whether to include vendors in the response', + description: 'Whether to include thirdParties in the response', }, ], }, @@ -164,10 +164,10 @@ export async function execute( const ownerId = this.getNodeParameter('ownerId', itemIndex) as string; const assetType = this.getNodeParameter('assetType', itemIndex) as string; const dataTypesStored = this.getNodeParameter('dataTypesStored', itemIndex) as string; - const vendorIdsStr = this.getNodeParameter('vendorIds', itemIndex, '') as string; + const thirdPartyIdsStr = this.getNodeParameter('thirdPartyIds', itemIndex, '') as string; const options = this.getNodeParameter('options', itemIndex, {}) as { includeOwner?: boolean; - includeVendors?: boolean; + includeThirdParties?: boolean; }; const ownerFragment = options.includeOwner @@ -178,8 +178,8 @@ export async function execute( }` : ''; - const vendorsFragment = options.includeVendors - ? `vendors(first: 100) { + const thirdPartiesFragment = options.includeThirdParties + ? `thirdParties(first: 100) { edges { node { id @@ -200,7 +200,7 @@ export async function execute( assetType dataTypesStored ${ownerFragment} - ${vendorsFragment} + ${thirdPartiesFragment} createdAt updatedAt } @@ -209,7 +209,7 @@ export async function execute( } `; - const vendorIds = vendorIdsStr ? vendorIdsStr.split(',').map((id) => id.trim()).filter(Boolean) : undefined; + const thirdPartyIds = thirdPartyIdsStr ? thirdPartyIdsStr.split(',').map((id) => id.trim()).filter(Boolean) : undefined; const variables = { input: { @@ -219,7 +219,7 @@ export async function execute( ownerId, assetType, dataTypesStored, - ...(vendorIds && vendorIds.length > 0 && { vendorIds }), + ...(thirdPartyIds && thirdPartyIds.length > 0 && { thirdPartyIds }), }, }; diff --git a/packages/n8n-node/nodes/Probo/actions/asset/get.operation.ts b/packages/n8n-node/nodes/Probo/actions/asset/get.operation.ts index 8af52f2e3..e370d9d23 100644 --- a/packages/n8n-node/nodes/Probo/actions/asset/get.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/asset/get.operation.ts @@ -51,11 +51,11 @@ export const description: INodeProperties[] = [ description: 'Whether to include owner details in the response', }, { - displayName: 'Include Vendors', - name: 'includeVendors', + displayName: 'Include ThirdParties', + name: 'includeThirdParties', type: 'boolean', default: false, - description: 'Whether to include vendors in the response', + description: 'Whether to include thirdParties in the response', }, ], }, @@ -68,7 +68,7 @@ export async function execute( const assetId = this.getNodeParameter('assetId', itemIndex) as string; const options = this.getNodeParameter('options', itemIndex, {}) as { includeOwner?: boolean; - includeVendors?: boolean; + includeThirdParties?: boolean; }; const ownerFragment = options.includeOwner @@ -79,8 +79,8 @@ export async function execute( }` : ''; - const vendorsFragment = options.includeVendors - ? `vendors(first: 100) { + const thirdPartiesFragment = options.includeThirdParties + ? `thirdParties(first: 100) { edges { node { id @@ -100,7 +100,7 @@ export async function execute( assetType dataTypesStored ${ownerFragment} - ${vendorsFragment} + ${thirdPartiesFragment} createdAt updatedAt } diff --git a/packages/n8n-node/nodes/Probo/actions/asset/getAll.operation.ts b/packages/n8n-node/nodes/Probo/actions/asset/getAll.operation.ts index 3a048be9e..80b133bfc 100644 --- a/packages/n8n-node/nodes/Probo/actions/asset/getAll.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/asset/getAll.operation.ts @@ -81,11 +81,11 @@ export const description: INodeProperties[] = [ description: 'Whether to include owner details in the response', }, { - displayName: 'Include Vendors', - name: 'includeVendors', + displayName: 'Include ThirdParties', + name: 'includeThirdParties', type: 'boolean', default: false, - description: 'Whether to include vendors in the response', + description: 'Whether to include thirdParties in the response', }, ], }, @@ -100,7 +100,7 @@ export async function execute( const limit = this.getNodeParameter('limit', itemIndex, 50) as number; const options = this.getNodeParameter('options', itemIndex, {}) as { includeOwner?: boolean; - includeVendors?: boolean; + includeThirdParties?: boolean; }; const ownerFragment = options.includeOwner @@ -111,8 +111,8 @@ export async function execute( }` : ''; - const vendorsFragment = options.includeVendors - ? `vendors(first: 100) { + const thirdPartiesFragment = options.includeThirdParties + ? `thirdParties(first: 100) { edges { node { id @@ -135,7 +135,7 @@ export async function execute( assetType dataTypesStored ${ownerFragment} - ${vendorsFragment} + ${thirdPartiesFragment} createdAt updatedAt } diff --git a/packages/n8n-node/nodes/Probo/actions/asset/update.operation.ts b/packages/n8n-node/nodes/Probo/actions/asset/update.operation.ts index 4592a1e71..400d9a9ef 100644 --- a/packages/n8n-node/nodes/Probo/actions/asset/update.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/asset/update.operation.ts @@ -106,8 +106,8 @@ export const description: INodeProperties[] = [ description: 'The types of data stored in the asset', }, { - displayName: 'Vendor IDs', - name: 'vendorIds', + displayName: 'ThirdParty IDs', + name: 'thirdPartyIds', type: 'string', displayOptions: { show: { @@ -116,7 +116,7 @@ export const description: INodeProperties[] = [ }, }, default: '', - description: 'Comma-separated list of vendor IDs', + description: 'Comma-separated list of thirdParty IDs', }, { displayName: 'Options', @@ -139,11 +139,11 @@ export const description: INodeProperties[] = [ description: 'Whether to include owner details in the response', }, { - displayName: 'Include Vendors', - name: 'includeVendors', + displayName: 'Include ThirdParties', + name: 'includeThirdParties', type: 'boolean', default: false, - description: 'Whether to include vendors in the response', + description: 'Whether to include thirdParties in the response', }, ], }, @@ -159,10 +159,10 @@ export async function execute( const ownerId = this.getNodeParameter('ownerId', itemIndex, '') as string; const assetType = this.getNodeParameter('assetType', itemIndex, '') as string; const dataTypesStored = this.getNodeParameter('dataTypesStored', itemIndex, '') as string; - const vendorIdsStr = this.getNodeParameter('vendorIds', itemIndex, '') as string; + const thirdPartyIdsStr = this.getNodeParameter('thirdPartyIds', itemIndex, '') as string; const options = this.getNodeParameter('options', itemIndex, {}) as { includeOwner?: boolean; - includeVendors?: boolean; + includeThirdParties?: boolean; }; const ownerFragment = options.includeOwner @@ -173,8 +173,8 @@ export async function execute( }` : ''; - const vendorsFragment = options.includeVendors - ? `vendors(first: 100) { + const thirdPartiesFragment = options.includeThirdParties + ? `thirdParties(first: 100) { edges { node { id @@ -194,7 +194,7 @@ export async function execute( assetType dataTypesStored ${ownerFragment} - ${vendorsFragment} + ${thirdPartiesFragment} createdAt updatedAt } @@ -208,9 +208,9 @@ export async function execute( if (ownerId) input.ownerId = ownerId; if (assetType) input.assetType = assetType; if (dataTypesStored) input.dataTypesStored = dataTypesStored; - if (vendorIdsStr) { - const vendorIds = vendorIdsStr.split(',').map((vid) => vid.trim()).filter(Boolean); - if (vendorIds.length > 0) input.vendorIds = vendorIds; + if (thirdPartyIdsStr) { + const thirdPartyIds = thirdPartyIdsStr.split(',').map((vid) => vid.trim()).filter(Boolean); + if (thirdPartyIds.length > 0) input.thirdPartyIds = thirdPartyIds; } const responseData = await proboApiRequest.call(this, query, { input }); diff --git a/packages/n8n-node/nodes/Probo/actions/datum/create.operation.ts b/packages/n8n-node/nodes/Probo/actions/datum/create.operation.ts index 9992aa11d..166c24b81 100644 --- a/packages/n8n-node/nodes/Probo/actions/datum/create.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/datum/create.operation.ts @@ -91,8 +91,8 @@ export const description: INodeProperties[] = [ required: true, }, { - displayName: 'Vendor IDs', - name: 'vendorIds', + displayName: 'ThirdParty IDs', + name: 'thirdPartyIds', type: 'string', displayOptions: { show: { @@ -101,7 +101,7 @@ export const description: INodeProperties[] = [ }, }, default: '', - description: 'Comma-separated list of vendor IDs', + description: 'Comma-separated list of thirdParty IDs', }, { displayName: 'Options', @@ -124,11 +124,11 @@ export const description: INodeProperties[] = [ description: 'Whether to include owner details in the response', }, { - displayName: 'Include Vendors', - name: 'includeVendors', + displayName: 'Include ThirdParties', + name: 'includeThirdParties', type: 'boolean', default: false, - description: 'Whether to include vendors in the response', + description: 'Whether to include thirdParties in the response', }, ], }, @@ -142,10 +142,10 @@ export async function execute( const name = this.getNodeParameter('name', itemIndex) as string; const dataClassification = this.getNodeParameter('dataClassification', itemIndex) as string; const ownerId = this.getNodeParameter('ownerId', itemIndex) as string; - const vendorIdsStr = this.getNodeParameter('vendorIds', itemIndex, '') as string; + const thirdPartyIdsStr = this.getNodeParameter('thirdPartyIds', itemIndex, '') as string; const options = this.getNodeParameter('options', itemIndex, {}) as { includeOwner?: boolean; - includeVendors?: boolean; + includeThirdParties?: boolean; }; const ownerFragment = options.includeOwner @@ -156,8 +156,8 @@ export async function execute( }` : ''; - const vendorsFragment = options.includeVendors - ? `vendors(first: 100) { + const thirdPartiesFragment = options.includeThirdParties + ? `thirdParties(first: 100) { edges { node { id @@ -176,7 +176,7 @@ export async function execute( name dataClassification ${ownerFragment} - ${vendorsFragment} + ${thirdPartiesFragment} createdAt updatedAt } @@ -185,7 +185,7 @@ export async function execute( } `; - const vendorIds = vendorIdsStr ? vendorIdsStr.split(',').map((id) => id.trim()).filter(Boolean) : undefined; + const thirdPartyIds = thirdPartyIdsStr ? thirdPartyIdsStr.split(',').map((id) => id.trim()).filter(Boolean) : undefined; const variables = { input: { @@ -193,7 +193,7 @@ export async function execute( name, dataClassification, ownerId, - ...(vendorIds && vendorIds.length > 0 && { vendorIds }), + ...(thirdPartyIds && thirdPartyIds.length > 0 && { thirdPartyIds }), }, }; diff --git a/packages/n8n-node/nodes/Probo/actions/datum/get.operation.ts b/packages/n8n-node/nodes/Probo/actions/datum/get.operation.ts index 739c614de..22628e481 100644 --- a/packages/n8n-node/nodes/Probo/actions/datum/get.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/datum/get.operation.ts @@ -51,11 +51,11 @@ export const description: INodeProperties[] = [ description: 'Whether to include owner details in the response', }, { - displayName: 'Include Vendors', - name: 'includeVendors', + displayName: 'Include ThirdParties', + name: 'includeThirdParties', type: 'boolean', default: false, - description: 'Whether to include vendors in the response', + description: 'Whether to include thirdParties in the response', }, ], }, @@ -68,7 +68,7 @@ export async function execute( const datumId = this.getNodeParameter('datumId', itemIndex) as string; const options = this.getNodeParameter('options', itemIndex, {}) as { includeOwner?: boolean; - includeVendors?: boolean; + includeThirdParties?: boolean; }; const ownerFragment = options.includeOwner @@ -79,8 +79,8 @@ export async function execute( }` : ''; - const vendorsFragment = options.includeVendors - ? `vendors(first: 100) { + const thirdPartiesFragment = options.includeThirdParties + ? `thirdParties(first: 100) { edges { node { id @@ -98,7 +98,7 @@ export async function execute( name dataClassification ${ownerFragment} - ${vendorsFragment} + ${thirdPartiesFragment} createdAt updatedAt } diff --git a/packages/n8n-node/nodes/Probo/actions/datum/getAll.operation.ts b/packages/n8n-node/nodes/Probo/actions/datum/getAll.operation.ts index cd74c8d02..69aae8f8b 100644 --- a/packages/n8n-node/nodes/Probo/actions/datum/getAll.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/datum/getAll.operation.ts @@ -81,11 +81,11 @@ export const description: INodeProperties[] = [ description: 'Whether to include owner details in the response', }, { - displayName: 'Include Vendors', - name: 'includeVendors', + displayName: 'Include ThirdParties', + name: 'includeThirdParties', type: 'boolean', default: false, - description: 'Whether to include vendors in the response', + description: 'Whether to include thirdParties in the response', }, ], }, @@ -100,7 +100,7 @@ export async function execute( const limit = this.getNodeParameter('limit', itemIndex, 50) as number; const options = this.getNodeParameter('options', itemIndex, {}) as { includeOwner?: boolean; - includeVendors?: boolean; + includeThirdParties?: boolean; }; const ownerFragment = options.includeOwner @@ -111,8 +111,8 @@ export async function execute( }` : ''; - const vendorsFragment = options.includeVendors - ? `vendors(first: 100) { + const thirdPartiesFragment = options.includeThirdParties + ? `thirdParties(first: 100) { edges { node { id @@ -133,7 +133,7 @@ export async function execute( name dataClassification ${ownerFragment} - ${vendorsFragment} + ${thirdPartiesFragment} createdAt updatedAt } diff --git a/packages/n8n-node/nodes/Probo/actions/datum/update.operation.ts b/packages/n8n-node/nodes/Probo/actions/datum/update.operation.ts index 1b880d26e..5ed760b5f 100644 --- a/packages/n8n-node/nodes/Probo/actions/datum/update.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/datum/update.operation.ts @@ -88,8 +88,8 @@ export const description: INodeProperties[] = [ description: 'The ID of the owner (People)', }, { - displayName: 'Vendor IDs', - name: 'vendorIds', + displayName: 'ThirdParty IDs', + name: 'thirdPartyIds', type: 'string', displayOptions: { show: { @@ -98,7 +98,7 @@ export const description: INodeProperties[] = [ }, }, default: '', - description: 'Comma-separated list of vendor IDs', + description: 'Comma-separated list of thirdParty IDs', }, { displayName: 'Options', @@ -121,11 +121,11 @@ export const description: INodeProperties[] = [ description: 'Whether to include owner details in the response', }, { - displayName: 'Include Vendors', - name: 'includeVendors', + displayName: 'Include ThirdParties', + name: 'includeThirdParties', type: 'boolean', default: false, - description: 'Whether to include vendors in the response', + description: 'Whether to include thirdParties in the response', }, ], }, @@ -139,10 +139,10 @@ export async function execute( const name = this.getNodeParameter('name', itemIndex, '') as string; const dataClassification = this.getNodeParameter('dataClassification', itemIndex, '') as string; const ownerId = this.getNodeParameter('ownerId', itemIndex, '') as string; - const vendorIdsStr = this.getNodeParameter('vendorIds', itemIndex, '') as string; + const thirdPartyIdsStr = this.getNodeParameter('thirdPartyIds', itemIndex, '') as string; const options = this.getNodeParameter('options', itemIndex, {}) as { includeOwner?: boolean; - includeVendors?: boolean; + includeThirdParties?: boolean; }; const ownerFragment = options.includeOwner @@ -153,8 +153,8 @@ export async function execute( }` : ''; - const vendorsFragment = options.includeVendors - ? `vendors(first: 100) { + const thirdPartiesFragment = options.includeThirdParties + ? `thirdParties(first: 100) { edges { node { id @@ -172,7 +172,7 @@ export async function execute( name dataClassification ${ownerFragment} - ${vendorsFragment} + ${thirdPartiesFragment} createdAt updatedAt } @@ -184,9 +184,9 @@ export async function execute( if (name) input.name = name; if (dataClassification) input.dataClassification = dataClassification; if (ownerId) input.ownerId = ownerId; - if (vendorIdsStr) { - const vendorIds = vendorIdsStr.split(',').map((vid) => vid.trim()).filter(Boolean); - if (vendorIds.length > 0) input.vendorIds = vendorIds; + if (thirdPartyIdsStr) { + const thirdPartyIds = thirdPartyIdsStr.split(',').map((vid) => vid.trim()).filter(Boolean); + if (thirdPartyIds.length > 0) input.thirdPartyIds = thirdPartyIds; } const responseData = await proboApiRequest.call(this, query, { input }); diff --git a/packages/n8n-node/nodes/Probo/actions/index.ts b/packages/n8n-node/nodes/Probo/actions/index.ts index 75dcc7a09..60f27182a 100644 --- a/packages/n8n-node/nodes/Probo/actions/index.ts +++ b/packages/n8n-node/nodes/Probo/actions/index.ts @@ -41,7 +41,7 @@ import * as statementOfApplicability from './statementOfApplicability'; import * as task from './task'; import * as tia from './tia'; import * as trustCenter from './trustCenter'; -import * as vendor from './vendor'; +import * as thirdParty from './thirdParty'; import * as webhook from './webhook'; export interface ResourceModule { @@ -83,7 +83,7 @@ export const resources: Record = { task: task as ResourceModule, tia: tia as ResourceModule, trustCenter: trustCenter as ResourceModule, - vendor: vendor as ResourceModule, + thirdParty: thirdParty as ResourceModule, webhook: webhook as ResourceModule, }; diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/create.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/create.operation.ts similarity index 91% rename from packages/n8n-node/nodes/Probo/actions/vendor/create.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/create.operation.ts index ae4696d8f..e3ff76ca8 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/create.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/create.operation.ts @@ -22,7 +22,7 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['create'], }, }, @@ -36,12 +36,12 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['create'], }, }, default: '', - description: 'The name of the vendor', + description: 'The name of the thirdParty', required: true, }, { @@ -53,12 +53,12 @@ export const description: INodeProperties[] = [ }, displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['create'], }, }, default: '', - description: 'The description of the vendor', + description: 'The description of the thirdParty', }, { displayName: 'Category', @@ -66,12 +66,12 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['create'], }, }, default: '', - description: 'The category of the vendor', + description: 'The category of the thirdParty', }, { displayName: 'Website URL', @@ -79,12 +79,12 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['create'], }, }, default: '', - description: 'The website URL of the vendor', + description: 'The website URL of the thirdParty', }, { displayName: 'Legal Name', @@ -92,12 +92,12 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['create'], }, }, default: '', - description: 'The legal name of the vendor', + description: 'The legal name of the thirdParty', }, { displayName: 'Headquarter Address', @@ -105,12 +105,12 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['create'], }, }, default: '', - description: 'The headquarter address of the vendor', + description: 'The headquarter address of the thirdParty', }, { displayName: 'Business Owner ID', @@ -118,7 +118,7 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['create'], }, }, @@ -131,7 +131,7 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['create'], }, }, @@ -146,7 +146,7 @@ export const description: INodeProperties[] = [ default: {}, displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['create'], }, }, @@ -200,7 +200,7 @@ export const description: INodeProperties[] = [ name: 'statusPageUrl', type: 'string', default: '', - description: 'The status page URL of the vendor', + description: 'The status page URL of the thirdParty', }, { displayName: 'Subprocessors List URL', @@ -252,9 +252,9 @@ export async function execute( }; const query = ` - mutation CreateVendor($input: CreateVendorInput!) { - createVendor(input: $input) { - vendorEdge { + mutation CreateThirdParty($input: CreateThirdPartyInput!) { + createThirdParty(input: $input) { + thirdPartyEdge { node { id name diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/createContact.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/createContact.operation.ts similarity index 83% rename from packages/n8n-node/nodes/Probo/actions/vendor/createContact.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/createContact.operation.ts index 7d6f962a9..6a5dd37f9 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/createContact.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/createContact.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor ID', - name: 'vendorId', + displayName: 'ThirdParty ID', + name: 'thirdPartyId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['createContact'], }, }, default: '', - description: 'The ID of the vendor', + description: 'The ID of the thirdParty', required: true, }, { @@ -36,7 +36,7 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['createContact'], }, }, @@ -50,7 +50,7 @@ export const description: INodeProperties[] = [ placeholder: 'name@email.com', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['createContact'], }, }, @@ -63,7 +63,7 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['createContact'], }, }, @@ -76,7 +76,7 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['createContact'], }, }, @@ -89,16 +89,16 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorId = this.getNodeParameter('vendorId', itemIndex) as string; + const thirdPartyId = this.getNodeParameter('thirdPartyId', itemIndex) as string; const fullName = this.getNodeParameter('fullName', itemIndex, '') as string; const email = this.getNodeParameter('email', itemIndex, '') as string; const phone = this.getNodeParameter('phone', itemIndex, '') as string; const role = this.getNodeParameter('role', itemIndex, '') as string; const query = ` - mutation CreateVendorContact($input: CreateVendorContactInput!) { - createVendorContact(input: $input) { - vendorContactEdge { + mutation CreateThirdPartyContact($input: CreateThirdPartyContactInput!) { + createThirdPartyContact(input: $input) { + thirdPartyContactEdge { node { id fullName @@ -113,7 +113,7 @@ export async function execute( } `; - const input: Record = { vendorId }; + const input: Record = { thirdPartyId }; if (fullName) input.fullName = fullName; if (email) input.email = email; if (phone) input.phone = phone; diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/createRiskAssessment.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/createRiskAssessment.operation.ts similarity index 87% rename from packages/n8n-node/nodes/Probo/actions/vendor/createRiskAssessment.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/createRiskAssessment.operation.ts index e60cac19c..c06ddad7e 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/createRiskAssessment.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/createRiskAssessment.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor ID', - name: 'vendorId', + displayName: 'ThirdParty ID', + name: 'thirdPartyId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['createRiskAssessment'], }, }, default: '', - description: 'The ID of the vendor', + description: 'The ID of the thirdParty', required: true, }, { @@ -36,7 +36,7 @@ export const description: INodeProperties[] = [ type: 'dateTime', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['createRiskAssessment'], }, }, @@ -50,7 +50,7 @@ export const description: INodeProperties[] = [ type: 'options', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['createRiskAssessment'], }, }, @@ -71,7 +71,7 @@ export const description: INodeProperties[] = [ type: 'options', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['createRiskAssessment'], }, }, @@ -94,7 +94,7 @@ export const description: INodeProperties[] = [ }, displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['createRiskAssessment'], }, }, @@ -107,7 +107,7 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorId = this.getNodeParameter('vendorId', itemIndex) as string; + const thirdPartyId = this.getNodeParameter('thirdPartyId', itemIndex) as string; const expiresAtRaw = this.getNodeParameter('expiresAt', itemIndex) as string; const dataSensitivity = this.getNodeParameter('dataSensitivity', itemIndex) as string; const businessImpact = this.getNodeParameter('businessImpact', itemIndex) as string; @@ -117,9 +117,9 @@ export async function execute( const expiresAt = new Date(expiresAtRaw).toISOString(); const query = ` - mutation CreateVendorRiskAssessment($input: CreateVendorRiskAssessmentInput!) { - createVendorRiskAssessment(input: $input) { - vendorRiskAssessmentEdge { + mutation CreateThirdPartyRiskAssessment($input: CreateThirdPartyRiskAssessmentInput!) { + createThirdPartyRiskAssessment(input: $input) { + thirdPartyRiskAssessmentEdge { node { id expiresAt @@ -135,7 +135,7 @@ export async function execute( `; const input: Record = { - vendorId, + thirdPartyId, expiresAt, dataSensitivity, businessImpact, diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/createService.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/createService.operation.ts similarity index 79% rename from packages/n8n-node/nodes/Probo/actions/vendor/createService.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/createService.operation.ts index 0f26e8660..649baeca4 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/createService.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/createService.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor ID', - name: 'vendorId', + displayName: 'ThirdParty ID', + name: 'thirdPartyId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['createService'], }, }, default: '', - description: 'The ID of the vendor', + description: 'The ID of the thirdParty', required: true, }, { @@ -36,12 +36,12 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['createService'], }, }, default: '', - description: 'The name of the vendor service', + description: 'The name of the thirdParty service', required: true, }, { @@ -53,12 +53,12 @@ export const description: INodeProperties[] = [ }, displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['createService'], }, }, default: '', - description: 'The description of the vendor service', + description: 'The description of the thirdParty service', }, ]; @@ -66,14 +66,14 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorId = this.getNodeParameter('vendorId', itemIndex) as string; + const thirdPartyId = this.getNodeParameter('thirdPartyId', itemIndex) as string; const name = this.getNodeParameter('name', itemIndex) as string; const description = this.getNodeParameter('description', itemIndex, '') as string; const query = ` - mutation CreateVendorService($input: CreateVendorServiceInput!) { - createVendorService(input: $input) { - vendorServiceEdge { + mutation CreateThirdPartyService($input: CreateThirdPartyServiceInput!) { + createThirdPartyService(input: $input) { + thirdPartyServiceEdge { node { id name @@ -87,7 +87,7 @@ export async function execute( `; const input: Record = { - vendorId, + thirdPartyId, name, }; if (description) input.description = description; diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/delete.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/delete.operation.ts similarity index 78% rename from packages/n8n-node/nodes/Probo/actions/vendor/delete.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/delete.operation.ts index 62b9cd138..6a7ec312a 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/delete.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/delete.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor ID', - name: 'vendorId', + displayName: 'ThirdParty ID', + name: 'thirdPartyId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['delete'], }, }, default: '', - description: 'The ID of the vendor to delete', + description: 'The ID of the thirdParty to delete', required: true, }, ]; @@ -36,17 +36,17 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorId = this.getNodeParameter('vendorId', itemIndex) as string; + const thirdPartyId = this.getNodeParameter('thirdPartyId', itemIndex) as string; const query = ` - mutation DeleteVendor($input: DeleteVendorInput!) { - deleteVendor(input: $input) { - deletedVendorId + mutation DeleteThirdParty($input: DeleteThirdPartyInput!) { + deleteThirdParty(input: $input) { + deletedThirdPartyId } } `; - const responseData = await proboApiRequest.call(this, query, { input: { vendorId } }); + const responseData = await proboApiRequest.call(this, query, { input: { thirdPartyId } }); return { json: responseData, diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/deleteBusinessAssociateAgreement.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/deleteBusinessAssociateAgreement.operation.ts similarity index 75% rename from packages/n8n-node/nodes/Probo/actions/vendor/deleteBusinessAssociateAgreement.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/deleteBusinessAssociateAgreement.operation.ts index e149ffd6f..bfb203165 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/deleteBusinessAssociateAgreement.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/deleteBusinessAssociateAgreement.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor ID', - name: 'vendorId', + displayName: 'ThirdParty ID', + name: 'thirdPartyId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['deleteBusinessAssociateAgreement'], }, }, default: '', - description: 'The ID of the vendor', + description: 'The ID of the thirdParty', required: true, }, ]; @@ -36,17 +36,17 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorId = this.getNodeParameter('vendorId', itemIndex) as string; + const thirdPartyId = this.getNodeParameter('thirdPartyId', itemIndex) as string; const query = ` - mutation DeleteVendorBusinessAssociateAgreement($input: DeleteVendorBusinessAssociateAgreementInput!) { - deleteVendorBusinessAssociateAgreement(input: $input) { - deletedVendorBusinessAssociateAgreementId + mutation DeleteThirdPartyBusinessAssociateAgreement($input: DeleteThirdPartyBusinessAssociateAgreementInput!) { + deleteThirdPartyBusinessAssociateAgreement(input: $input) { + deletedThirdPartyBusinessAssociateAgreementId } } `; - const responseData = await proboApiRequest.call(this, query, { input: { vendorId } }); + const responseData = await proboApiRequest.call(this, query, { input: { thirdPartyId } }); return { json: responseData, diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/deleteComplianceReport.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/deleteComplianceReport.operation.ts similarity index 72% rename from packages/n8n-node/nodes/Probo/actions/vendor/deleteComplianceReport.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/deleteComplianceReport.operation.ts index 0e719ec2b..0b4582098 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/deleteComplianceReport.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/deleteComplianceReport.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor Compliance Report ID', - name: 'vendorComplianceReportId', + displayName: 'ThirdParty Compliance Report ID', + name: 'thirdPartyComplianceReportId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['deleteComplianceReport'], }, }, default: '', - description: 'The ID of the vendor compliance report to delete', + description: 'The ID of the thirdParty compliance report to delete', required: true, }, ]; @@ -36,17 +36,17 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorComplianceReportId = this.getNodeParameter('vendorComplianceReportId', itemIndex) as string; + const thirdPartyComplianceReportId = this.getNodeParameter('thirdPartyComplianceReportId', itemIndex) as string; const query = ` - mutation DeleteVendorComplianceReport($input: DeleteVendorComplianceReportInput!) { - deleteVendorComplianceReport(input: $input) { - deletedVendorComplianceReportId + mutation DeleteThirdPartyComplianceReport($input: DeleteThirdPartyComplianceReportInput!) { + deleteThirdPartyComplianceReport(input: $input) { + deletedThirdPartyComplianceReportId } } `; - const responseData = await proboApiRequest.call(this, query, { input: { vendorComplianceReportId } }); + const responseData = await proboApiRequest.call(this, query, { input: { thirdPartyComplianceReportId } }); return { json: responseData, diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/deleteContact.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/deleteContact.operation.ts similarity index 75% rename from packages/n8n-node/nodes/Probo/actions/vendor/deleteContact.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/deleteContact.operation.ts index b8dae6307..3e6bd6b7d 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/deleteContact.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/deleteContact.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor Contact ID', - name: 'vendorContactId', + displayName: 'ThirdParty Contact ID', + name: 'thirdPartyContactId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['deleteContact'], }, }, default: '', - description: 'The ID of the vendor contact to delete', + description: 'The ID of the thirdParty contact to delete', required: true, }, ]; @@ -36,17 +36,17 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorContactId = this.getNodeParameter('vendorContactId', itemIndex) as string; + const thirdPartyContactId = this.getNodeParameter('thirdPartyContactId', itemIndex) as string; const query = ` - mutation DeleteVendorContact($input: DeleteVendorContactInput!) { - deleteVendorContact(input: $input) { - deletedVendorContactId + mutation DeleteThirdPartyContact($input: DeleteThirdPartyContactInput!) { + deleteThirdPartyContact(input: $input) { + deletedThirdPartyContactId } } `; - const responseData = await proboApiRequest.call(this, query, { input: { vendorContactId } }); + const responseData = await proboApiRequest.call(this, query, { input: { thirdPartyContactId } }); return { json: responseData, diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/deleteDataPrivacyAgreement.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/deleteDataPrivacyAgreement.operation.ts similarity index 76% rename from packages/n8n-node/nodes/Probo/actions/vendor/deleteDataPrivacyAgreement.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/deleteDataPrivacyAgreement.operation.ts index 1e28d3f88..749aebea6 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/deleteDataPrivacyAgreement.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/deleteDataPrivacyAgreement.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor ID', - name: 'vendorId', + displayName: 'ThirdParty ID', + name: 'thirdPartyId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['deleteDataPrivacyAgreement'], }, }, default: '', - description: 'The ID of the vendor', + description: 'The ID of the thirdParty', required: true, }, ]; @@ -36,17 +36,17 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorId = this.getNodeParameter('vendorId', itemIndex) as string; + const thirdPartyId = this.getNodeParameter('thirdPartyId', itemIndex) as string; const query = ` - mutation DeleteVendorDataPrivacyAgreement($input: DeleteVendorDataPrivacyAgreementInput!) { - deleteVendorDataPrivacyAgreement(input: $input) { - deletedVendorDataPrivacyAgreementId + mutation DeleteThirdPartyDataPrivacyAgreement($input: DeleteThirdPartyDataPrivacyAgreementInput!) { + deleteThirdPartyDataPrivacyAgreement(input: $input) { + deletedThirdPartyDataPrivacyAgreementId } } `; - const responseData = await proboApiRequest.call(this, query, { input: { vendorId } }); + const responseData = await proboApiRequest.call(this, query, { input: { thirdPartyId } }); return { json: responseData, diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/deleteService.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/deleteService.operation.ts similarity index 75% rename from packages/n8n-node/nodes/Probo/actions/vendor/deleteService.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/deleteService.operation.ts index fd8c92dd1..af71ed794 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/deleteService.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/deleteService.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor Service ID', - name: 'vendorServiceId', + displayName: 'ThirdParty Service ID', + name: 'thirdPartyServiceId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['deleteService'], }, }, default: '', - description: 'The ID of the vendor service to delete', + description: 'The ID of the thirdParty service to delete', required: true, }, ]; @@ -36,17 +36,17 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorServiceId = this.getNodeParameter('vendorServiceId', itemIndex) as string; + const thirdPartyServiceId = this.getNodeParameter('thirdPartyServiceId', itemIndex) as string; const query = ` - mutation DeleteVendorService($input: DeleteVendorServiceInput!) { - deleteVendorService(input: $input) { - deletedVendorServiceId + mutation DeleteThirdPartyService($input: DeleteThirdPartyServiceInput!) { + deleteThirdPartyService(input: $input) { + deletedThirdPartyServiceId } } `; - const responseData = await proboApiRequest.call(this, query, { input: { vendorServiceId } }); + const responseData = await proboApiRequest.call(this, query, { input: { thirdPartyServiceId } }); return { json: responseData, diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/get.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/get.operation.ts similarity index 90% rename from packages/n8n-node/nodes/Probo/actions/vendor/get.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/get.operation.ts index 293fab633..20f166155 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/get.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/get.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor ID', - name: 'vendorId', + displayName: 'ThirdParty ID', + name: 'thirdPartyId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['get'], }, }, default: '', - description: 'The ID of the vendor', + description: 'The ID of the thirdParty', required: true, }, { @@ -38,7 +38,7 @@ export const description: INodeProperties[] = [ default: {}, displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['get'], }, }, @@ -72,7 +72,7 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorId = this.getNodeParameter('vendorId', itemIndex) as string; + const thirdPartyId = this.getNodeParameter('thirdPartyId', itemIndex) as string; const options = this.getNodeParameter('options', itemIndex, {}) as { includeOrganization?: boolean; includeBusinessOwner?: boolean; @@ -103,9 +103,9 @@ export async function execute( : ''; const query = ` - query GetVendor($vendorId: ID!) { - node(id: $vendorId) { - ... on Vendor { + query GetThirdParty($thirdPartyId: ID!) { + node(id: $thirdPartyId) { + ... on ThirdParty { id name description @@ -136,7 +136,7 @@ export async function execute( `; const variables = { - vendorId, + thirdPartyId, }; const responseData = await proboApiRequest.call(this, query, variables); diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/getAll.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/getAll.operation.ts similarity index 91% rename from packages/n8n-node/nodes/Probo/actions/vendor/getAll.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/getAll.operation.ts index 63fce20c4..87afdd073 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/getAll.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/getAll.operation.ts @@ -22,7 +22,7 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAll'], }, }, @@ -36,7 +36,7 @@ export const description: INodeProperties[] = [ type: 'boolean', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAll'], }, }, @@ -49,7 +49,7 @@ export const description: INodeProperties[] = [ type: 'number', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAll'], returnAll: [false], }, @@ -68,7 +68,7 @@ export const description: INodeProperties[] = [ default: {}, displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAll'], }, }, @@ -135,10 +135,10 @@ export async function execute( : ''; const query = ` - query GetVendors($organizationId: ID!, $first: Int, $after: CursorKey) { + query GetThirdParties($organizationId: ID!, $first: Int, $after: CursorKey) { node(id: $organizationId) { ... on Organization { - vendors(first: $first, after: $after) { + thirdParties(first: $first, after: $after) { edges { node { id @@ -177,21 +177,21 @@ export async function execute( } `; - const vendors = await proboApiRequestAllItems.call( + const thirdParties = await proboApiRequestAllItems.call( this, query, { organizationId }, (response) => { const data = response?.data as IDataObject | undefined; const node = data?.node as IDataObject | undefined; - return node?.vendors as IDataObject | undefined; + return node?.thirdParties as IDataObject | undefined; }, returnAll, limit, ); return { - json: { vendors }, + json: { thirdParties }, pairedItem: { item: itemIndex }, }; } diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/getAllComplianceReports.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/getAllComplianceReports.operation.ts similarity index 82% rename from packages/n8n-node/nodes/Probo/actions/vendor/getAllComplianceReports.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/getAllComplianceReports.operation.ts index ffbd84be4..c5f28dbeb 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/getAllComplianceReports.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/getAllComplianceReports.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequestAllItems } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor ID', - name: 'vendorId', + displayName: 'ThirdParty ID', + name: 'thirdPartyId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAllComplianceReports'], }, }, default: '', - description: 'The ID of the vendor', + description: 'The ID of the thirdParty', required: true, }, { @@ -36,7 +36,7 @@ export const description: INodeProperties[] = [ type: 'boolean', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAllComplianceReports'], }, }, @@ -49,7 +49,7 @@ export const description: INodeProperties[] = [ type: 'number', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAllComplianceReports'], returnAll: [false], }, @@ -66,14 +66,14 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorId = this.getNodeParameter('vendorId', itemIndex) as string; + const thirdPartyId = this.getNodeParameter('thirdPartyId', itemIndex) as string; const returnAll = this.getNodeParameter('returnAll', itemIndex) as boolean; const limit = this.getNodeParameter('limit', itemIndex, 50) as number; const query = ` - query GetVendorComplianceReports($vendorId: ID!, $first: Int, $after: CursorKey) { - node(id: $vendorId) { - ... on Vendor { + query GetThirdPartyComplianceReports($thirdPartyId: ID!, $first: Int, $after: CursorKey) { + node(id: $thirdPartyId) { + ... on ThirdParty { complianceReports(first: $first, after: $after) { edges { node { @@ -95,10 +95,10 @@ export async function execute( } `; - const vendorComplianceReports = await proboApiRequestAllItems.call( + const thirdPartyComplianceReports = await proboApiRequestAllItems.call( this, query, - { vendorId }, + { thirdPartyId }, (response) => { const data = response?.data as IDataObject | undefined; const node = data?.node as IDataObject | undefined; @@ -109,7 +109,7 @@ export async function execute( ); return { - json: { vendorComplianceReports }, + json: { thirdPartyComplianceReports }, pairedItem: { item: itemIndex }, }; } diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/getAllContacts.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/getAllContacts.operation.ts similarity index 77% rename from packages/n8n-node/nodes/Probo/actions/vendor/getAllContacts.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/getAllContacts.operation.ts index 3709a61ab..ebf7222e0 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/getAllContacts.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/getAllContacts.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequestAllItems } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor ID', - name: 'vendorId', + displayName: 'ThirdParty ID', + name: 'thirdPartyId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAllContacts'], }, }, default: '', - description: 'The ID of the vendor', + description: 'The ID of the thirdParty', required: true, }, { @@ -36,7 +36,7 @@ export const description: INodeProperties[] = [ type: 'boolean', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAllContacts'], }, }, @@ -49,7 +49,7 @@ export const description: INodeProperties[] = [ type: 'number', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAllContacts'], returnAll: [false], }, @@ -68,17 +68,17 @@ export const description: INodeProperties[] = [ default: {}, displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAllContacts'], }, }, options: [ { - displayName: 'Include Vendor', - name: 'includeVendor', + displayName: 'Include ThirdParty', + name: 'includeThirdParty', type: 'boolean', default: false, - description: 'Whether to include vendor in the response', + description: 'Whether to include thirdParty in the response', }, ], }, @@ -88,24 +88,24 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorId = this.getNodeParameter('vendorId', itemIndex) as string; + const thirdPartyId = this.getNodeParameter('thirdPartyId', itemIndex) as string; const returnAll = this.getNodeParameter('returnAll', itemIndex) as boolean; const limit = this.getNodeParameter('limit', itemIndex, 50) as number; const options = this.getNodeParameter('options', itemIndex, {}) as { - includeVendor?: boolean; + includeThirdParty?: boolean; }; - const vendorFragment = options.includeVendor - ? `vendor { + const thirdPartyFragment = options.includeThirdParty + ? `thirdParty { id name }` : ''; const query = ` - query GetVendorContacts($vendorId: ID!, $first: Int, $after: CursorKey) { - node(id: $vendorId) { - ... on Vendor { + query GetThirdPartyContacts($thirdPartyId: ID!, $first: Int, $after: CursorKey) { + node(id: $thirdPartyId) { + ... on ThirdParty { contacts(first: $first, after: $after) { edges { node { @@ -114,7 +114,7 @@ export async function execute( email phone role - ${vendorFragment} + ${thirdPartyFragment} createdAt updatedAt } @@ -129,10 +129,10 @@ export async function execute( } `; - const vendorContacts = await proboApiRequestAllItems.call( + const thirdPartyContacts = await proboApiRequestAllItems.call( this, query, - { vendorId }, + { thirdPartyId }, (response) => { const data = response?.data as IDataObject | undefined; const node = data?.node as IDataObject | undefined; @@ -143,7 +143,7 @@ export async function execute( ); return { - json: { vendorContacts }, + json: { thirdPartyContacts }, pairedItem: { item: itemIndex }, }; } diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/getAllRiskAssessments.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/getAllRiskAssessments.operation.ts similarity index 77% rename from packages/n8n-node/nodes/Probo/actions/vendor/getAllRiskAssessments.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/getAllRiskAssessments.operation.ts index 5e08dde82..782725ff3 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/getAllRiskAssessments.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/getAllRiskAssessments.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequestAllItems } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor ID', - name: 'vendorId', + displayName: 'ThirdParty ID', + name: 'thirdPartyId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAllRiskAssessments'], }, }, default: '', - description: 'The ID of the vendor', + description: 'The ID of the thirdParty', required: true, }, { @@ -36,7 +36,7 @@ export const description: INodeProperties[] = [ type: 'boolean', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAllRiskAssessments'], }, }, @@ -49,7 +49,7 @@ export const description: INodeProperties[] = [ type: 'number', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAllRiskAssessments'], returnAll: [false], }, @@ -68,17 +68,17 @@ export const description: INodeProperties[] = [ default: {}, displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAllRiskAssessments'], }, }, options: [ { - displayName: 'Include Vendor', - name: 'includeVendor', + displayName: 'Include ThirdParty', + name: 'includeThirdParty', type: 'boolean', default: false, - description: 'Whether to include vendor in the response', + description: 'Whether to include thirdParty in the response', }, ], }, @@ -88,24 +88,24 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorId = this.getNodeParameter('vendorId', itemIndex) as string; + const thirdPartyId = this.getNodeParameter('thirdPartyId', itemIndex) as string; const returnAll = this.getNodeParameter('returnAll', itemIndex) as boolean; const limit = this.getNodeParameter('limit', itemIndex, 50) as number; const options = this.getNodeParameter('options', itemIndex, {}) as { - includeVendor?: boolean; + includeThirdParty?: boolean; }; - const vendorFragment = options.includeVendor - ? `vendor { + const thirdPartyFragment = options.includeThirdParty + ? `thirdParty { id name }` : ''; const query = ` - query GetVendorRiskAssessments($vendorId: ID!, $first: Int, $after: CursorKey) { - node(id: $vendorId) { - ... on Vendor { + query GetThirdPartyRiskAssessments($thirdPartyId: ID!, $first: Int, $after: CursorKey) { + node(id: $thirdPartyId) { + ... on ThirdParty { riskAssessments(first: $first, after: $after) { edges { node { @@ -114,7 +114,7 @@ export async function execute( dataSensitivity businessImpact notes - ${vendorFragment} + ${thirdPartyFragment} createdAt updatedAt } @@ -129,10 +129,10 @@ export async function execute( } `; - const vendorRiskAssessments = await proboApiRequestAllItems.call( + const thirdPartyRiskAssessments = await proboApiRequestAllItems.call( this, query, - { vendorId }, + { thirdPartyId }, (response) => { const data = response?.data as IDataObject | undefined; const node = data?.node as IDataObject | undefined; @@ -143,7 +143,7 @@ export async function execute( ); return { - json: { vendorRiskAssessments }, + json: { thirdPartyRiskAssessments }, pairedItem: { item: itemIndex }, }; } diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/getAllServices.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/getAllServices.operation.ts similarity index 77% rename from packages/n8n-node/nodes/Probo/actions/vendor/getAllServices.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/getAllServices.operation.ts index d06821853..3b9a6d50c 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/getAllServices.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/getAllServices.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequestAllItems } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor ID', - name: 'vendorId', + displayName: 'ThirdParty ID', + name: 'thirdPartyId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAllServices'], }, }, default: '', - description: 'The ID of the vendor', + description: 'The ID of the thirdParty', required: true, }, { @@ -36,7 +36,7 @@ export const description: INodeProperties[] = [ type: 'boolean', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAllServices'], }, }, @@ -49,7 +49,7 @@ export const description: INodeProperties[] = [ type: 'number', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAllServices'], returnAll: [false], }, @@ -68,17 +68,17 @@ export const description: INodeProperties[] = [ default: {}, displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getAllServices'], }, }, options: [ { - displayName: 'Include Vendor', - name: 'includeVendor', + displayName: 'Include ThirdParty', + name: 'includeThirdParty', type: 'boolean', default: false, - description: 'Whether to include vendor in the response', + description: 'Whether to include thirdParty in the response', }, ], }, @@ -88,31 +88,31 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorId = this.getNodeParameter('vendorId', itemIndex) as string; + const thirdPartyId = this.getNodeParameter('thirdPartyId', itemIndex) as string; const returnAll = this.getNodeParameter('returnAll', itemIndex) as boolean; const limit = this.getNodeParameter('limit', itemIndex, 50) as number; const options = this.getNodeParameter('options', itemIndex, {}) as { - includeVendor?: boolean; + includeThirdParty?: boolean; }; - const vendorFragment = options.includeVendor - ? `vendor { + const thirdPartyFragment = options.includeThirdParty + ? `thirdParty { id name }` : ''; const query = ` - query GetVendorServices($vendorId: ID!, $first: Int, $after: CursorKey) { - node(id: $vendorId) { - ... on Vendor { + query GetThirdPartyServices($thirdPartyId: ID!, $first: Int, $after: CursorKey) { + node(id: $thirdPartyId) { + ... on ThirdParty { services(first: $first, after: $after) { edges { node { id name description - ${vendorFragment} + ${thirdPartyFragment} createdAt updatedAt } @@ -127,10 +127,10 @@ export async function execute( } `; - const vendorServices = await proboApiRequestAllItems.call( + const thirdPartyServices = await proboApiRequestAllItems.call( this, query, - { vendorId }, + { thirdPartyId }, (response) => { const data = response?.data as IDataObject | undefined; const node = data?.node as IDataObject | undefined; @@ -141,7 +141,7 @@ export async function execute( ); return { - json: { vendorServices }, + json: { thirdPartyServices }, pairedItem: { item: itemIndex }, }; } diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/getBusinessAssociateAgreement.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/getBusinessAssociateAgreement.operation.ts similarity index 81% rename from packages/n8n-node/nodes/Probo/actions/vendor/getBusinessAssociateAgreement.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/getBusinessAssociateAgreement.operation.ts index f5b0c6399..b04887424 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/getBusinessAssociateAgreement.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/getBusinessAssociateAgreement.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor ID', - name: 'vendorId', + displayName: 'ThirdParty ID', + name: 'thirdPartyId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getBusinessAssociateAgreement'], }, }, default: '', - description: 'The ID of the vendor', + description: 'The ID of the thirdParty', required: true, }, ]; @@ -36,12 +36,12 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorId = this.getNodeParameter('vendorId', itemIndex) as string; + const thirdPartyId = this.getNodeParameter('thirdPartyId', itemIndex) as string; const query = ` - query GetVendorBusinessAssociateAgreement($vendorId: ID!) { - node(id: $vendorId) { - ... on Vendor { + query GetThirdPartyBusinessAssociateAgreement($thirdPartyId: ID!) { + node(id: $thirdPartyId) { + ... on ThirdParty { businessAssociateAgreement { id validFrom @@ -57,7 +57,7 @@ export async function execute( } `; - const responseData = await proboApiRequest.call(this, query, { vendorId }); + const responseData = await proboApiRequest.call(this, query, { thirdPartyId }); return { json: responseData, diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/getContact.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/getContact.operation.ts similarity index 72% rename from packages/n8n-node/nodes/Probo/actions/vendor/getContact.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/getContact.operation.ts index 271afad1c..5b4a8d3a0 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/getContact.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/getContact.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor Contact ID', - name: 'vendorContactId', + displayName: 'ThirdParty Contact ID', + name: 'thirdPartyContactId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getContact'], }, }, default: '', - description: 'The ID of the vendor contact', + description: 'The ID of the thirdParty contact', required: true, }, { @@ -38,17 +38,17 @@ export const description: INodeProperties[] = [ default: {}, displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getContact'], }, }, options: [ { - displayName: 'Include Vendor', - name: 'includeVendor', + displayName: 'Include ThirdParty', + name: 'includeThirdParty', type: 'boolean', default: false, - description: 'Whether to include vendor in the response', + description: 'Whether to include thirdParty in the response', }, ], }, @@ -58,28 +58,28 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorContactId = this.getNodeParameter('vendorContactId', itemIndex) as string; + const thirdPartyContactId = this.getNodeParameter('thirdPartyContactId', itemIndex) as string; const options = this.getNodeParameter('options', itemIndex, {}) as { - includeVendor?: boolean; + includeThirdParty?: boolean; }; - const vendorFragment = options.includeVendor - ? `vendor { + const thirdPartyFragment = options.includeThirdParty + ? `thirdParty { id name }` : ''; const query = ` - query GetVendorContact($vendorContactId: ID!) { - node(id: $vendorContactId) { - ... on VendorContact { + query GetThirdPartyContact($thirdPartyContactId: ID!) { + node(id: $thirdPartyContactId) { + ... on ThirdPartyContact { id fullName email phone role - ${vendorFragment} + ${thirdPartyFragment} createdAt updatedAt } @@ -87,7 +87,7 @@ export async function execute( } `; - const responseData = await proboApiRequest.call(this, query, { vendorContactId }); + const responseData = await proboApiRequest.call(this, query, { thirdPartyContactId }); return { json: responseData, diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/getDataPrivacyAgreement.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/getDataPrivacyAgreement.operation.ts similarity index 82% rename from packages/n8n-node/nodes/Probo/actions/vendor/getDataPrivacyAgreement.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/getDataPrivacyAgreement.operation.ts index 6894aeeb3..7370dcff4 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/getDataPrivacyAgreement.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/getDataPrivacyAgreement.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor ID', - name: 'vendorId', + displayName: 'ThirdParty ID', + name: 'thirdPartyId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getDataPrivacyAgreement'], }, }, default: '', - description: 'The ID of the vendor', + description: 'The ID of the thirdParty', required: true, }, ]; @@ -36,12 +36,12 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorId = this.getNodeParameter('vendorId', itemIndex) as string; + const thirdPartyId = this.getNodeParameter('thirdPartyId', itemIndex) as string; const query = ` - query GetVendorDataPrivacyAgreement($vendorId: ID!) { - node(id: $vendorId) { - ... on Vendor { + query GetThirdPartyDataPrivacyAgreement($thirdPartyId: ID!) { + node(id: $thirdPartyId) { + ... on ThirdParty { dataPrivacyAgreement { id validFrom @@ -57,7 +57,7 @@ export async function execute( } `; - const responseData = await proboApiRequest.call(this, query, { vendorId }); + const responseData = await proboApiRequest.call(this, query, { thirdPartyId }); return { json: responseData, diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/getRiskAssessment.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/getRiskAssessment.operation.ts similarity index 71% rename from packages/n8n-node/nodes/Probo/actions/vendor/getRiskAssessment.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/getRiskAssessment.operation.ts index e5e27edde..5efec5252 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/getRiskAssessment.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/getRiskAssessment.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor Risk Assessment ID', - name: 'vendorRiskAssessmentId', + displayName: 'ThirdParty Risk Assessment ID', + name: 'thirdPartyRiskAssessmentId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getRiskAssessment'], }, }, default: '', - description: 'The ID of the vendor risk assessment', + description: 'The ID of the thirdParty risk assessment', required: true, }, { @@ -38,17 +38,17 @@ export const description: INodeProperties[] = [ default: {}, displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getRiskAssessment'], }, }, options: [ { - displayName: 'Include Vendor', - name: 'includeVendor', + displayName: 'Include ThirdParty', + name: 'includeThirdParty', type: 'boolean', default: false, - description: 'Whether to include vendor in the response', + description: 'Whether to include thirdParty in the response', }, ], }, @@ -58,28 +58,28 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorRiskAssessmentId = this.getNodeParameter('vendorRiskAssessmentId', itemIndex) as string; + const thirdPartyRiskAssessmentId = this.getNodeParameter('thirdPartyRiskAssessmentId', itemIndex) as string; const options = this.getNodeParameter('options', itemIndex, {}) as { - includeVendor?: boolean; + includeThirdParty?: boolean; }; - const vendorFragment = options.includeVendor - ? `vendor { + const thirdPartyFragment = options.includeThirdParty + ? `thirdParty { id name }` : ''; const query = ` - query GetVendorRiskAssessment($vendorRiskAssessmentId: ID!) { - node(id: $vendorRiskAssessmentId) { - ... on VendorRiskAssessment { + query GetThirdPartyRiskAssessment($thirdPartyRiskAssessmentId: ID!) { + node(id: $thirdPartyRiskAssessmentId) { + ... on ThirdPartyRiskAssessment { id expiresAt dataSensitivity businessImpact notes - ${vendorFragment} + ${thirdPartyFragment} createdAt updatedAt } @@ -88,7 +88,7 @@ export async function execute( `; const variables = { - vendorRiskAssessmentId, + thirdPartyRiskAssessmentId, }; const responseData = await proboApiRequest.call(this, query, variables); diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/getService.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/getService.operation.ts similarity index 73% rename from packages/n8n-node/nodes/Probo/actions/vendor/getService.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/getService.operation.ts index a449c8576..42512940b 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/getService.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/getService.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor Service ID', - name: 'vendorServiceId', + displayName: 'ThirdParty Service ID', + name: 'thirdPartyServiceId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getService'], }, }, default: '', - description: 'The ID of the vendor service', + description: 'The ID of the thirdParty service', required: true, }, { @@ -38,17 +38,17 @@ export const description: INodeProperties[] = [ default: {}, displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['getService'], }, }, options: [ { - displayName: 'Include Vendor', - name: 'includeVendor', + displayName: 'Include ThirdParty', + name: 'includeThirdParty', type: 'boolean', default: false, - description: 'Whether to include vendor in the response', + description: 'Whether to include thirdParty in the response', }, ], }, @@ -58,26 +58,26 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorServiceId = this.getNodeParameter('vendorServiceId', itemIndex) as string; + const thirdPartyServiceId = this.getNodeParameter('thirdPartyServiceId', itemIndex) as string; const options = this.getNodeParameter('options', itemIndex, {}) as { - includeVendor?: boolean; + includeThirdParty?: boolean; }; - const vendorFragment = options.includeVendor - ? `vendor { + const thirdPartyFragment = options.includeThirdParty + ? `thirdParty { id name }` : ''; const query = ` - query GetVendorService($vendorServiceId: ID!) { - node(id: $vendorServiceId) { - ... on VendorService { + query GetThirdPartyService($thirdPartyServiceId: ID!) { + node(id: $thirdPartyServiceId) { + ... on ThirdPartyService { id name description - ${vendorFragment} + ${thirdPartyFragment} createdAt updatedAt } @@ -86,7 +86,7 @@ export async function execute( `; const variables = { - vendorServiceId, + thirdPartyServiceId, }; const responseData = await proboApiRequest.call(this, query, variables); diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/index.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/index.ts similarity index 71% rename from packages/n8n-node/nodes/Probo/actions/vendor/index.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/index.ts index ec5cdbe38..d9447a5d9 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/index.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/index.ts @@ -49,171 +49,171 @@ export const description: INodeProperties[] = [ noDataExpression: true, displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], }, }, options: [ { name: 'Create', value: 'create', - description: 'Create a new vendor', - action: 'Create a vendor', + description: 'Create a new third party', + action: 'Create a third party', }, { name: 'Create Contact', value: 'createContact', - description: 'Create a new vendor contact', - action: 'Create a vendor contact', + description: 'Create a new third party contact', + action: 'Create a third party contact', }, { name: 'Create Risk Assessment', value: 'createRiskAssessment', - description: 'Create a new vendor risk assessment', - action: 'Create a vendor risk assessment', + description: 'Create a new third party risk assessment', + action: 'Create a third party risk assessment', }, { name: 'Create Service', value: 'createService', - description: 'Create a new vendor service', - action: 'Create a vendor service', + description: 'Create a new third party service', + action: 'Create a third party service', }, { name: 'Delete', value: 'delete', - description: 'Delete a vendor', - action: 'Delete a vendor', + description: 'Delete a third party', + action: 'Delete a third party', }, { name: 'Delete Business Associate Agreement', value: 'deleteBusinessAssociateAgreement', - description: 'Delete a vendor business associate agreement', - action: 'Delete a vendor business associate agreement', + description: 'Delete a third party business associate agreement', + action: 'Delete a third party business associate agreement', }, { name: 'Delete Compliance Report', value: 'deleteComplianceReport', - description: 'Delete a vendor compliance report', - action: 'Delete a vendor compliance report', + description: 'Delete a third party compliance report', + action: 'Delete a third party compliance report', }, { name: 'Delete Contact', value: 'deleteContact', - description: 'Delete a vendor contact', - action: 'Delete a vendor contact', + description: 'Delete a third party contact', + action: 'Delete a third party contact', }, { name: 'Delete Data Privacy Agreement', value: 'deleteDataPrivacyAgreement', - description: 'Delete a vendor data privacy agreement', - action: 'Delete a vendor data privacy agreement', + description: 'Delete a third party data privacy agreement', + action: 'Delete a third party data privacy agreement', }, { name: 'Delete Service', value: 'deleteService', - description: 'Delete a vendor service', - action: 'Delete a vendor service', + description: 'Delete a third party service', + action: 'Delete a third party service', }, { name: 'Get', value: 'get', - description: 'Get a vendor', - action: 'Get a vendor', + description: 'Get a third party', + action: 'Get a third party', }, { name: 'Get Business Associate Agreement', value: 'getBusinessAssociateAgreement', - description: 'Get a vendor business associate agreement', - action: 'Get a vendor business associate agreement', + description: 'Get a third party business associate agreement', + action: 'Get a third party business associate agreement', }, { name: 'Get Contact', value: 'getContact', - description: 'Get a vendor contact', - action: 'Get a vendor contact', + description: 'Get a third party contact', + action: 'Get a third party contact', }, { name: 'Get Data Privacy Agreement', value: 'getDataPrivacyAgreement', - description: 'Get a vendor data privacy agreement', - action: 'Get a vendor data privacy agreement', + description: 'Get a third party data privacy agreement', + action: 'Get a third party data privacy agreement', }, { name: 'Get Many', value: 'getAll', - description: 'Get many vendors', - action: 'Get many vendors', + description: 'Get many third parties', + action: 'Get many third parties', }, { name: 'Get Many Compliance Reports', value: 'getAllComplianceReports', - description: 'Get many vendor compliance reports', - action: 'Get many vendor compliance reports', + description: 'Get many third party compliance reports', + action: 'Get many third party compliance reports', }, { name: 'Get Many Contacts', value: 'getAllContacts', - description: 'Get many vendor contacts', - action: 'Get many vendor contacts', + description: 'Get many third party contacts', + action: 'Get many third party contacts', }, { name: 'Get Many Risk Assessments', value: 'getAllRiskAssessments', - description: 'Get many vendor risk assessments', - action: 'Get many vendor risk assessments', + description: 'Get many third party risk assessments', + action: 'Get many third party risk assessments', }, { name: 'Get Many Services', value: 'getAllServices', - description: 'Get many vendor services', - action: 'Get many vendor services', + description: 'Get many third party services', + action: 'Get many third party services', }, { name: 'Get Risk Assessment', value: 'getRiskAssessment', - description: 'Get a vendor risk assessment', - action: 'Get a vendor risk assessment', + description: 'Get a third party risk assessment', + action: 'Get a third party risk assessment', }, { name: 'Get Service', value: 'getService', - description: 'Get a vendor service', - action: 'Get a vendor service', + description: 'Get a third party service', + action: 'Get a third party service', }, { name: 'Publish List', value: 'publish', - description: 'Publish the vendor register as a document version', - action: 'Publish the vendor register', + description: 'Publish the third party register as a document version', + action: 'Publish the third party register', }, { name: 'Update', value: 'update', - description: 'Update an existing vendor', - action: 'Update a vendor', + description: 'Update an existing third party', + action: 'Update a third party', }, { name: 'Update Business Associate Agreement', value: 'updateBusinessAssociateAgreement', - description: 'Update a vendor business associate agreement validity', - action: 'Update a vendor business associate agreement', + description: 'Update a third party business associate agreement validity', + action: 'Update a third party business associate agreement', }, { name: 'Update Contact', value: 'updateContact', - description: 'Update an existing vendor contact', - action: 'Update a vendor contact', + description: 'Update an existing third party contact', + action: 'Update a third party contact', }, { name: 'Update Data Privacy Agreement', value: 'updateDataPrivacyAgreement', - description: 'Update a vendor data privacy agreement validity', - action: 'Update a vendor data privacy agreement', + description: 'Update a third party data privacy agreement validity', + action: 'Update a third party data privacy agreement', }, { name: 'Update Service', value: 'updateService', - description: 'Update an existing vendor service', - action: 'Update a vendor service', + description: 'Update an existing third party service', + action: 'Update a third party service', }, ], default: 'create', diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/publish.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/publish.operation.ts similarity index 90% rename from packages/n8n-node/nodes/Probo/actions/vendor/publish.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/publish.operation.ts index 53a64be64..8a6fc39d2 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/publish.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/publish.operation.ts @@ -22,12 +22,12 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['publish'], }, }, default: '', - description: 'The ID of the organization whose vendor list to publish', + description: 'The ID of the organization whose thirdParty list to publish', required: true, }, { @@ -36,7 +36,7 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['publish'], }, }, @@ -49,7 +49,7 @@ export const description: INodeProperties[] = [ type: 'boolean', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['publish'], }, }, @@ -67,8 +67,8 @@ export async function execute( const minor = this.getNodeParameter('minor', itemIndex, false) as boolean; const query = ` - mutation PublishVendorList($input: PublishVendorListInput!) { - publishVendorList(input: $input) { + mutation PublishThirdPartyList($input: PublishThirdPartyListInput!) { + publishThirdPartyList(input: $input) { documentEdge { node { id diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/update.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/update.operation.ts similarity index 89% rename from packages/n8n-node/nodes/Probo/actions/vendor/update.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/update.operation.ts index 34eb1cb7f..9a2372d89 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/update.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/update.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor ID', - name: 'vendorId', + displayName: 'ThirdParty ID', + name: 'thirdPartyId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['update'], }, }, default: '', - description: 'The ID of the vendor to update', + description: 'The ID of the thirdParty to update', required: true, }, { @@ -36,12 +36,12 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['update'], }, }, default: '', - description: 'The name of the vendor', + description: 'The name of the thirdParty', }, { displayName: 'Description', @@ -52,12 +52,12 @@ export const description: INodeProperties[] = [ }, displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['update'], }, }, default: '', - description: 'The description of the vendor', + description: 'The description of the thirdParty', }, { displayName: 'Category', @@ -65,12 +65,12 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['update'], }, }, default: '', - description: 'The category of the vendor', + description: 'The category of the thirdParty', }, { displayName: 'Website URL', @@ -78,12 +78,12 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['update'], }, }, default: '', - description: 'The website URL of the vendor', + description: 'The website URL of the thirdParty', }, { displayName: 'Legal Name', @@ -91,12 +91,12 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['update'], }, }, default: '', - description: 'The legal name of the vendor', + description: 'The legal name of the thirdParty', }, { displayName: 'Headquarter Address', @@ -104,12 +104,12 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['update'], }, }, default: '', - description: 'The headquarter address of the vendor', + description: 'The headquarter address of the thirdParty', }, { displayName: 'Business Owner ID', @@ -117,7 +117,7 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['update'], }, }, @@ -130,7 +130,7 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['update'], }, }, @@ -143,12 +143,12 @@ export const description: INodeProperties[] = [ type: 'boolean', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['update'], }, }, default: false, - description: 'Whether to show the vendor on the trust center', + description: 'Whether to show the thirdParty on the trust center', }, { displayName: 'Additional Fields', @@ -158,7 +158,7 @@ export const description: INodeProperties[] = [ default: {}, displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['update'], }, }, @@ -212,7 +212,7 @@ export const description: INodeProperties[] = [ name: 'statusPageUrl', type: 'string', default: '', - description: 'The status page URL of the vendor', + description: 'The status page URL of the thirdParty', }, { displayName: 'Subprocessors List URL', @@ -240,7 +240,7 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorId = this.getNodeParameter('vendorId', itemIndex) as string; + const thirdPartyId = this.getNodeParameter('thirdPartyId', itemIndex) as string; const name = this.getNodeParameter('name', itemIndex, '') as string; const description = this.getNodeParameter('description', itemIndex, '') as string; const category = this.getNodeParameter('category', itemIndex, '') as string; @@ -265,9 +265,9 @@ export async function execute( }; const query = ` - mutation UpdateVendor($input: UpdateVendorInput!) { - updateVendor(input: $input) { - vendor { + mutation UpdateThirdParty($input: UpdateThirdPartyInput!) { + updateThirdParty(input: $input) { + thirdParty { id name description @@ -294,7 +294,7 @@ export async function execute( } `; - const input: Record = { id: vendorId }; + const input: Record = { id: thirdPartyId }; if (name) input.name = name; if (description !== undefined) input.description = description === '' ? null : description; if (category) input.category = category; diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/updateBusinessAssociateAgreement.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/updateBusinessAssociateAgreement.operation.ts similarity index 80% rename from packages/n8n-node/nodes/Probo/actions/vendor/updateBusinessAssociateAgreement.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/updateBusinessAssociateAgreement.operation.ts index 8ca0ff509..b6e9e6bf6 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/updateBusinessAssociateAgreement.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/updateBusinessAssociateAgreement.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor ID', - name: 'vendorId', + displayName: 'ThirdParty ID', + name: 'thirdPartyId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['updateBusinessAssociateAgreement'], }, }, default: '', - description: 'The ID of the vendor', + description: 'The ID of the thirdParty', required: true, }, { @@ -36,7 +36,7 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['updateBusinessAssociateAgreement'], }, }, @@ -49,7 +49,7 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['updateBusinessAssociateAgreement'], }, }, @@ -62,14 +62,14 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorId = this.getNodeParameter('vendorId', itemIndex) as string; + const thirdPartyId = this.getNodeParameter('thirdPartyId', itemIndex) as string; const validFrom = this.getNodeParameter('validFrom', itemIndex, '') as string; const validUntil = this.getNodeParameter('validUntil', itemIndex, '') as string; const query = ` - mutation UpdateVendorBusinessAssociateAgreement($input: UpdateVendorBusinessAssociateAgreementInput!) { - updateVendorBusinessAssociateAgreement(input: $input) { - vendorBusinessAssociateAgreement { + mutation UpdateThirdPartyBusinessAssociateAgreement($input: UpdateThirdPartyBusinessAssociateAgreementInput!) { + updateThirdPartyBusinessAssociateAgreement(input: $input) { + thirdPartyBusinessAssociateAgreement { id validFrom validUntil @@ -78,7 +78,7 @@ export async function execute( } `; - const input: Record = { vendorId }; + const input: Record = { thirdPartyId }; if (validFrom) input.validFrom = validFrom; if (validUntil) input.validUntil = validUntil; diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/updateContact.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/updateContact.operation.ts similarity index 85% rename from packages/n8n-node/nodes/Probo/actions/vendor/updateContact.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/updateContact.operation.ts index a6e3838d5..0942c3b44 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/updateContact.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/updateContact.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor Contact ID', - name: 'vendorContactId', + displayName: 'ThirdParty Contact ID', + name: 'thirdPartyContactId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['updateContact'], }, }, default: '', - description: 'The ID of the vendor contact to update', + description: 'The ID of the thirdParty contact to update', required: true, }, { @@ -38,7 +38,7 @@ export const description: INodeProperties[] = [ default: {}, displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['updateContact'], }, }, @@ -80,7 +80,7 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorContactId = this.getNodeParameter('vendorContactId', itemIndex) as string; + const thirdPartyContactId = this.getNodeParameter('thirdPartyContactId', itemIndex) as string; const additionalFields = this.getNodeParameter('additionalFields', itemIndex, {}) as { fullName?: string; email?: string; @@ -89,9 +89,9 @@ export async function execute( }; const query = ` - mutation UpdateVendorContact($input: UpdateVendorContactInput!) { - updateVendorContact(input: $input) { - vendorContact { + mutation UpdateThirdPartyContact($input: UpdateThirdPartyContactInput!) { + updateThirdPartyContact(input: $input) { + thirdPartyContact { id fullName email @@ -104,7 +104,7 @@ export async function execute( } `; - const input: Record = { id: vendorContactId }; + const input: Record = { id: thirdPartyContactId }; if (additionalFields.fullName !== undefined) input.fullName = additionalFields.fullName === '' ? null : additionalFields.fullName; if (additionalFields.email !== undefined) input.email = additionalFields.email === '' ? null : additionalFields.email; if (additionalFields.phone !== undefined) input.phone = additionalFields.phone === '' ? null : additionalFields.phone; diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/updateDataPrivacyAgreement.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/updateDataPrivacyAgreement.operation.ts similarity index 80% rename from packages/n8n-node/nodes/Probo/actions/vendor/updateDataPrivacyAgreement.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/updateDataPrivacyAgreement.operation.ts index 6233042dd..3d0252204 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/updateDataPrivacyAgreement.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/updateDataPrivacyAgreement.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor ID', - name: 'vendorId', + displayName: 'ThirdParty ID', + name: 'thirdPartyId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['updateDataPrivacyAgreement'], }, }, default: '', - description: 'The ID of the vendor', + description: 'The ID of the thirdParty', required: true, }, { @@ -36,7 +36,7 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['updateDataPrivacyAgreement'], }, }, @@ -49,7 +49,7 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['updateDataPrivacyAgreement'], }, }, @@ -62,14 +62,14 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorId = this.getNodeParameter('vendorId', itemIndex) as string; + const thirdPartyId = this.getNodeParameter('thirdPartyId', itemIndex) as string; const validFrom = this.getNodeParameter('validFrom', itemIndex, '') as string; const validUntil = this.getNodeParameter('validUntil', itemIndex, '') as string; const query = ` - mutation UpdateVendorDataPrivacyAgreement($input: UpdateVendorDataPrivacyAgreementInput!) { - updateVendorDataPrivacyAgreement(input: $input) { - vendorDataPrivacyAgreement { + mutation UpdateThirdPartyDataPrivacyAgreement($input: UpdateThirdPartyDataPrivacyAgreementInput!) { + updateThirdPartyDataPrivacyAgreement(input: $input) { + thirdPartyDataPrivacyAgreement { id validFrom validUntil @@ -78,7 +78,7 @@ export async function execute( } `; - const input: Record = { vendorId }; + const input: Record = { thirdPartyId }; if (validFrom) input.validFrom = validFrom; if (validUntil) input.validUntil = validUntil; diff --git a/packages/n8n-node/nodes/Probo/actions/vendor/updateService.operation.ts b/packages/n8n-node/nodes/Probo/actions/thirdParty/updateService.operation.ts similarity index 76% rename from packages/n8n-node/nodes/Probo/actions/vendor/updateService.operation.ts rename to packages/n8n-node/nodes/Probo/actions/thirdParty/updateService.operation.ts index 193919b01..3d0fdff2e 100644 --- a/packages/n8n-node/nodes/Probo/actions/vendor/updateService.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/thirdParty/updateService.operation.ts @@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions'; export const description: INodeProperties[] = [ { - displayName: 'Vendor Service ID', - name: 'vendorServiceId', + displayName: 'ThirdParty Service ID', + name: 'thirdPartyServiceId', type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['updateService'], }, }, default: '', - description: 'The ID of the vendor service to update', + description: 'The ID of the thirdParty service to update', required: true, }, { @@ -36,12 +36,12 @@ export const description: INodeProperties[] = [ type: 'string', displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['updateService'], }, }, default: '', - description: 'The name of the vendor service', + description: 'The name of the thirdParty service', }, { displayName: 'Description', @@ -52,12 +52,12 @@ export const description: INodeProperties[] = [ }, displayOptions: { show: { - resource: ['vendor'], + resource: ['thirdParty'], operation: ['updateService'], }, }, default: '', - description: 'The description of the vendor service', + description: 'The description of the thirdParty service', }, ]; @@ -65,14 +65,14 @@ export async function execute( this: IExecuteFunctions, itemIndex: number, ): Promise { - const vendorServiceId = this.getNodeParameter('vendorServiceId', itemIndex) as string; + const thirdPartyServiceId = this.getNodeParameter('thirdPartyServiceId', itemIndex) as string; const name = this.getNodeParameter('name', itemIndex, '') as string; const description = this.getNodeParameter('description', itemIndex, '') as string; const query = ` - mutation UpdateVendorService($input: UpdateVendorServiceInput!) { - updateVendorService(input: $input) { - vendorService { + mutation UpdateThirdPartyService($input: UpdateThirdPartyServiceInput!) { + updateThirdPartyService(input: $input) { + thirdPartyService { id name description @@ -83,7 +83,7 @@ export async function execute( } `; - const input: Record = { id: vendorServiceId }; + const input: Record = { id: thirdPartyServiceId }; if (name) input.name = name; if (description !== undefined) input.description = description === '' ? null : description; diff --git a/packages/n8n-node/nodes/Probo/actions/webhook/create.operation.ts b/packages/n8n-node/nodes/Probo/actions/webhook/create.operation.ts index 99298e8c4..4682df13f 100644 --- a/packages/n8n-node/nodes/Probo/actions/webhook/create.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/webhook/create.operation.ts @@ -61,12 +61,12 @@ export const description: INodeProperties[] = [ { name: 'Obligation Created', value: 'OBLIGATION_CREATED' }, { name: 'Obligation Deleted', value: 'OBLIGATION_DELETED' }, { name: 'Obligation Updated', value: 'OBLIGATION_UPDATED' }, + { name: 'Third Party Created', value: 'THIRD_PARTY_CREATED' }, + { name: 'Third Party Deleted', value: 'THIRD_PARTY_DELETED' }, + { name: 'Third Party Updated', value: 'THIRD_PARTY_UPDATED' }, { name: 'User Created', value: 'USER_CREATED' }, { name: 'User Deleted', value: 'USER_DELETED' }, { name: 'User Updated', value: 'USER_UPDATED' }, - { name: 'Vendor Created', value: 'VENDOR_CREATED' }, - { name: 'Vendor Deleted', value: 'VENDOR_DELETED' }, - { name: 'Vendor Updated', value: 'VENDOR_UPDATED' }, ], default: [], description: 'The event types to subscribe to', diff --git a/packages/n8n-node/nodes/Probo/actions/webhook/update.operation.ts b/packages/n8n-node/nodes/Probo/actions/webhook/update.operation.ts index ec6c34c50..bd3529e4d 100644 --- a/packages/n8n-node/nodes/Probo/actions/webhook/update.operation.ts +++ b/packages/n8n-node/nodes/Probo/actions/webhook/update.operation.ts @@ -60,12 +60,12 @@ export const description: INodeProperties[] = [ { name: 'Obligation Created', value: 'OBLIGATION_CREATED' }, { name: 'Obligation Deleted', value: 'OBLIGATION_DELETED' }, { name: 'Obligation Updated', value: 'OBLIGATION_UPDATED' }, + { name: 'Third Party Created', value: 'THIRD_PARTY_CREATED' }, + { name: 'Third Party Deleted', value: 'THIRD_PARTY_DELETED' }, + { name: 'Third Party Updated', value: 'THIRD_PARTY_UPDATED' }, { name: 'User Created', value: 'USER_CREATED' }, { name: 'User Deleted', value: 'USER_DELETED' }, { name: 'User Updated', value: 'USER_UPDATED' }, - { name: 'Vendor Created', value: 'VENDOR_CREATED' }, - { name: 'Vendor Deleted', value: 'VENDOR_DELETED' }, - { name: 'Vendor Updated', value: 'VENDOR_UPDATED' }, ], default: [], description: 'The event types to subscribe to (replaces existing selection)', diff --git a/packages/vendors/LICENSE.md b/packages/third-parties/LICENSE.md similarity index 74% rename from packages/vendors/LICENSE.md rename to packages/third-parties/LICENSE.md index feea9ea6e..08da4f6f4 100644 --- a/packages/vendors/LICENSE.md +++ b/packages/third-parties/LICENSE.md @@ -1,6 +1,6 @@ -# Vendors +# Third parties -The [vendors.json](vendors.json) file and derived files contains data about various vendors and their security certifications. This data is licensed under the Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license. +The [data.json](data.json) file and derived files contain data about various third parties and their security certifications. This data is licensed under the Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license. ## License Requirements diff --git a/packages/vendors/README.md b/packages/third-parties/README.md similarity index 53% rename from packages/vendors/README.md rename to packages/third-parties/README.md index 748dad590..14e769c5a 100644 --- a/packages/vendors/README.md +++ b/packages/third-parties/README.md @@ -1,23 +1,23 @@ -# Vendors library +# ThirdParties library -A curated collection of software vendor information for use in vendor management and compliance activities. +A curated collection of software thirdParty information for use in thirdParty management and compliance activities. ## Overview -This package provides a structured dataset of software vendors and service providers with comprehensive metadata including: +This package provides a structured dataset of software thirdParties and service providers with comprehensive metadata including: -- Basic vendor information (name, website, description) +- Basic thirdParty information (name, website, description) - Legal documentation URLs (privacy policy, terms of service, etc.) - Compliance certifications - Security information ## Data Structure -Each vendor entry follows the structure defined in `data.d.ts`, including fields for: +Each thirdParty entry follows the structure defined in `data.d.ts`, including fields for: -- `name`: Display name of the vendor +- `name`: Display name of the thirdParty - `legalName`: Legal business name -- `websiteUrl`: Vendor's website +- `websiteUrl`: ThirdParty's website - `privacyPolicyUrl`: URL to privacy policy - `termsOfServiceUrl`: URL to terms of service - And many more compliance-related URLs and metadata diff --git a/packages/vendors/VENDORS.md b/packages/third-parties/THIRD_PARTIES.md similarity index 99% rename from packages/vendors/VENDORS.md rename to packages/third-parties/THIRD_PARTIES.md index 354abf920..744cbb758 100644 --- a/packages/vendors/VENDORS.md +++ b/packages/third-parties/THIRD_PARTIES.md @@ -1,4 +1,4 @@ -# Vendors +# ThirdParties ## Table of Contents by Category @@ -2727,7 +2727,7 @@ AI-powered global spend platform offering corporate cards, expense management, b ## Ramp -Corporate spend-management platform offering corporate cards, expense automation, bill pay, and vendor management for finance teams. +Corporate spend-management platform offering corporate cards, expense automation, bill pay, and thirdParty management for finance teams. **Legal Name:** Ramp Business Corporation @@ -2861,7 +2861,7 @@ Puzzle provides modern, real-time accounting software that gives startups automa ## Probo -Probo is an open-source compliance platform that helps startups achieve SOC 2 and ISO 27001 certifications quickly and affordably, with expert guidance and no vendor lock-in. +Probo is an open-source compliance platform that helps startups achieve SOC 2 and ISO 27001 certifications quickly and affordably, with expert guidance and no thirdParty lock-in. **Legal Name:** Probo Inc. @@ -3057,7 +3057,7 @@ Comprehensive public cloud computing platform offering infrastructure, data anal - Cloud Computing Compliance Controls Catalog (C5) - CSA - GSMA SAS-SM -- Higher Education Cloud Vendor Assessment Tool (HECVAT) +- Higher Education Cloud ThirdParty Assessment Tool (HECVAT) - ISO 9001:2015 - ISO 22301:2019 & BS EN ISO 22301:2019 - ISO 50001:2018 diff --git a/packages/vendors/data.d.ts b/packages/third-parties/data.d.ts similarity index 61% rename from packages/vendors/data.d.ts rename to packages/third-parties/data.d.ts index 4476863e8..ed6d270f0 100644 --- a/packages/vendors/data.d.ts +++ b/packages/third-parties/data.d.ts @@ -13,13 +13,13 @@ // PERFORMANCE OF THIS SOFTWARE. /** - * Type definitions for vendor data + * Type definitions for thirdParty data */ /** - * Category types for vendors + * Category types for thirdParties */ -export type VendorCategory = +export type ThirdPartyCategory = | "ANALYTICS" | "CLOUD_MONITORING" | "CLOUD_PROVIDER" @@ -44,72 +44,72 @@ export type VendorCategory = | "VERSION_CONTROL"; /** - * Represents a software vendor or service provider with associated metadata + * Represents a software thirdParty or service provider with associated metadata */ -export interface Vendor { - /** Display name of the vendor */ +export interface ThirdParty { + /** Display name of the thirdParty */ name: string; - /** Legal business name of the vendor */ + /** Legal business name of the thirdParty */ legalName?: string; /** Physical headquarters address */ headquarterAddress?: string; - /** Vendor's website URL */ + /** ThirdParty's website URL */ websiteUrl: string; - /** URL to vendor's privacy policy */ + /** URL to thirdParty's privacy policy */ privacyPolicyUrl?: string; - /** URL to vendor's terms of service */ + /** URL to thirdParty's terms of service */ termsOfServiceUrl?: string; - /** URL to vendor's service level agreement */ + /** URL to thirdParty's service level agreement */ serviceLevelAgreementUrl?: string; /** URL to service software agreement */ serviceSoftwareAgreementUrl?: string; - /** URL to vendor's data processing agreement */ + /** URL to thirdParty's data processing agreement */ dataProcessingAgreementUrl?: string; - /** URL to vendor's list of subprocessors */ + /** URL to thirdParty's list of subprocessors */ subprocessorsListUrl?: string; - /** URL to vendor's business associate agreement */ + /** URL to thirdParty's business associate agreement */ businessAssociateAgreementUrl?: string; - /** Short description of the vendor/service */ + /** Short description of the thirdParty/service */ description?: string; - /** Primary category for the vendor */ - category?: VendorCategory; + /** Primary category for the thirdParty */ + category?: ThirdPartyCategory; - /** Security or compliance certifications held by the vendor */ + /** Security or compliance certifications held by the thirdParty */ certifications?: string[]; - /** URL to vendor's security page */ + /** URL to thirdParty's security page */ securityPageUrl?: string; - /** URL to vendor's trust page */ + /** URL to thirdParty's trust page */ trustPageUrl?: string; - /** URL to vendor's status page */ + /** URL to thirdParty's status page */ statusPageUrl?: string; - /** Countries where the vendor is located */ + /** Countries where the thirdParty is located */ countries?: CountryCode[]; } /** - * Array of vendor data + * Array of thirdParty data */ -export type Vendors = Vendor[]; +export type ThirdParties = ThirdParty[]; /** - * Default export representing the entire vendor dataset + * Default export representing the entire thirdParty dataset */ -declare const data: Vendors; +declare const data: ThirdParties; export default data; diff --git a/packages/vendors/data.json b/packages/third-parties/data.json similarity index 100% rename from packages/vendors/data.json rename to packages/third-parties/data.json diff --git a/packages/vendors/package.json b/packages/third-parties/package.json similarity index 90% rename from packages/vendors/package.json rename to packages/third-parties/package.json index c5a4b1d18..58860da51 100644 --- a/packages/vendors/package.json +++ b/packages/third-parties/package.json @@ -1,5 +1,5 @@ { - "name": "@probo/vendors", + "name": "@probo/third-parties", "version": "0.0.1", "publishConfig": { "access": "public" diff --git a/packages/third-parties/scripts/build-md.mjs b/packages/third-parties/scripts/build-md.mjs new file mode 100644 index 000000000..4542a23fc --- /dev/null +++ b/packages/third-parties/scripts/build-md.mjs @@ -0,0 +1,143 @@ +import { readFile } from "node:fs/promises"; +import { createWriteStream } from "node:fs"; +import path from "node:path"; + +const data = await readFile(path.join(import.meta.dirname, '../data.json'), 'utf8'); +const thirdParties = JSON.parse(data); + +const output = path.join(import.meta.dirname, '../VENDORS.md'); +const file = createWriteStream(output); + +const formatAsList = (array) => { + if (!array || array.length === 0) return ''; + if (typeof array === 'string') { + return array.split(',').map(item => `- ${item.trim()}`).join('\n'); + } + return array.map(item => `- ${item}`).join('\n'); +}; + +file.write('# ThirdParties\n\n'); +file.write('## Table of Contents by Category\n\n'); + +const categoriesMap = new Map(); + +for (const thirdParty of thirdParties) { + const category = (thirdParty.category || thirdParty.categories || 'Uncategorized'); + + if (!categoriesMap.has(category)) { + categoriesMap.set(category, []); + } + + categoriesMap.get(category).push(thirdParty.name); +} + +const sortedCategories = [...categoriesMap.keys()].sort(); + +for (const category of sortedCategories) { + file.write(`### ${category}\n\n`); + + const thirdPartiesInCategory = categoriesMap.get(category).sort(); + for (const thirdPartyName of thirdPartiesInCategory) { + // Create proper anchor by: + // 1. Converting to lowercase + // 2. Replacing spaces with hyphens + // 3. Removing parentheses, dots, and other special characters + const anchor = thirdPartyName.toLowerCase() + .replace(/\s+/g, '-') + .replace(/[\(\)\.]/g, '') + .replace(/[^a-z0-9\-]/g, ''); + + file.write(`- [${thirdPartyName}](#${anchor})\n`); + } + + file.write('\n'); +} + +file.write('---\n\n'); + +for (const thirdParty of thirdParties) { + file.write(`## ${thirdParty.name}\n\n`); + if (thirdParty.description) { + file.write(`${thirdParty.description}\n\n`); + } + + if (thirdParty.legalName) { + file.write(`**Legal Name:** ${thirdParty.legalName}\n\n`); + } + + if (thirdParty.headquarterAddress) { + file.write(`**Headquarters:** ${thirdParty.headquarterAddress}\n\n`); + } + + file.write('### Links\n\n'); + file.write('| Resource | Link |\n'); + file.write('|----------|------|\n'); + + if (thirdParty.websiteUrl) { + file.write(`| Website | [Link](${thirdParty.websiteUrl}) |\n`); + } + + if (thirdParty.privacyPolicyUrl) { + file.write(`| Privacy Policy | [Link](${thirdParty.privacyPolicyUrl}) |\n`); + } + + if (thirdParty.termsOfServiceUrl && thirdParty.termsOfServiceUrl !== 'undefined') { + file.write(`| Terms of Service | [Link](${thirdParty.termsOfServiceUrl}) |\n`); + } + + if (thirdParty.serviceLevelAgreementUrl && thirdParty.serviceLevelAgreementUrl !== 'undefined') { + file.write(`| Service Level Agreement | [Link](${thirdParty.serviceLevelAgreementUrl}) |\n`); + } + + if (thirdParty.securityPageUrl && thirdParty.securityPageUrl !== 'undefined') { + file.write(`| Security Page | [Link](${thirdParty.securityPageUrl}) |\n`); + } + + if (thirdParty.trustPageUrl && thirdParty.trustPageUrl !== 'undefined') { + file.write(`| Trust Page | [Link](${thirdParty.trustPageUrl}) |\n`); + } + + if (thirdParty.statusPageUrl && thirdParty.statusPageUrl !== 'undefined') { + file.write(`| Status Page | [Link](${thirdParty.statusPageUrl}) |\n`); + } + + if (thirdParty.dataProcessingAgreementUrl && thirdParty.dataProcessingAgreementUrl !== 'undefined') { + file.write(`| Data Processing Agreement | [Link](${thirdParty.dataProcessingAgreementUrl}) |\n`); + } + + if (thirdParty.businessAssociateAgreementUrl && thirdParty.businessAssociateAgreementUrl !== 'undefined') { + file.write(`| Business Associate Agreement | [Link](${thirdParty.businessAssociateAgreementUrl}) |\n`); + } + + if (thirdParty.serviceSoftwareAgreementUrl && thirdParty.serviceSoftwareAgreementUrl !== 'undefined') { + file.write(`| Service Software Agreement | [Link](${thirdParty.serviceSoftwareAgreementUrl}) |\n`); + } + + if (thirdParty.subprocessorsListUrl && thirdParty.subprocessorsListUrl !== 'undefined') { + file.write(`| Subprocessors List | [Link](${thirdParty.subprocessorsListUrl}) |\n`); + } + + file.write('\n'); + + if (thirdParty.categories && thirdParty.categories !== 'undefined') { + file.write(`**Categories:** ${thirdParty.categories}\n\n`); + } else if (thirdParty.category && thirdParty.category !== 'undefined') { + file.write(`**Category:** ${thirdParty.category}\n\n`); + } + + if (thirdParty.certifications && thirdParty.certifications !== 'undefined') { + file.write('### Certifications\n\n'); + file.write(formatAsList(thirdParty.certifications)); + file.write('\n\n'); + } + + if (thirdParty.subprocessors && thirdParty.subprocessors !== 'undefined') { + file.write('### Subprocessors\n\n'); + file.write(formatAsList(thirdParty.subprocessors)); + file.write('\n\n'); + } + + file.write('---\n\n'); +} + +file.end(); diff --git a/packages/ui/src/Atoms/Vendors/Brex.tsx b/packages/ui/src/Atoms/ThirdParties/Brex.tsx similarity index 100% rename from packages/ui/src/Atoms/Vendors/Brex.tsx rename to packages/ui/src/Atoms/ThirdParties/Brex.tsx diff --git a/packages/ui/src/Atoms/Vendors/Cloudflare.tsx b/packages/ui/src/Atoms/ThirdParties/Cloudflare.tsx similarity index 100% rename from packages/ui/src/Atoms/Vendors/Cloudflare.tsx rename to packages/ui/src/Atoms/ThirdParties/Cloudflare.tsx diff --git a/packages/ui/src/Atoms/Vendors/DocuSign.tsx b/packages/ui/src/Atoms/ThirdParties/DocuSign.tsx similarity index 100% rename from packages/ui/src/Atoms/Vendors/DocuSign.tsx rename to packages/ui/src/Atoms/ThirdParties/DocuSign.tsx diff --git a/packages/ui/src/Atoms/Vendors/Figma.tsx b/packages/ui/src/Atoms/ThirdParties/Figma.tsx similarity index 100% rename from packages/ui/src/Atoms/Vendors/Figma.tsx rename to packages/ui/src/Atoms/ThirdParties/Figma.tsx diff --git a/packages/ui/src/Atoms/Vendors/GitHub.tsx b/packages/ui/src/Atoms/ThirdParties/GitHub.tsx similarity index 100% rename from packages/ui/src/Atoms/Vendors/GitHub.tsx rename to packages/ui/src/Atoms/ThirdParties/GitHub.tsx diff --git a/packages/ui/src/Atoms/Vendors/Google.tsx b/packages/ui/src/Atoms/ThirdParties/Google.tsx similarity index 100% rename from packages/ui/src/Atoms/Vendors/Google.tsx rename to packages/ui/src/Atoms/ThirdParties/Google.tsx diff --git a/packages/ui/src/Atoms/Vendors/HubSpot.tsx b/packages/ui/src/Atoms/ThirdParties/HubSpot.tsx similarity index 100% rename from packages/ui/src/Atoms/Vendors/HubSpot.tsx rename to packages/ui/src/Atoms/ThirdParties/HubSpot.tsx diff --git a/packages/ui/src/Atoms/Vendors/Intercom.tsx b/packages/ui/src/Atoms/ThirdParties/Intercom.tsx similarity index 100% rename from packages/ui/src/Atoms/Vendors/Intercom.tsx rename to packages/ui/src/Atoms/ThirdParties/Intercom.tsx diff --git a/packages/ui/src/Atoms/Vendors/Linear.tsx b/packages/ui/src/Atoms/ThirdParties/Linear.tsx similarity index 100% rename from packages/ui/src/Atoms/Vendors/Linear.tsx rename to packages/ui/src/Atoms/ThirdParties/Linear.tsx diff --git a/packages/ui/src/Atoms/Vendors/Microsoft.tsx b/packages/ui/src/Atoms/ThirdParties/Microsoft.tsx similarity index 100% rename from packages/ui/src/Atoms/Vendors/Microsoft.tsx rename to packages/ui/src/Atoms/ThirdParties/Microsoft.tsx diff --git a/packages/ui/src/Atoms/Vendors/Notion.tsx b/packages/ui/src/Atoms/ThirdParties/Notion.tsx similarity index 100% rename from packages/ui/src/Atoms/Vendors/Notion.tsx rename to packages/ui/src/Atoms/ThirdParties/Notion.tsx diff --git a/packages/ui/src/Atoms/Vendors/OnePassword.tsx b/packages/ui/src/Atoms/ThirdParties/OnePassword.tsx similarity index 100% rename from packages/ui/src/Atoms/Vendors/OnePassword.tsx rename to packages/ui/src/Atoms/ThirdParties/OnePassword.tsx diff --git a/packages/ui/src/Atoms/Vendors/OpenAI.tsx b/packages/ui/src/Atoms/ThirdParties/OpenAI.tsx similarity index 100% rename from packages/ui/src/Atoms/Vendors/OpenAI.tsx rename to packages/ui/src/Atoms/ThirdParties/OpenAI.tsx diff --git a/packages/ui/src/Atoms/Vendors/Resend.tsx b/packages/ui/src/Atoms/ThirdParties/Resend.tsx similarity index 100% rename from packages/ui/src/Atoms/Vendors/Resend.tsx rename to packages/ui/src/Atoms/ThirdParties/Resend.tsx diff --git a/packages/ui/src/Atoms/Vendors/Sentry.tsx b/packages/ui/src/Atoms/ThirdParties/Sentry.tsx similarity index 100% rename from packages/ui/src/Atoms/Vendors/Sentry.tsx rename to packages/ui/src/Atoms/ThirdParties/Sentry.tsx diff --git a/packages/ui/src/Atoms/Vendors/Slack.tsx b/packages/ui/src/Atoms/ThirdParties/Slack.tsx similarity index 100% rename from packages/ui/src/Atoms/Vendors/Slack.tsx rename to packages/ui/src/Atoms/ThirdParties/Slack.tsx diff --git a/packages/ui/src/Atoms/Vendors/Supabase.tsx b/packages/ui/src/Atoms/ThirdParties/Supabase.tsx similarity index 100% rename from packages/ui/src/Atoms/Vendors/Supabase.tsx rename to packages/ui/src/Atoms/ThirdParties/Supabase.tsx diff --git a/packages/ui/src/Atoms/Vendors/Tally.tsx b/packages/ui/src/Atoms/ThirdParties/Tally.tsx similarity index 100% rename from packages/ui/src/Atoms/Vendors/Tally.tsx rename to packages/ui/src/Atoms/ThirdParties/Tally.tsx diff --git a/packages/ui/src/Atoms/Vendors/VendorLogo.tsx b/packages/ui/src/Atoms/ThirdParties/ThirdPartyLogo.tsx similarity index 85% rename from packages/ui/src/Atoms/Vendors/VendorLogo.tsx rename to packages/ui/src/Atoms/ThirdParties/ThirdPartyLogo.tsx index 9b3059ed9..ce420de7d 100644 --- a/packages/ui/src/Atoms/Vendors/VendorLogo.tsx +++ b/packages/ui/src/Atoms/ThirdParties/ThirdPartyLogo.tsx @@ -33,7 +33,7 @@ import { Slack } from "./Slack"; import { Supabase } from "./Supabase"; import { Tally } from "./Tally"; -const vendors: Record>> = { +const thirdParties: Record>> = { BREX: Brex, CLOUDFLARE: Cloudflare, DOCUSIGN: DocuSign, @@ -57,15 +57,15 @@ const vendors: Record>> = { TALLY: Tally, }; -type VendorLogoProps = ComponentProps<"svg"> & { - /** The vendor/brand name (case-insensitive, supports enum values like GOOGLE_WORKSPACE). */ - vendor: string; +type ThirdPartyLogoProps = ComponentProps<"svg"> & { + /** The thirdParty/brand name (case-insensitive, supports enum values like GOOGLE_WORKSPACE). */ + thirdParty: string; /** When true, renders the SVG in monochrome, adapting to the current theme. */ tint?: boolean; }; -export function VendorLogo({ vendor, tint, ...props }: VendorLogoProps) { - const Component = vendors[vendor.toUpperCase()]; +export function ThirdPartyLogo({ thirdParty, tint, ...props }: ThirdPartyLogoProps) { + const Component = thirdParties[thirdParty.toUpperCase()]; if (!Component) return null; if (tint) { diff --git a/packages/ui/src/Atoms/Vendors/index.ts b/packages/ui/src/Atoms/ThirdParties/index.ts similarity index 92% rename from packages/ui/src/Atoms/Vendors/index.ts rename to packages/ui/src/Atoms/ThirdParties/index.ts index bad051ada..26e572fbe 100644 --- a/packages/ui/src/Atoms/Vendors/index.ts +++ b/packages/ui/src/Atoms/ThirdParties/index.ts @@ -16,4 +16,4 @@ export { Sentry } from "./Sentry"; export { Slack } from "./Slack"; export { Supabase } from "./Supabase"; export { Tally } from "./Tally"; -export { VendorLogo } from "./VendorLogo"; +export { ThirdPartyLogo } from "./ThirdPartyLogo"; diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 87b24fdb5..c20a3cae3 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -61,7 +61,7 @@ export { Row, RowButton, } from "./Atoms/DataTable/DataTable"; -export * from "./Atoms/Vendors"; +export * from "./Atoms/ThirdParties"; // Molecules export { diff --git a/packages/vendors/scripts/build-md.mjs b/packages/vendors/scripts/build-md.mjs deleted file mode 100644 index 694554b7b..000000000 --- a/packages/vendors/scripts/build-md.mjs +++ /dev/null @@ -1,143 +0,0 @@ -import { readFile } from "node:fs/promises"; -import { createWriteStream } from "node:fs"; -import path from "node:path"; - -const data = await readFile(path.join(import.meta.dirname, '../data.json'), 'utf8'); -const vendors = JSON.parse(data); - -const output = path.join(import.meta.dirname, '../VENDORS.md'); -const file = createWriteStream(output); - -const formatAsList = (array) => { - if (!array || array.length === 0) return ''; - if (typeof array === 'string') { - return array.split(',').map(item => `- ${item.trim()}`).join('\n'); - } - return array.map(item => `- ${item}`).join('\n'); -}; - -file.write('# Vendors\n\n'); -file.write('## Table of Contents by Category\n\n'); - -const categoriesMap = new Map(); - -for (const vendor of vendors) { - const category = (vendor.category || vendor.categories || 'Uncategorized'); - - if (!categoriesMap.has(category)) { - categoriesMap.set(category, []); - } - - categoriesMap.get(category).push(vendor.name); -} - -const sortedCategories = [...categoriesMap.keys()].sort(); - -for (const category of sortedCategories) { - file.write(`### ${category}\n\n`); - - const vendorsInCategory = categoriesMap.get(category).sort(); - for (const vendorName of vendorsInCategory) { - // Create proper anchor by: - // 1. Converting to lowercase - // 2. Replacing spaces with hyphens - // 3. Removing parentheses, dots, and other special characters - const anchor = vendorName.toLowerCase() - .replace(/\s+/g, '-') - .replace(/[\(\)\.]/g, '') - .replace(/[^a-z0-9\-]/g, ''); - - file.write(`- [${vendorName}](#${anchor})\n`); - } - - file.write('\n'); -} - -file.write('---\n\n'); - -for (const vendor of vendors) { - file.write(`## ${vendor.name}\n\n`); - if (vendor.description) { - file.write(`${vendor.description}\n\n`); - } - - if (vendor.legalName) { - file.write(`**Legal Name:** ${vendor.legalName}\n\n`); - } - - if (vendor.headquarterAddress) { - file.write(`**Headquarters:** ${vendor.headquarterAddress}\n\n`); - } - - file.write('### Links\n\n'); - file.write('| Resource | Link |\n'); - file.write('|----------|------|\n'); - - if (vendor.websiteUrl) { - file.write(`| Website | [Link](${vendor.websiteUrl}) |\n`); - } - - if (vendor.privacyPolicyUrl) { - file.write(`| Privacy Policy | [Link](${vendor.privacyPolicyUrl}) |\n`); - } - - if (vendor.termsOfServiceUrl && vendor.termsOfServiceUrl !== 'undefined') { - file.write(`| Terms of Service | [Link](${vendor.termsOfServiceUrl}) |\n`); - } - - if (vendor.serviceLevelAgreementUrl && vendor.serviceLevelAgreementUrl !== 'undefined') { - file.write(`| Service Level Agreement | [Link](${vendor.serviceLevelAgreementUrl}) |\n`); - } - - if (vendor.securityPageUrl && vendor.securityPageUrl !== 'undefined') { - file.write(`| Security Page | [Link](${vendor.securityPageUrl}) |\n`); - } - - if (vendor.trustPageUrl && vendor.trustPageUrl !== 'undefined') { - file.write(`| Trust Page | [Link](${vendor.trustPageUrl}) |\n`); - } - - if (vendor.statusPageUrl && vendor.statusPageUrl !== 'undefined') { - file.write(`| Status Page | [Link](${vendor.statusPageUrl}) |\n`); - } - - if (vendor.dataProcessingAgreementUrl && vendor.dataProcessingAgreementUrl !== 'undefined') { - file.write(`| Data Processing Agreement | [Link](${vendor.dataProcessingAgreementUrl}) |\n`); - } - - if (vendor.businessAssociateAgreementUrl && vendor.businessAssociateAgreementUrl !== 'undefined') { - file.write(`| Business Associate Agreement | [Link](${vendor.businessAssociateAgreementUrl}) |\n`); - } - - if (vendor.serviceSoftwareAgreementUrl && vendor.serviceSoftwareAgreementUrl !== 'undefined') { - file.write(`| Service Software Agreement | [Link](${vendor.serviceSoftwareAgreementUrl}) |\n`); - } - - if (vendor.subprocessorsListUrl && vendor.subprocessorsListUrl !== 'undefined') { - file.write(`| Subprocessors List | [Link](${vendor.subprocessorsListUrl}) |\n`); - } - - file.write('\n'); - - if (vendor.categories && vendor.categories !== 'undefined') { - file.write(`**Categories:** ${vendor.categories}\n\n`); - } else if (vendor.category && vendor.category !== 'undefined') { - file.write(`**Category:** ${vendor.category}\n\n`); - } - - if (vendor.certifications && vendor.certifications !== 'undefined') { - file.write('### Certifications\n\n'); - file.write(formatAsList(vendor.certifications)); - file.write('\n\n'); - } - - if (vendor.subprocessors && vendor.subprocessors !== 'undefined') { - file.write('### Subprocessors\n\n'); - file.write(formatAsList(vendor.subprocessors)); - file.write('\n\n'); - } - - file.write('---\n\n'); -} - -file.end(); diff --git a/pkg/cmd/asset/create/create.go b/pkg/cmd/asset/create/create.go index 013a302eb..407b86f0e 100644 --- a/pkg/cmd/asset/create/create.go +++ b/pkg/cmd/asset/create/create.go @@ -60,7 +60,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { flagAmount int flagOwner string flagDataTypesStored string - flagVendorIDs []string + flagThirdPartyIDs []string ) cmd := &cobra.Command{ @@ -146,8 +146,8 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { if flagDataTypesStored != "" { input["dataTypesStored"] = flagDataTypesStored } - if len(flagVendorIDs) > 0 { - input["vendorIds"] = flagVendorIDs + if len(flagThirdPartyIDs) > 0 { + input["thirdPartyIds"] = flagThirdPartyIDs } data, err := client.Do( @@ -181,7 +181,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { cmd.Flags().IntVar(&flagAmount, "amount", 0, "Asset amount") cmd.Flags().StringVar(&flagOwner, "owner", "", "Owner profile ID") cmd.Flags().StringVar(&flagDataTypesStored, "data-types-stored", "", "Data types stored") - cmd.Flags().StringSliceVar(&flagVendorIDs, "vendor-ids", nil, "Vendor IDs (comma-separated)") + cmd.Flags().StringSliceVar(&flagThirdPartyIDs, "thirdParty-ids", nil, "ThirdParty IDs (comma-separated)") return cmd } diff --git a/pkg/cmd/asset/update/update.go b/pkg/cmd/asset/update/update.go index 2edc6141d..249961618 100644 --- a/pkg/cmd/asset/update/update.go +++ b/pkg/cmd/asset/update/update.go @@ -54,7 +54,7 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command { flagAmount int flagOwner string flagDataTypesStored string - flagVendorIDs []string + flagThirdPartyIDs []string ) cmd := &cobra.Command{ @@ -103,8 +103,8 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command { if cmd.Flags().Changed("data-types-stored") { input["dataTypesStored"] = flagDataTypesStored } - if cmd.Flags().Changed("vendor-ids") { - input["vendorIds"] = flagVendorIDs + if cmd.Flags().Changed("thirdParty-ids") { + input["thirdPartyIds"] = flagThirdPartyIDs } if len(input) == 1 { @@ -141,7 +141,7 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command { cmd.Flags().IntVar(&flagAmount, "amount", 0, "Asset amount") cmd.Flags().StringVar(&flagOwner, "owner", "", "Owner profile ID") cmd.Flags().StringVar(&flagDataTypesStored, "data-types-stored", "", "Data types stored") - cmd.Flags().StringSliceVar(&flagVendorIDs, "vendor-ids", nil, "Vendor IDs (comma-separated)") + cmd.Flags().StringSliceVar(&flagThirdPartyIDs, "thirdParty-ids", nil, "ThirdParty IDs (comma-separated)") return cmd } diff --git a/pkg/cmd/auditlog/list/list.go b/pkg/cmd/auditlog/list/list.go index fa28fd086..ff594c1a3 100644 --- a/pkg/cmd/auditlog/list/list.go +++ b/pkg/cmd/auditlog/list/list.go @@ -79,8 +79,8 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command { Short: "List audit log entries", Aliases: []string{"ls"}, Example: ` prb audit-log list - prb audit-log list --action core:vendor:create - prb audit-log list --resource-type Vendor --limit 50`, + prb audit-log list --action core:thirdParty:create + prb audit-log list --resource-type ThirdParty --limit 50`, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { if err := cmdutil.ValidateOutputFlag(flagOutput); err != nil { @@ -218,9 +218,9 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command { cmd.Flags().IntVarP(&flagLimit, "limit", "L", 30, "Maximum number of entries to list") cmd.Flags().StringVar(&flagOrderBy, "order-by", "", "Order by field (CREATED_AT)") cmd.Flags().StringVar(&flagOrderDir, "order-direction", "DESC", "Sort direction (ASC, DESC)") - cmd.Flags().StringVar(&flagAction, "action", "", "Filter by action (e.g. core:vendor:create)") + cmd.Flags().StringVar(&flagAction, "action", "", "Filter by action (e.g. core:thirdParty:create)") cmd.Flags().StringVar(&flagActorID, "actor-id", "", "Filter by actor ID") - cmd.Flags().StringVar(&flagResourceType, "resource-type", "", "Filter by resource type (e.g. Vendor)") + cmd.Flags().StringVar(&flagResourceType, "resource-type", "", "Filter by resource type (e.g. ThirdParty)") cmd.Flags().StringVar(&flagResourceID, "resource-id", "", "Filter by resource ID") flagOutput = cmdutil.AddOutputFlag(cmd) diff --git a/pkg/cmd/datum/create/create.go b/pkg/cmd/datum/create/create.go index c5f674354..58375b3bf 100644 --- a/pkg/cmd/datum/create/create.go +++ b/pkg/cmd/datum/create/create.go @@ -56,7 +56,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { flagName string flagClassification string flagOwner string - flagVendorIDs []string + flagThirdPartyIDs []string ) cmd := &cobra.Command{ @@ -138,8 +138,8 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { if flagOwner != "" { input["ownerId"] = flagOwner } - if len(flagVendorIDs) > 0 { - input["vendorIds"] = flagVendorIDs + if len(flagThirdPartyIDs) > 0 { + input["thirdPartyIds"] = flagThirdPartyIDs } data, err := client.Do( @@ -171,7 +171,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { cmd.Flags().StringVar(&flagName, "name", "", "Datum name (required)") cmd.Flags().StringVar(&flagClassification, "data-classification", "", "Data classification: PUBLIC, INTERNAL, CONFIDENTIAL, SECRET (required)") cmd.Flags().StringVar(&flagOwner, "owner", "", "Owner profile ID") - cmd.Flags().StringSliceVar(&flagVendorIDs, "vendor-ids", nil, "Vendor IDs (comma-separated)") + cmd.Flags().StringSliceVar(&flagThirdPartyIDs, "thirdParty-ids", nil, "ThirdParty IDs (comma-separated)") return cmd } diff --git a/pkg/cmd/datum/update/update.go b/pkg/cmd/datum/update/update.go index 5a8ea1d9a..8e46437ca 100644 --- a/pkg/cmd/datum/update/update.go +++ b/pkg/cmd/datum/update/update.go @@ -50,7 +50,7 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command { flagName string flagClassification string flagOwner string - flagVendorIDs []string + flagThirdPartyIDs []string ) cmd := &cobra.Command{ @@ -93,8 +93,8 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command { input["ownerId"] = flagOwner } } - if cmd.Flags().Changed("vendor-ids") { - input["vendorIds"] = flagVendorIDs + if cmd.Flags().Changed("thirdParty-ids") { + input["thirdPartyIds"] = flagThirdPartyIDs } if len(input) == 1 { @@ -129,7 +129,7 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command { cmd.Flags().StringVar(&flagName, "name", "", "Datum name") cmd.Flags().StringVar(&flagClassification, "data-classification", "", "Data classification: PUBLIC, INTERNAL, CONFIDENTIAL, SECRET") cmd.Flags().StringVar(&flagOwner, "owner", "", "Owner profile ID") - cmd.Flags().StringSliceVar(&flagVendorIDs, "vendor-ids", nil, "Vendor IDs (comma-separated)") + cmd.Flags().StringSliceVar(&flagThirdPartyIDs, "thirdParty-ids", nil, "ThirdParty IDs (comma-separated)") return cmd } diff --git a/pkg/cmd/root/root.go b/pkg/cmd/root/root.go index c94243815..ed5fefd6d 100644 --- a/pkg/cmd/root/root.go +++ b/pkg/cmd/root/root.go @@ -46,12 +46,12 @@ import ( "go.probo.inc/probo/pkg/cmd/scim" "go.probo.inc/probo/pkg/cmd/soa" "go.probo.inc/probo/pkg/cmd/task" + "go.probo.inc/probo/pkg/cmd/thirdpartymgmt" "go.probo.inc/probo/pkg/cmd/tia" trackerpattern "go.probo.inc/probo/pkg/cmd/tracker-pattern" trackerresource "go.probo.inc/probo/pkg/cmd/tracker-resource" trustcenter "go.probo.inc/probo/pkg/cmd/trust-center" "go.probo.inc/probo/pkg/cmd/user" - "go.probo.inc/probo/pkg/cmd/vendormgmt" "go.probo.inc/probo/pkg/cmd/version" "go.probo.inc/probo/pkg/cmd/webhook" ) @@ -120,7 +120,7 @@ func NewCmdRoot(f *cmdutil.Factory) *cobra.Command { cmd.AddCommand(tia.NewCmdTIA(f)) cmd.AddCommand(trustcenter.NewCmdTrustCenter(f)) cmd.AddCommand(user.NewCmdUser(f)) - cmd.AddCommand(vendormgmt.NewCmdVendor(f)) + cmd.AddCommand(thirdpartymgmt.NewCmdThirdParty(f)) cmd.AddCommand(version.NewCmdVersion(f)) cmd.AddCommand(webhook.NewCmdWebhook(f)) diff --git a/pkg/cmd/vendormgmt/assess/assess.go b/pkg/cmd/thirdpartymgmt/assess/assess.go similarity index 74% rename from pkg/cmd/vendormgmt/assess/assess.go rename to pkg/cmd/thirdpartymgmt/assess/assess.go index cdbd11f2d..cbbff4d9e 100644 --- a/pkg/cmd/vendormgmt/assess/assess.go +++ b/pkg/cmd/thirdpartymgmt/assess/assess.go @@ -26,15 +26,15 @@ import ( ) const assessMutation = ` -mutation($input: AssessVendorInput!) { - assessVendor(input: $input) { +mutation($input: AssessThirdPartyInput!) { + assessThirdParty(input: $input) { report subprocessors { name country purpose } - vendor { + third_party { id name } @@ -43,18 +43,18 @@ mutation($input: AssessVendorInput!) { ` type assessResponse struct { - AssessVendor struct { + AssessThirdParty struct { Report string `json:"report"` Subprocessors []struct { Name string `json:"name"` Country string `json:"country"` Purpose string `json:"purpose"` } `json:"subprocessors"` - Vendor struct { + ThirdParty struct { ID string `json:"id"` Name string `json:"name"` - } `json:"vendor"` - } `json:"assessVendor"` + } `json:"third_party"` + } `json:"assessThirdParty"` } func NewCmdAssess(f *cmdutil.Factory) *cobra.Command { @@ -63,17 +63,17 @@ func NewCmdAssess(f *cmdutil.Factory) *cobra.Command { ) cmd := &cobra.Command{ - Use: "assess --url ", - Short: "Run AI assessment on a vendor from its website", - Long: "Analyze a vendor's website using AI agents to extract security, compliance, and business information.", - Example: ` # Assess a vendor by website URL - prb vendor assess VND_123 --url https://example.com + Use: "assess --url ", + Short: "Run AI assessment on a thirdParty from its website", + Long: "Analyze a thirdParty's website using AI agents to extract security, compliance, and business information.", + Example: ` # Assess a third_party by website URL + prb third_party assess VND_123 --url https://example.com # Assess with a custom procedure file - prb vendor assess VND_123 --url https://example.com --procedure-file ./my-procedure.txt + prb third_party assess VND_123 --url https://example.com --procedure-file ./my-procedure.txt # Output as JSON - prb vendor assess VND_123 --url https://example.com -o json`, + prb third_party assess VND_123 --url https://example.com -o json`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { if err := cmdutil.ValidateOutputFlag(flagOutput); err != nil { @@ -115,7 +115,7 @@ func NewCmdAssess(f *cmdutil.Factory) *cobra.Command { 22*time.Minute, ) - _, _ = fmt.Fprintf(f.IOStreams.ErrOut, "Assessing vendor from %s (this may take a few minutes)...\n", flagURL) + _, _ = fmt.Fprintf(f.IOStreams.ErrOut, "Assessing thirdParty from %s (this may take a few minutes)...\n", flagURL) data, err := client.Do( assessMutation, @@ -133,16 +133,16 @@ func NewCmdAssess(f *cmdutil.Factory) *cobra.Command { } if *flagOutput == cmdutil.OutputJSON { - return cmdutil.PrintJSON(f.IOStreams.Out, resp.AssessVendor) + return cmdutil.PrintJSON(f.IOStreams.Out, resp.AssessThirdParty) } - _, _ = fmt.Fprintln(f.IOStreams.Out, resp.AssessVendor.Report) + _, _ = fmt.Fprintln(f.IOStreams.Out, resp.AssessThirdParty.Report) return nil }, } - cmd.Flags().String("url", "", "Vendor website URL to assess (required)") + cmd.Flags().String("url", "", "ThirdParty website URL to assess (required)") _ = cmd.MarkFlagRequired("url") cmd.Flags().String("procedure-file", "", "Path to a custom assessment procedure file") flagOutput = cmdutil.AddOutputFlag(cmd) diff --git a/pkg/cmd/vendormgmt/create/create.go b/pkg/cmd/thirdpartymgmt/create/create.go similarity index 85% rename from pkg/cmd/vendormgmt/create/create.go rename to pkg/cmd/thirdpartymgmt/create/create.go index 0e702e1e2..39b4bc311 100644 --- a/pkg/cmd/vendormgmt/create/create.go +++ b/pkg/cmd/thirdpartymgmt/create/create.go @@ -25,9 +25,9 @@ import ( ) const createMutation = ` -mutation($input: CreateVendorInput!) { - createVendor(input: $input) { - vendorEdge { +mutation($input: CreateThirdPartyInput!) { + createThirdParty(input: $input) { + thirdPartyEdge { node { id name @@ -39,15 +39,15 @@ mutation($input: CreateVendorInput!) { ` type createResponse struct { - CreateVendor struct { - VendorEdge struct { + CreateThirdParty struct { + ThirdPartyEdge struct { Node struct { ID string `json:"id"` Name string `json:"name"` Category string `json:"category"` } `json:"node"` - } `json:"vendorEdge"` - } `json:"createVendor"` + } `json:"thirdPartyEdge"` + } `json:"createThirdParty"` } func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { @@ -63,12 +63,12 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "create", - Short: "Create a new vendor", - Example: ` # Create a vendor interactively - prb vendor create + Short: "Create a new thirdParty", + Example: ` # Create a third_party interactively + prb third_party create - # Create a vendor non-interactively - prb vendor create --name "Acme Corp" --category CLOUD_PROVIDER`, + # Create a third_party non-interactively + prb third_party create --name "Acme Corp" --category CLOUD_PROVIDER`, RunE: func(cmd *cobra.Command, args []string) error { cfg, err := f.Config() if err != nil { @@ -99,7 +99,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { if f.IOStreams.IsInteractive() { if flagName == "" { err := huh.NewInput(). - Title("Vendor name"). + Title("ThirdParty name"). Value(&flagName). Run() if err != nil { @@ -109,7 +109,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { if flagCategory == "" { err := huh.NewSelect[string](). - Title("Vendor category"). + Title("ThirdParty category"). Options( huh.NewOption("Analytics", "ANALYTICS"), huh.NewOption("Cloud Monitoring", "CLOUD_MONITORING"), @@ -181,10 +181,10 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { return fmt.Errorf("cannot parse response: %w", err) } - v := resp.CreateVendor.VendorEdge.Node + v := resp.CreateThirdParty.ThirdPartyEdge.Node _, _ = fmt.Fprintf( f.IOStreams.Out, - "Created vendor %s (%s)\n", + "Created thirdParty %s (%s)\n", v.ID, v.Name, ) @@ -194,9 +194,9 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { } cmd.Flags().StringVar(&flagOrg, "org", "", "Organization ID") - cmd.Flags().StringVar(&flagName, "name", "", "Vendor name (required)") - cmd.Flags().StringVar(&flagCategory, "category", "", "Vendor category (required)") - cmd.Flags().StringVar(&flagDescription, "description", "", "Vendor description") + cmd.Flags().StringVar(&flagName, "name", "", "ThirdParty name (required)") + cmd.Flags().StringVar(&flagCategory, "category", "", "ThirdParty category (required)") + cmd.Flags().StringVar(&flagDescription, "description", "", "ThirdParty description") cmd.Flags().StringVar(&flagLegalName, "legal-name", "", "Legal name") cmd.Flags().StringVar(&flagAddress, "address", "", "Headquarter address") cmd.Flags().StringVar(&flagWebsite, "website", "", "Website URL") diff --git a/pkg/cmd/vendormgmt/delete/delete.go b/pkg/cmd/thirdpartymgmt/delete/delete.go similarity index 85% rename from pkg/cmd/vendormgmt/delete/delete.go rename to pkg/cmd/thirdpartymgmt/delete/delete.go index 1137039ce..3c37147dd 100644 --- a/pkg/cmd/vendormgmt/delete/delete.go +++ b/pkg/cmd/thirdpartymgmt/delete/delete.go @@ -24,9 +24,9 @@ import ( ) const deleteMutation = ` -mutation($input: DeleteVendorInput!) { - deleteVendor(input: $input) { - deletedVendorId +mutation($input: DeleteThirdPartyInput!) { + deleteThirdParty(input: $input) { + deletedThirdPartyId } } ` @@ -36,17 +36,17 @@ func NewCmdDelete(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "delete ", - Short: "Delete a vendor", + Short: "Delete a thirdParty", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { if !flagYes { if !f.IOStreams.IsInteractive() { - return fmt.Errorf("cannot delete vendor: confirmation required, use --yes to confirm") + return fmt.Errorf("cannot delete thirdParty: confirmation required, use --yes to confirm") } var confirmed bool err := huh.NewConfirm(). - Title(fmt.Sprintf("Delete vendor %s?", args[0])). + Title(fmt.Sprintf("Delete thirdParty %s?", args[0])). Value(&confirmed). Run() if err != nil { @@ -79,7 +79,7 @@ func NewCmdDelete(f *cmdutil.Factory) *cobra.Command { deleteMutation, map[string]any{ "input": map[string]any{ - "vendorId": args[0], + "thirdPartyId": args[0], }, }, ) @@ -89,7 +89,7 @@ func NewCmdDelete(f *cmdutil.Factory) *cobra.Command { _, _ = fmt.Fprintf( f.IOStreams.Out, - "Deleted vendor %s\n", + "Deleted thirdParty %s\n", args[0], ) diff --git a/pkg/cmd/vendormgmt/list/list.go b/pkg/cmd/thirdpartymgmt/list/list.go similarity index 78% rename from pkg/cmd/vendormgmt/list/list.go rename to pkg/cmd/thirdpartymgmt/list/list.go index 10ab27cac..b6347ef3c 100644 --- a/pkg/cmd/vendormgmt/list/list.go +++ b/pkg/cmd/thirdpartymgmt/list/list.go @@ -24,11 +24,11 @@ import ( ) const listQuery = ` -query($id: ID!, $first: Int, $after: CursorKey, $orderBy: VendorOrder) { +query($id: ID!, $first: Int, $after: CursorKey, $orderBy: ThirdPartyOrder) { node(id: $id) { __typename ... on Organization { - vendors(first: $first, after: $after, orderBy: $orderBy) { + third_parties(first: $first, after: $after, orderBy: $orderBy) { totalCount edges { node { @@ -47,7 +47,7 @@ query($id: ID!, $first: Int, $after: CursorKey, $orderBy: VendorOrder) { } ` -type vendor struct { +type thirdParty struct { ID string `json:"id"` Name string `json:"name"` Category string `json:"category"` @@ -64,13 +64,13 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "list", - Short: "List vendors in an organization", + Short: "List thirdParties in an organization", Aliases: []string{"ls"}, - Example: ` # List vendors in the default organization - prb vendor list + Example: ` # List third_parties in the default organization + prb third_party list - # List vendors sorted by name - prb vendor ls --order-by NAME --json`, + # List third_parties sorted by name + prb third_party ls --order-by NAME --json`, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { if err := cmdutil.ValidateOutputFlag(flagOutput); err != nil { @@ -117,16 +117,16 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command { } } - vendors, totalCount, err := api.Paginate( + thirdParties, totalCount, err := api.Paginate( client, listQuery, variables, flagLimit, - func(data json.RawMessage) (*api.Connection[vendor], error) { + func(data json.RawMessage) (*api.Connection[thirdParty], error) { var resp struct { Node *struct { - Typename string `json:"__typename"` - Vendors api.Connection[vendor] `json:"vendors"` + Typename string `json:"__typename"` + ThirdParties api.Connection[thirdParty] `json:"third_parties"` } `json:"node"` } if err := json.Unmarshal(data, &resp); err != nil { @@ -138,7 +138,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command { if resp.Node.Typename != "Organization" { return nil, fmt.Errorf("expected Organization node, got %s", resp.Node.Typename) } - return &resp.Node.Vendors, nil + return &resp.Node.ThirdParties, nil }, ) if err != nil { @@ -146,16 +146,16 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command { } if *flagOutput == cmdutil.OutputJSON { - return cmdutil.PrintJSON(f.IOStreams.Out, vendors) + return cmdutil.PrintJSON(f.IOStreams.Out, thirdParties) } - if len(vendors) == 0 { - _, _ = fmt.Fprintln(f.IOStreams.Out, "No vendors found.") + if len(thirdParties) == 0 { + _, _ = fmt.Fprintln(f.IOStreams.Out, "No thirdParties found.") return nil } - rows := make([][]string, 0, len(vendors)) - for _, v := range vendors { + rows := make([][]string, 0, len(thirdParties)) + for _, v := range thirdParties { rows = append(rows, []string{ v.ID, v.Name, @@ -167,11 +167,11 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command { _, _ = fmt.Fprintln(f.IOStreams.Out, t) - if totalCount > len(vendors) { + if totalCount > len(thirdParties) { _, _ = fmt.Fprintf( f.IOStreams.ErrOut, - "\nShowing %d of %d vendors\n", - len(vendors), + "\nShowing %d of %d thirdParties\n", + len(thirdParties), totalCount, ) } @@ -181,7 +181,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command { } cmd.Flags().StringVar(&flagOrg, "org", "", "Organization ID") - cmd.Flags().IntVarP(&flagLimit, "limit", "L", 30, "Maximum number of vendors to list") + cmd.Flags().IntVarP(&flagLimit, "limit", "L", 30, "Maximum number of thirdParties to list") cmd.Flags().StringVar(&flagOrderBy, "order-by", "", "Order by field (NAME, CREATED_AT, UPDATED_AT)") cmd.Flags().StringVar(&flagOrderDir, "order-direction", "DESC", "Sort direction (ASC, DESC)") flagOutput = cmdutil.AddOutputFlag(cmd) diff --git a/pkg/cmd/vendormgmt/publish/publish.go b/pkg/cmd/thirdpartymgmt/publish/publish.go similarity index 87% rename from pkg/cmd/vendormgmt/publish/publish.go rename to pkg/cmd/thirdpartymgmt/publish/publish.go index 7885206a2..43d249634 100644 --- a/pkg/cmd/vendormgmt/publish/publish.go +++ b/pkg/cmd/thirdpartymgmt/publish/publish.go @@ -24,8 +24,8 @@ import ( ) const publishMutation = ` -mutation($input: PublishVendorListInput!) { - publishVendorList(input: $input) { +mutation($input: PublishThirdPartyListInput!) { + publishThirdPartyList(input: $input) { documentEdge { node { id @@ -47,7 +47,7 @@ mutation($input: PublishVendorListInput!) { ` type publishResponse struct { - PublishVendorList struct { + PublishThirdPartyList struct { DocumentEdge struct { Node struct { ID string `json:"id"` @@ -64,7 +64,7 @@ type publishResponse struct { Status string `json:"status"` } `json:"node"` } `json:"documentVersionEdge"` - } `json:"publishVendorList"` + } `json:"publishThirdPartyList"` } func NewCmdPublish(f *cmdutil.Factory) *cobra.Command { @@ -76,12 +76,12 @@ func NewCmdPublish(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "publish", - Short: "Publish the vendor register as a document version", - Example: ` # Publish the vendor register - prb vendor publish --org ORG_ID + Short: "Publish the thirdParty register as a document version", + Example: ` # Publish the third_party register + prb third_party publish --org ORG_ID # Publish with approvers - prb vendor publish --org ORG_ID --approver PROFILE_ID1 --approver PROFILE_ID2`, + prb third_party publish --org ORG_ID --approver PROFILE_ID1 --approver PROFILE_ID2`, RunE: func(cmd *cobra.Command, args []string) error { cfg, err := f.Config() if err != nil { @@ -130,10 +130,10 @@ func NewCmdPublish(f *cmdutil.Factory) *cobra.Command { return fmt.Errorf("cannot parse response: %w", err) } - v := resp.PublishVendorList.DocumentVersionEdge.Node + v := resp.PublishThirdPartyList.DocumentVersionEdge.Node _, _ = fmt.Fprintf( f.IOStreams.Out, - "Published vendor register %s (v%d.%d)\n", + "Published thirdParty register %s (v%d.%d)\n", v.Title, v.Major, v.Minor, diff --git a/pkg/cmd/vendormgmt/vendormgmt.go b/pkg/cmd/thirdpartymgmt/thirdpartymgmt.go similarity index 69% rename from pkg/cmd/vendormgmt/vendormgmt.go rename to pkg/cmd/thirdpartymgmt/thirdpartymgmt.go index da139ba4a..3a76eb06f 100644 --- a/pkg/cmd/vendormgmt/vendormgmt.go +++ b/pkg/cmd/thirdpartymgmt/thirdpartymgmt.go @@ -12,24 +12,24 @@ // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -package vendormgmt +package thirdpartymgmt import ( "github.com/spf13/cobra" "go.probo.inc/probo/pkg/cmd/cmdutil" - "go.probo.inc/probo/pkg/cmd/vendormgmt/assess" - "go.probo.inc/probo/pkg/cmd/vendormgmt/create" - "go.probo.inc/probo/pkg/cmd/vendormgmt/delete" - "go.probo.inc/probo/pkg/cmd/vendormgmt/list" - "go.probo.inc/probo/pkg/cmd/vendormgmt/publish" - "go.probo.inc/probo/pkg/cmd/vendormgmt/update" - "go.probo.inc/probo/pkg/cmd/vendormgmt/view" + "go.probo.inc/probo/pkg/cmd/thirdpartymgmt/assess" + "go.probo.inc/probo/pkg/cmd/thirdpartymgmt/create" + "go.probo.inc/probo/pkg/cmd/thirdpartymgmt/delete" + "go.probo.inc/probo/pkg/cmd/thirdpartymgmt/list" + "go.probo.inc/probo/pkg/cmd/thirdpartymgmt/publish" + "go.probo.inc/probo/pkg/cmd/thirdpartymgmt/update" + "go.probo.inc/probo/pkg/cmd/thirdpartymgmt/view" ) -func NewCmdVendor(f *cmdutil.Factory) *cobra.Command { +func NewCmdThirdParty(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ - Use: "vendor ", - Short: "Manage vendors", + Use: "thirdParty ", + Short: "Manage thirdParties", } cmd.AddCommand(list.NewCmdList(f)) diff --git a/pkg/cmd/vendormgmt/update/update.go b/pkg/cmd/thirdpartymgmt/update/update.go similarity index 85% rename from pkg/cmd/vendormgmt/update/update.go rename to pkg/cmd/thirdpartymgmt/update/update.go index 9be5da251..e08459d4b 100644 --- a/pkg/cmd/vendormgmt/update/update.go +++ b/pkg/cmd/thirdpartymgmt/update/update.go @@ -24,9 +24,9 @@ import ( ) const updateMutation = ` -mutation($input: UpdateVendorInput!) { - updateVendor(input: $input) { - vendor { +mutation($input: UpdateThirdPartyInput!) { + updateThirdParty(input: $input) { + third_party { id name category @@ -36,13 +36,13 @@ mutation($input: UpdateVendorInput!) { ` type updateResponse struct { - UpdateVendor struct { - Vendor struct { + UpdateThirdParty struct { + ThirdParty struct { ID string `json:"id"` Name string `json:"name"` Category string `json:"category"` - } `json:"vendor"` - } `json:"updateVendor"` + } `json:"third_party"` + } `json:"updateThirdParty"` } func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command { @@ -57,7 +57,7 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "update ", - Short: "Update a vendor", + Short: "Update a thirdParty", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { cfg, err := f.Config() @@ -118,10 +118,10 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command { return fmt.Errorf("cannot parse response: %w", err) } - v := resp.UpdateVendor.Vendor + v := resp.UpdateThirdParty.ThirdParty _, _ = fmt.Fprintf( f.IOStreams.Out, - "Updated vendor %s (%s)\n", + "Updated thirdParty %s (%s)\n", v.ID, v.Name, ) @@ -130,9 +130,9 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command { }, } - cmd.Flags().StringVar(&flagName, "name", "", "Vendor name") - cmd.Flags().StringVar(&flagDescription, "description", "", "Vendor description") - cmd.Flags().StringVar(&flagCategory, "category", "", "Vendor category") + cmd.Flags().StringVar(&flagName, "name", "", "ThirdParty name") + cmd.Flags().StringVar(&flagDescription, "description", "", "ThirdParty description") + cmd.Flags().StringVar(&flagCategory, "category", "", "ThirdParty category") cmd.Flags().StringVar(&flagLegalName, "legal-name", "", "Legal name") cmd.Flags().StringVar(&flagAddress, "address", "", "Headquarter address") cmd.Flags().StringVar(&flagWebsite, "website", "", "Website URL") diff --git a/pkg/cmd/vendormgmt/view/view.go b/pkg/cmd/thirdpartymgmt/view/view.go similarity index 94% rename from pkg/cmd/vendormgmt/view/view.go rename to pkg/cmd/thirdpartymgmt/view/view.go index c2371e41c..ca24d931b 100644 --- a/pkg/cmd/vendormgmt/view/view.go +++ b/pkg/cmd/thirdpartymgmt/view/view.go @@ -28,7 +28,7 @@ const viewQuery = ` query($id: ID!) { node(id: $id) { __typename - ... on Vendor { + ... on ThirdParty { id name description @@ -63,7 +63,7 @@ func NewCmdView(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "view ", - Short: "View a vendor", + Short: "View a thirdParty", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { if err := cmdutil.ValidateOutputFlag(flagOutput); err != nil { @@ -102,11 +102,11 @@ func NewCmdView(f *cmdutil.Factory) *cobra.Command { } if resp.Node == nil { - return fmt.Errorf("vendor %s not found", args[0]) + return fmt.Errorf("thirdParty %s not found", args[0]) } - if resp.Node.Typename != "Vendor" { - return fmt.Errorf("expected Vendor node, got %s", resp.Node.Typename) + if resp.Node.Typename != "ThirdParty" { + return fmt.Errorf("expected ThirdParty node, got %s", resp.Node.Typename) } if *flagOutput == cmdutil.OutputJSON { diff --git a/pkg/cmd/webhook/create/create.go b/pkg/cmd/webhook/create/create.go index e10fa1568..b669d07db 100644 --- a/pkg/cmd/webhook/create/create.go +++ b/pkg/cmd/webhook/create/create.go @@ -62,11 +62,11 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "create", Short: "Create a webhook subscription", - Example: ` # Create a webhook for vendor events - prb webhook create --url https://example.com/webhook --event VENDOR_CREATED --event VENDOR_UPDATED + Example: ` # Create a webhook for thirdParty events + prb webhook create --url https://example.com/webhook --event THIRD_PARTY_CREATED --event THIRD_PARTY_UPDATED # Create a webhook for all supported events - prb webhook create --url https://example.com/webhook --event VENDOR_CREATED --event VENDOR_UPDATED --event VENDOR_DELETED --event USER_CREATED --event USER_UPDATED --event USER_DELETED --event OBLIGATION_CREATED --event OBLIGATION_UPDATED --event OBLIGATION_DELETED`, + prb webhook create --url https://example.com/webhook --event THIRD_PARTY_CREATED --event THIRD_PARTY_UPDATED --event THIRD_PARTY_DELETED --event USER_CREATED --event USER_UPDATED --event USER_DELETED --event OBLIGATION_CREATED --event OBLIGATION_UPDATED --event OBLIGATION_DELETED`, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { for _, e := range flagEvents { diff --git a/pkg/cmd/webhook/shared/events.go b/pkg/cmd/webhook/shared/events.go index 4a9dbba1a..a4dfe9cd1 100644 --- a/pkg/cmd/webhook/shared/events.go +++ b/pkg/cmd/webhook/shared/events.go @@ -21,9 +21,9 @@ var ValidEvents = []string{ "MEETING_CREATED", "MEETING_UPDATED", "MEETING_DELETED", - "VENDOR_CREATED", - "VENDOR_UPDATED", - "VENDOR_DELETED", + "THIRD_PARTY_CREATED", + "THIRD_PARTY_UPDATED", + "THIRD_PARTY_DELETED", "USER_CREATED", "USER_UPDATED", "USER_DELETED", diff --git a/pkg/coredata/asset_vendor.go b/pkg/coredata/asset_third_party.go similarity index 64% rename from pkg/coredata/asset_vendor.go rename to pkg/coredata/asset_third_party.go index 7dfb6289b..902a0b719 100644 --- a/pkg/coredata/asset_vendor.go +++ b/pkg/coredata/asset_third_party.go @@ -25,41 +25,41 @@ import ( ) type ( - AssetVendor struct { - AssetID gid.GID `db:"asset_id"` - VendorID gid.GID `db:"vendor_id"` - TenantID gid.TenantID `db:"tenant_id"` - CreatedAt time.Time `db:"created_at"` + AssetThirdParty struct { + AssetID gid.GID `db:"asset_id"` + ThirdPartyID gid.GID `db:"third_party_id"` + TenantID gid.TenantID `db:"tenant_id"` + CreatedAt time.Time `db:"created_at"` } - AssetVendors []*AssetVendor + AssetThirdParties []*AssetThirdParty ) -func (av AssetVendors) Merge( +func (av AssetThirdParties) Merge( ctx context.Context, conn pg.Querier, scope Scoper, assetID gid.GID, organizationID gid.GID, - vendorIDs []gid.GID, + thirdPartyIDs []gid.GID, ) error { q := ` -WITH vendor_ids AS ( +WITH third_party_ids AS ( SELECT - unnest(@vendor_ids::text[]) AS vendor_id, + unnest(@third_party_ids::text[]) AS third_party_id, @tenant_id AS tenant_id, @asset_id AS asset_id, @organization_id AS organization_id, @created_at::timestamptz AS created_at ) -MERGE INTO asset_vendors AS tgt -USING vendor_ids AS src +MERGE INTO asset_third_parties AS tgt +USING third_party_ids AS src ON tgt.tenant_id = src.tenant_id AND tgt.asset_id = src.asset_id - AND tgt.vendor_id = src.vendor_id + AND tgt.third_party_id = src.third_party_id WHEN NOT MATCHED - THEN INSERT (tenant_id, asset_id, vendor_id, organization_id, created_at) - VALUES (src.tenant_id, src.asset_id, src.vendor_id, src.organization_id, src.created_at) + THEN INSERT (tenant_id, asset_id, third_party_id, organization_id, created_at) + VALUES (src.tenant_id, src.asset_id, src.third_party_id, src.organization_id, src.created_at) WHEN NOT MATCHED BY SOURCE AND tgt.tenant_id = @tenant_id AND tgt.asset_id = @asset_id THEN DELETE @@ -70,37 +70,37 @@ WHEN NOT MATCHED "asset_id": assetID, "organization_id": organizationID, "created_at": time.Now(), - "vendor_ids": vendorIDs, + "third_party_ids": thirdPartyIDs, } _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot merge asset vendors: %w", err) + return fmt.Errorf("cannot merge asset thirdParties: %w", err) } return nil } -func (av AssetVendors) Insert( +func (av AssetThirdParties) Insert( ctx context.Context, conn pg.Tx, scope Scoper, assetID gid.GID, organizationID gid.GID, - vendorIDs []gid.GID, + thirdPartyIDs []gid.GID, ) error { q := ` -WITH vendor_ids AS ( - SELECT unnest(@vendor_ids::text[]) AS vendor_id +WITH third_party_ids AS ( + SELECT unnest(@third_party_ids::text[]) AS third_party_id ) -INSERT INTO asset_vendors (tenant_id, asset_id, vendor_id, organization_id, created_at) +INSERT INTO asset_third_parties (tenant_id, asset_id, third_party_id, organization_id, created_at) SELECT @tenant_id AS tenant_id, @asset_id AS asset_id, - vendor_id, + third_party_id, @organization_id AS organization_id, @created_at AS created_at -FROM vendor_ids +FROM third_party_ids ` args := pgx.StrictNamedArgs{ @@ -108,12 +108,12 @@ FROM vendor_ids "asset_id": assetID, "organization_id": organizationID, "created_at": time.Now(), - "vendor_ids": vendorIDs, + "third_party_ids": thirdPartyIDs, } _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot insert asset vendors: %w", err) + return fmt.Errorf("cannot insert asset thirdParties: %w", err) } return nil diff --git a/pkg/coredata/audit_log_resource_type.go b/pkg/coredata/audit_log_resource_type.go index ace0579c9..02d7888ef 100644 --- a/pkg/coredata/audit_log_resource_type.go +++ b/pkg/coredata/audit_log_resource_type.go @@ -29,12 +29,12 @@ func ResourceTypeName(entityType uint16) string { return "Evidence" case ConnectorEntityType: return "Connector" - case VendorRiskAssessmentEntityType: - return "VendorRiskAssessment" - case VendorEntityType: - return "Vendor" - case VendorComplianceReportEntityType: - return "VendorComplianceReport" + case ThirdPartyRiskAssessmentEntityType: + return "ThirdPartyRiskAssessment" + case ThirdPartyEntityType: + return "ThirdParty" + case ThirdPartyComplianceReportEntityType: + return "ThirdPartyComplianceReport" case DocumentEntityType: return "Document" case IdentityEntityType: @@ -59,20 +59,20 @@ func ResourceTypeName(entityType uint16) string { return "TrustCenter" case TrustCenterAccessEntityType: return "TrustCenterAccess" - case VendorBusinessAssociateAgreementEntityType: - return "VendorBusinessAssociateAgreement" + case ThirdPartyBusinessAssociateAgreementEntityType: + return "ThirdPartyBusinessAssociateAgreement" case FileEntityType: return "File" - case VendorContactEntityType: - return "VendorContact" - case VendorDataPrivacyAgreementEntityType: - return "VendorDataPrivacyAgreement" + case ThirdPartyContactEntityType: + return "ThirdPartyContact" + case ThirdPartyDataPrivacyAgreementEntityType: + return "ThirdPartyDataPrivacyAgreement" case FindingEntityType: return "Finding" case ObligationEntityType: return "Obligation" - case VendorServiceEntityType: - return "VendorService" + case ThirdPartyServiceEntityType: + return "ThirdPartyService" case ProcessingActivityEntityType: return "ProcessingActivity" case TrustCenterReferenceEntityType: diff --git a/pkg/coredata/common_third_party.go b/pkg/coredata/common_third_party.go index cf7f5c043..e3d429c58 100644 --- a/pkg/coredata/common_third_party.go +++ b/pkg/coredata/common_third_party.go @@ -28,26 +28,26 @@ import ( type ( CommonThirdParty struct { - ID gid.GID `db:"id"` - Name string `db:"name"` - Category VendorCategory `db:"category"` - HeadquarterAddress *string `db:"headquarter_address"` - LegalName *string `db:"legal_name"` - WebsiteURL *string `db:"website_url"` - PrivacyPolicyURL *string `db:"privacy_policy_url"` - ServiceLevelAgreementURL *string `db:"service_level_agreement_url"` - ServiceSoftwareAgreementURL *string `db:"service_software_agreement_url"` - DataProcessingAgreementURL *string `db:"data_processing_agreement_url"` - BusinessAssociateAgreementURL *string `db:"business_associate_agreement_url"` - SubprocessorsListURL *string `db:"subprocessors_list_url"` - Certifications []string `db:"certifications"` - StatusPageURL *string `db:"status_page_url"` - TermsOfServiceURL *string `db:"terms_of_service_url"` - SecurityPageURL *string `db:"security_page_url"` - TrustPageURL *string `db:"trust_page_url"` - LogoFileID *gid.GID `db:"logo_file_id"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` + ID gid.GID `db:"id"` + Name string `db:"name"` + Category ThirdPartyCategory `db:"category"` + HeadquarterAddress *string `db:"headquarter_address"` + LegalName *string `db:"legal_name"` + WebsiteURL *string `db:"website_url"` + PrivacyPolicyURL *string `db:"privacy_policy_url"` + ServiceLevelAgreementURL *string `db:"service_level_agreement_url"` + ServiceSoftwareAgreementURL *string `db:"service_software_agreement_url"` + DataProcessingAgreementURL *string `db:"data_processing_agreement_url"` + BusinessAssociateAgreementURL *string `db:"business_associate_agreement_url"` + SubprocessorsListURL *string `db:"subprocessors_list_url"` + Certifications []string `db:"certifications"` + StatusPageURL *string `db:"status_page_url"` + TermsOfServiceURL *string `db:"terms_of_service_url"` + SecurityPageURL *string `db:"security_page_url"` + TrustPageURL *string `db:"trust_page_url"` + LogoFileID *gid.GID `db:"logo_file_id"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` } CommonThirdParties []*CommonThirdParty diff --git a/pkg/coredata/datum_vendor.go b/pkg/coredata/datum_third_party.go similarity index 66% rename from pkg/coredata/datum_vendor.go rename to pkg/coredata/datum_third_party.go index 747cf4ed1..6707e8bd5 100644 --- a/pkg/coredata/datum_vendor.go +++ b/pkg/coredata/datum_third_party.go @@ -25,40 +25,40 @@ import ( ) type ( - DatumVendor struct { - DatumID gid.GID `db:"datum_id"` - VendorID gid.GID `db:"vendor_id"` - CreatedAt time.Time `db:"created_at"` + DatumThirdParty struct { + DatumID gid.GID `db:"datum_id"` + ThirdPartyID gid.GID `db:"third_party_id"` + CreatedAt time.Time `db:"created_at"` } - DatumVendors []*DatumVendor + DatumThirdParties []*DatumThirdParty ) -func (dv DatumVendors) Merge( +func (dv DatumThirdParties) Merge( ctx context.Context, conn pg.Querier, scope Scoper, datumID gid.GID, organizationID gid.GID, - vendorIDs []gid.GID, + thirdPartyIDs []gid.GID, ) error { q := ` -WITH vendor_ids AS ( +WITH third_party_ids AS ( SELECT - unnest(@vendor_ids::text[]) AS vendor_id, + unnest(@third_party_ids::text[]) AS third_party_id, @tenant_id AS tenant_id, @datum_id AS datum_id, @organization_id AS organization_id, @created_at::timestamptz AS created_at ) -MERGE INTO data_vendors AS tgt -USING vendor_ids AS src +MERGE INTO data_third_parties AS tgt +USING third_party_ids AS src ON tgt.tenant_id = src.tenant_id AND tgt.datum_id = src.datum_id - AND tgt.vendor_id = src.vendor_id + AND tgt.third_party_id = src.third_party_id WHEN NOT MATCHED THEN - INSERT (tenant_id, datum_id, vendor_id, organization_id, created_at) - VALUES (src.tenant_id, src.datum_id, src.vendor_id, src.organization_id, src.created_at) + INSERT (tenant_id, datum_id, third_party_id, organization_id, created_at) + VALUES (src.tenant_id, src.datum_id, src.third_party_id, src.organization_id, src.created_at) WHEN NOT MATCHED BY SOURCE AND tgt.tenant_id = @tenant_id AND tgt.datum_id = @datum_id THEN DELETE @@ -69,37 +69,37 @@ WHEN NOT MATCHED BY SOURCE "datum_id": datumID, "organization_id": organizationID, "created_at": time.Now(), - "vendor_ids": vendorIDs, + "third_party_ids": thirdPartyIDs, } _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot merge data vendors: %w", err) + return fmt.Errorf("cannot merge data thirdParties: %w", err) } return nil } -func (dv DatumVendors) Insert( +func (dv DatumThirdParties) Insert( ctx context.Context, conn pg.Tx, scope Scoper, datumID gid.GID, organizationID gid.GID, - vendorIDs []gid.GID, + thirdPartyIDs []gid.GID, ) error { q := ` -WITH vendor_ids AS ( - SELECT unnest(@vendor_ids::text[]) AS vendor_id +WITH third_party_ids AS ( + SELECT unnest(@third_party_ids::text[]) AS third_party_id ) -INSERT INTO data_vendors (tenant_id, datum_id, vendor_id, organization_id, created_at) +INSERT INTO data_third_parties (tenant_id, datum_id, third_party_id, organization_id, created_at) SELECT @tenant_id::text AS tenant_id, @datum_id::text AS datum_id, - vendor_id, + third_party_id, @organization_id::text AS organization_id, @created_at::timestamptz AS created_at -FROM vendor_ids +FROM third_party_ids ` args := pgx.StrictNamedArgs{ @@ -107,12 +107,12 @@ FROM vendor_ids "datum_id": datumID, "organization_id": organizationID, "created_at": time.Now(), - "vendor_ids": vendorIDs, + "third_party_ids": thirdPartyIDs, } _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot insert data vendors: %w", err) + return fmt.Errorf("cannot insert data thirdParties: %w", err) } return nil diff --git a/pkg/coredata/entity_type_reg.go b/pkg/coredata/entity_type_reg.go index caafad862..3ea38c146 100644 --- a/pkg/coredata/entity_type_reg.go +++ b/pkg/coredata/entity_type_reg.go @@ -23,99 +23,99 @@ var ( ) const ( - OrganizationEntityType uint16 = 0 - FrameworkEntityType uint16 = 1 - MeasureEntityType uint16 = 2 - TaskEntityType uint16 = 3 - EvidenceEntityType uint16 = 4 - ConnectorEntityType uint16 = 5 - VendorRiskAssessmentEntityType uint16 = 6 - VendorEntityType uint16 = 7 - _ uint16 = 8 // PeopleEntityType - removed - VendorComplianceReportEntityType uint16 = 9 - DocumentEntityType uint16 = 10 - IdentityEntityType uint16 = 11 - SessionEntityType uint16 = 12 - EmailEntityType uint16 = 13 - ControlEntityType uint16 = 14 - RiskEntityType uint16 = 15 - DocumentVersionEntityType uint16 = 16 - DocumentVersionSignatureEntityType uint16 = 17 - AssetEntityType uint16 = 18 - DatumEntityType uint16 = 19 - AuditEntityType uint16 = 20 - ReportEntityType uint16 = 21 - TrustCenterEntityType uint16 = 22 - TrustCenterAccessEntityType uint16 = 23 - VendorBusinessAssociateAgreementEntityType uint16 = 24 - FileEntityType uint16 = 25 - VendorContactEntityType uint16 = 26 - VendorDataPrivacyAgreementEntityType uint16 = 27 - _ uint16 = 28 // NonconformityEntityType - removed - ObligationEntityType uint16 = 29 - VendorServiceEntityType uint16 = 30 - _ uint16 = 31 // SnapshotEntityType - removed - _ uint16 = 32 // ContinualImprovementEntityType - removed - ProcessingActivityEntityType uint16 = 33 - ExportJobEntityType uint16 = 34 - TrustCenterReferenceEntityType uint16 = 35 - TrustCenterDocumentAccessEntityType uint16 = 36 - CustomDomainEntityType uint16 = 37 - InvitationEntityType uint16 = 38 - MembershipEntityType uint16 = 39 - SlackMessageEntityType uint16 = 40 - TrustCenterFileEntityType uint16 = 41 - SAMLConfigurationEntityType uint16 = 42 - PersonalAPIKeyEntityType uint16 = 43 - _ uint16 = 44 // PersonalAPIKeyMembershipEntityType - removed - _ uint16 = 45 // MeetingEntityType - removed - DataProtectionImpactAssessmentEntityType uint16 = 46 - TransferImpactAssessmentEntityType uint16 = 47 - RightsRequestEntityType uint16 = 48 - StatementOfApplicabilityEntityType uint16 = 49 - ApplicabilityStatementEntityType uint16 = 50 - MembershipProfileEntityType uint16 = 51 - SCIMConfigurationEntityType uint16 = 52 - SCIMEventEntityType uint16 = 53 - TokenEntityType uint16 = 54 - SCIMBridgeEntityType uint16 = 55 - WebhookSubscriptionEntityType uint16 = 56 - WebhookDataEntityType uint16 = 57 - WebhookEventEntityType uint16 = 58 - ElectronicSignatureEntityType uint16 = 59 - ElectronicSignatureEventEntityType uint16 = 60 - EmailAttachmentEntityType uint16 = 61 - ComplianceFrameworkEntityType uint16 = 62 - ComplianceExternalURLEntityType uint16 = 63 - MailingListEntityType uint16 = 64 - MailingListSubscriberEntityType uint16 = 65 - MailingListUpdateEntityType uint16 = 66 - FindingEntityType uint16 = 67 - AuditLogEntryEntityType uint16 = 68 - DocumentVersionApprovalQuorumEntityType uint16 = 69 - DocumentVersionApprovalDecisionEntityType uint16 = 70 - AccessSourceEntityType uint16 = 71 - AccessReviewCampaignEntityType uint16 = 72 - AccessEntryEntityType uint16 = 73 - AccessEntryDecisionHistoryEntityType uint16 = 74 - CookieBannerEntityType uint16 = 75 - CookieCategoryEntityType uint16 = 76 - CookieConsentRecordEntityType uint16 = 77 - CookieBannerVersionEntityType uint16 = 78 - OAuth2ClientEntityType uint16 = 79 - OAuth2ConsentEntityType uint16 = 80 - OAuth2AccessTokenEntityType uint16 = 81 - OAuth2RefreshTokenEntityType uint16 = 82 - OAuth2AuthorizationCodeEntityType uint16 = 83 - OAuth2DeviceCodeEntityType uint16 = 84 - _ uint16 = 85 // CookieEntityType - removed - CookieBannerTranslationEntityType uint16 = 86 - AgentRunEntityType uint16 = 87 - _ uint16 = 88 // CookiePatternEntityType - removed - TrackerPatternEntityType uint16 = 89 - DetectedTrackerEntityType uint16 = 90 - TrackerResourceEntityType uint16 = 91 - CommonThirdPartyEntityType uint16 = 92 + OrganizationEntityType uint16 = 0 + FrameworkEntityType uint16 = 1 + MeasureEntityType uint16 = 2 + TaskEntityType uint16 = 3 + EvidenceEntityType uint16 = 4 + ConnectorEntityType uint16 = 5 + ThirdPartyRiskAssessmentEntityType uint16 = 6 + ThirdPartyEntityType uint16 = 7 + _ uint16 = 8 // PeopleEntityType - removed + ThirdPartyComplianceReportEntityType uint16 = 9 + DocumentEntityType uint16 = 10 + IdentityEntityType uint16 = 11 + SessionEntityType uint16 = 12 + EmailEntityType uint16 = 13 + ControlEntityType uint16 = 14 + RiskEntityType uint16 = 15 + DocumentVersionEntityType uint16 = 16 + DocumentVersionSignatureEntityType uint16 = 17 + AssetEntityType uint16 = 18 + DatumEntityType uint16 = 19 + AuditEntityType uint16 = 20 + ReportEntityType uint16 = 21 + TrustCenterEntityType uint16 = 22 + TrustCenterAccessEntityType uint16 = 23 + ThirdPartyBusinessAssociateAgreementEntityType uint16 = 24 + FileEntityType uint16 = 25 + ThirdPartyContactEntityType uint16 = 26 + ThirdPartyDataPrivacyAgreementEntityType uint16 = 27 + _ uint16 = 28 // NonconformityEntityType - removed + ObligationEntityType uint16 = 29 + ThirdPartyServiceEntityType uint16 = 30 + _ uint16 = 31 // SnapshotEntityType - removed + _ uint16 = 32 // ContinualImprovementEntityType - removed + ProcessingActivityEntityType uint16 = 33 + ExportJobEntityType uint16 = 34 + TrustCenterReferenceEntityType uint16 = 35 + TrustCenterDocumentAccessEntityType uint16 = 36 + CustomDomainEntityType uint16 = 37 + InvitationEntityType uint16 = 38 + MembershipEntityType uint16 = 39 + SlackMessageEntityType uint16 = 40 + TrustCenterFileEntityType uint16 = 41 + SAMLConfigurationEntityType uint16 = 42 + PersonalAPIKeyEntityType uint16 = 43 + _ uint16 = 44 // PersonalAPIKeyMembershipEntityType - removed + _ uint16 = 45 // MeetingEntityType - removed + DataProtectionImpactAssessmentEntityType uint16 = 46 + TransferImpactAssessmentEntityType uint16 = 47 + RightsRequestEntityType uint16 = 48 + StatementOfApplicabilityEntityType uint16 = 49 + ApplicabilityStatementEntityType uint16 = 50 + MembershipProfileEntityType uint16 = 51 + SCIMConfigurationEntityType uint16 = 52 + SCIMEventEntityType uint16 = 53 + TokenEntityType uint16 = 54 + SCIMBridgeEntityType uint16 = 55 + WebhookSubscriptionEntityType uint16 = 56 + WebhookDataEntityType uint16 = 57 + WebhookEventEntityType uint16 = 58 + ElectronicSignatureEntityType uint16 = 59 + ElectronicSignatureEventEntityType uint16 = 60 + EmailAttachmentEntityType uint16 = 61 + ComplianceFrameworkEntityType uint16 = 62 + ComplianceExternalURLEntityType uint16 = 63 + MailingListEntityType uint16 = 64 + MailingListSubscriberEntityType uint16 = 65 + MailingListUpdateEntityType uint16 = 66 + FindingEntityType uint16 = 67 + AuditLogEntryEntityType uint16 = 68 + DocumentVersionApprovalQuorumEntityType uint16 = 69 + DocumentVersionApprovalDecisionEntityType uint16 = 70 + AccessSourceEntityType uint16 = 71 + AccessReviewCampaignEntityType uint16 = 72 + AccessEntryEntityType uint16 = 73 + AccessEntryDecisionHistoryEntityType uint16 = 74 + CookieBannerEntityType uint16 = 75 + CookieCategoryEntityType uint16 = 76 + CookieConsentRecordEntityType uint16 = 77 + CookieBannerVersionEntityType uint16 = 78 + OAuth2ClientEntityType uint16 = 79 + OAuth2ConsentEntityType uint16 = 80 + OAuth2AccessTokenEntityType uint16 = 81 + OAuth2RefreshTokenEntityType uint16 = 82 + OAuth2AuthorizationCodeEntityType uint16 = 83 + OAuth2DeviceCodeEntityType uint16 = 84 + _ uint16 = 85 // CookieEntityType - removed + CookieBannerTranslationEntityType uint16 = 86 + AgentRunEntityType uint16 = 87 + _ uint16 = 88 // CookiePatternEntityType - removed + TrackerPatternEntityType uint16 = 89 + DetectedTrackerEntityType uint16 = 90 + TrackerResourceEntityType uint16 = 91 + CommonThirdPartyEntityType uint16 = 92 ) func NewEntityFromID(id gid.GID) (any, bool) { @@ -132,12 +132,12 @@ func NewEntityFromID(id gid.GID) (any, bool) { return &Evidence{ID: id}, true case ConnectorEntityType: return &Connector{ID: id}, true - case VendorRiskAssessmentEntityType: - return &VendorRiskAssessment{ID: id}, true - case VendorEntityType: - return &Vendor{ID: id}, true - case VendorComplianceReportEntityType: - return &VendorComplianceReport{ID: id}, true + case ThirdPartyRiskAssessmentEntityType: + return &ThirdPartyRiskAssessment{ID: id}, true + case ThirdPartyEntityType: + return &ThirdParty{ID: id}, true + case ThirdPartyComplianceReportEntityType: + return &ThirdPartyComplianceReport{ID: id}, true case DocumentEntityType: return &Document{ID: id}, true case IdentityEntityType: @@ -166,20 +166,20 @@ func NewEntityFromID(id gid.GID) (any, bool) { return &TrustCenter{ID: id}, true case TrustCenterAccessEntityType: return &TrustCenterAccess{ID: id}, true - case VendorBusinessAssociateAgreementEntityType: - return &VendorBusinessAssociateAgreement{ID: id}, true + case ThirdPartyBusinessAssociateAgreementEntityType: + return &ThirdPartyBusinessAssociateAgreement{ID: id}, true case FileEntityType: return &File{ID: id}, true - case VendorContactEntityType: - return &VendorContact{ID: id}, true - case VendorDataPrivacyAgreementEntityType: - return &VendorDataPrivacyAgreement{ID: id}, true + case ThirdPartyContactEntityType: + return &ThirdPartyContact{ID: id}, true + case ThirdPartyDataPrivacyAgreementEntityType: + return &ThirdPartyDataPrivacyAgreement{ID: id}, true case FindingEntityType: return &Finding{ID: id}, true case ObligationEntityType: return &Obligation{ID: id}, true - case VendorServiceEntityType: - return &VendorService{ID: id}, true + case ThirdPartyServiceEntityType: + return &ThirdPartyService{ID: id}, true case ProcessingActivityEntityType: return &ProcessingActivity{ID: id}, true case ExportJobEntityType: diff --git a/pkg/coredata/migrations/20260513T100000Z.sql b/pkg/coredata/migrations/20260513T100000Z.sql new file mode 100644 index 000000000..371f8b7e7 --- /dev/null +++ b/pkg/coredata/migrations/20260513T100000Z.sql @@ -0,0 +1,60 @@ +-- Copyright (c) 2026 Probo Inc . +-- +-- Permission to use, copy, modify, and/or distribute this software for any +-- purpose with or without fee is hereby granted, provided that the above +-- copyright notice and this permission notice appear in all copies. +-- +-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +-- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +-- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +-- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +-- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +-- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +-- PERFORMANCE OF THIS SOFTWARE. + +-- Rename vendor concept to third party everywhere in the schema. + +-- Rename the vendor_category enum. + +ALTER TYPE vendor_category RENAME TO third_party_category; + +-- Rename the main vendor tables. + +ALTER TABLE vendors RENAME TO third_parties; +ALTER TABLE vendor_contacts RENAME TO third_party_contacts; +ALTER TABLE vendor_services RENAME TO third_party_services; +ALTER TABLE vendor_compliance_reports RENAME TO third_party_compliance_reports; +ALTER TABLE vendor_business_associate_agreements RENAME TO third_party_business_associate_agreements; +ALTER TABLE vendor_data_privacy_agreements RENAME TO third_party_data_privacy_agreements; +ALTER TABLE vendor_risk_assessments RENAME TO third_party_risk_assessments; + +-- Rename the junction tables. + +ALTER TABLE asset_vendors RENAME TO asset_third_parties; +ALTER TABLE data_vendors RENAME TO data_third_parties; +ALTER TABLE processing_activity_vendors RENAME TO processing_activity_third_parties; + +-- Rename vendor_id columns on child tables. + +ALTER TABLE third_party_contacts RENAME COLUMN vendor_id TO third_party_id; +ALTER TABLE third_party_services RENAME COLUMN vendor_id TO third_party_id; +ALTER TABLE third_party_compliance_reports RENAME COLUMN vendor_id TO third_party_id; +ALTER TABLE third_party_business_associate_agreements RENAME COLUMN vendor_id TO third_party_id; +ALTER TABLE third_party_data_privacy_agreements RENAME COLUMN vendor_id TO third_party_id; +ALTER TABLE third_party_risk_assessments RENAME COLUMN vendor_id TO third_party_id; + +-- Rename vendor_id columns on junction tables. + +ALTER TABLE asset_third_parties RENAME COLUMN vendor_id TO third_party_id; +ALTER TABLE data_third_parties RENAME COLUMN vendor_id TO third_party_id; +ALTER TABLE processing_activity_third_parties RENAME COLUMN vendor_id TO third_party_id; + +-- Rename generated_documents.vendors_document_id. + +ALTER TABLE generated_documents RENAME COLUMN vendors_document_id TO third_parties_document_id; + +-- Rename webhook event type enum values. + +ALTER TYPE webhook_event_type RENAME VALUE 'vendor:created' TO 'third-party:created'; +ALTER TYPE webhook_event_type RENAME VALUE 'vendor:updated' TO 'third-party:updated'; +ALTER TYPE webhook_event_type RENAME VALUE 'vendor:deleted' TO 'third-party:deleted'; diff --git a/pkg/coredata/processing_activity_vendor.go b/pkg/coredata/processing_activity_third_party.go similarity index 67% rename from pkg/coredata/processing_activity_vendor.go rename to pkg/coredata/processing_activity_third_party.go index a4173419f..23c2943ee 100644 --- a/pkg/coredata/processing_activity_vendor.go +++ b/pkg/coredata/processing_activity_third_party.go @@ -25,42 +25,42 @@ import ( ) type ( - ProcessingActivityVendor struct { + ProcessingActivityThirdParty struct { ProcessingActivityID gid.GID `db:"processing_activity_id"` - VendorID gid.GID `db:"vendor_id"` + ThirdPartyID gid.GID `db:"third_party_id"` TenantID gid.TenantID `db:"tenant_id"` SnapshotID *gid.GID `db:"snapshot_id"` CreatedAt time.Time `db:"created_at"` } - ProcessingActivityVendors []*ProcessingActivityVendor + ProcessingActivityThirdParties []*ProcessingActivityThirdParty ) -func (pav ProcessingActivityVendors) Merge( +func (pav ProcessingActivityThirdParties) Merge( ctx context.Context, conn pg.Querier, scope Scoper, processingActivityID gid.GID, organizationID gid.GID, - vendorIDs []gid.GID, + thirdPartyIDs []gid.GID, ) error { q := ` -WITH vendor_ids AS ( +WITH third_party_ids AS ( SELECT - unnest(@vendor_ids::text[]) AS vendor_id, + unnest(@third_party_ids::text[]) AS third_party_id, @tenant_id AS tenant_id, @processing_activity_id AS processing_activity_id, @organization_id AS organization_id, @created_at::timestamptz AS created_at ) -MERGE INTO processing_activity_vendors AS tgt -USING vendor_ids AS src +MERGE INTO processing_activity_third_parties AS tgt +USING third_party_ids AS src ON tgt.tenant_id = src.tenant_id AND tgt.processing_activity_id = src.processing_activity_id - AND tgt.vendor_id = src.vendor_id + AND tgt.third_party_id = src.third_party_id WHEN NOT MATCHED - THEN INSERT (tenant_id, processing_activity_id, vendor_id, organization_id, created_at) - VALUES (src.tenant_id, src.processing_activity_id, src.vendor_id, src.organization_id, src.created_at) + THEN INSERT (tenant_id, processing_activity_id, third_party_id, organization_id, created_at) + VALUES (src.tenant_id, src.processing_activity_id, src.third_party_id, src.organization_id, src.created_at) WHEN NOT MATCHED BY SOURCE AND tgt.tenant_id = @tenant_id AND tgt.processing_activity_id = @processing_activity_id THEN DELETE @@ -71,37 +71,37 @@ WHEN NOT MATCHED "processing_activity_id": processingActivityID, "organization_id": organizationID, "created_at": time.Now(), - "vendor_ids": vendorIDs, + "third_party_ids": thirdPartyIDs, } _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot merge processing activity vendors: %w", err) + return fmt.Errorf("cannot merge processing activity thirdParties: %w", err) } return nil } -func (pav ProcessingActivityVendors) Insert( +func (pav ProcessingActivityThirdParties) Insert( ctx context.Context, conn pg.Tx, scope Scoper, processingActivityID gid.GID, organizationID gid.GID, - vendorIDs []gid.GID, + thirdPartyIDs []gid.GID, ) error { q := ` -WITH vendor_ids AS ( - SELECT unnest(@vendor_ids::text[]) AS vendor_id +WITH third_party_ids AS ( + SELECT unnest(@third_party_ids::text[]) AS third_party_id ) -INSERT INTO processing_activity_vendors (tenant_id, processing_activity_id, vendor_id, organization_id, created_at) +INSERT INTO processing_activity_third_parties (tenant_id, processing_activity_id, third_party_id, organization_id, created_at) SELECT @tenant_id AS tenant_id, @processing_activity_id AS processing_activity_id, - vendor_id, + third_party_id, @organization_id AS organization_id, @created_at AS created_at -FROM vendor_ids +FROM third_party_ids ` args := pgx.StrictNamedArgs{ @@ -109,12 +109,12 @@ FROM vendor_ids "processing_activity_id": processingActivityID, "organization_id": organizationID, "created_at": time.Now(), - "vendor_ids": vendorIDs, + "third_party_ids": thirdPartyIDs, } _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot insert processing activity vendors: %w", err) + return fmt.Errorf("cannot insert processing activity thirdParties: %w", err) } return nil diff --git a/pkg/coredata/vendor.go b/pkg/coredata/third_party.go similarity index 73% rename from pkg/coredata/vendor.go rename to pkg/coredata/third_party.go index bb78456b0..52a83ba64 100644 --- a/pkg/coredata/vendor.go +++ b/pkg/coredata/third_party.go @@ -27,7 +27,7 @@ import ( "go.probo.inc/probo/pkg/page" ) -func (v Vendor) GetGeneratedDocumentID( +func (v ThirdParty) GetGeneratedDocumentID( ctx context.Context, conn pg.Querier, organizationID gid.GID, @@ -38,7 +38,7 @@ func (v Vendor) GetGeneratedDocumentID( ctx, ` SELECT - vendors_document_id + third_parties_document_id FROM generated_documents WHERE @@ -50,13 +50,13 @@ WHERE return nil, nil } if err != nil { - return nil, fmt.Errorf("cannot get vendor list document ID: %w", err) + return nil, fmt.Errorf("cannot get thirdParty list document ID: %w", err) } return documentID, nil } -func (v Vendor) UpsertGeneratedDocumentID( +func (v ThirdParty) UpsertGeneratedDocumentID( ctx context.Context, conn pg.Tx, organizationID gid.GID, @@ -71,37 +71,37 @@ func (v Vendor) UpsertGeneratedDocumentID( INSERT INTO generated_documents ( organization_id, tenant_id, - vendors_document_id, + third_parties_document_id, created_at, updated_at ) VALUES ( @organization_id, @tenant_id, - @vendors_document_id, + @third_parties_document_id, @created_at, @updated_at ) ON CONFLICT (organization_id) DO UPDATE SET - vendors_document_id = @vendors_document_id, + third_parties_document_id = @third_parties_document_id, updated_at = @updated_at `, pgx.NamedArgs{ - "organization_id": organizationID, - "tenant_id": tenantID, - "vendors_document_id": documentID, - "created_at": now, - "updated_at": now, + "organization_id": organizationID, + "tenant_id": tenantID, + "third_parties_document_id": documentID, + "created_at": now, + "updated_at": now, }, ) if err != nil { - return fmt.Errorf("cannot upsert vendor list document ID: %w", err) + return fmt.Errorf("cannot upsert thirdParty list document ID: %w", err) } return nil } -func (v Vendor) ClearGeneratedDocumentID( +func (v ThirdParty) ClearGeneratedDocumentID( ctx context.Context, conn pg.Tx, documentIDs []gid.GID, @@ -117,10 +117,10 @@ func (v Vendor) ClearGeneratedDocumentID( UPDATE generated_documents SET - vendors_document_id = NULL, + third_parties_document_id = NULL, updated_at = @now WHERE - vendors_document_id = ANY(@ids) + third_parties_document_id = ANY(@ids) `, pgx.NamedArgs{ "ids": ids, @@ -128,76 +128,76 @@ WHERE }, ) if err != nil { - return fmt.Errorf("cannot clear vendor list document references: %w", err) + return fmt.Errorf("cannot clear thirdParty list document references: %w", err) } return nil } type ( - Vendor struct { - ID gid.GID `db:"id"` - TenantID gid.TenantID `db:"tenant_id"` - OrganizationID gid.GID `db:"organization_id"` - Name string `db:"name"` - Description *string `db:"description"` - Category VendorCategory `db:"category"` - HeadquarterAddress *string `db:"headquarter_address"` - LegalName *string `db:"legal_name"` - WebsiteURL *string `db:"website_url"` - PrivacyPolicyURL *string `db:"privacy_policy_url"` - ServiceLevelAgreementURL *string `db:"service_level_agreement_url"` - DataProcessingAgreementURL *string `db:"data_processing_agreement_url"` - BusinessAssociateAgreementURL *string `db:"business_associate_agreement_url"` - SubprocessorsListURL *string `db:"subprocessors_list_url"` - Certifications []string `db:"certifications"` - Countries CountryCodes `db:"countries"` - BusinessOwnerID *gid.GID `db:"business_owner_profile_id"` - SecurityOwnerID *gid.GID `db:"security_owner_profile_id"` - StatusPageURL *string `db:"status_page_url"` - TermsOfServiceURL *string `db:"terms_of_service_url"` - SecurityPageURL *string `db:"security_page_url"` - TrustPageURL *string `db:"trust_page_url"` - ShowOnTrustCenter bool `db:"show_on_trust_center"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` + ThirdParty struct { + ID gid.GID `db:"id"` + TenantID gid.TenantID `db:"tenant_id"` + OrganizationID gid.GID `db:"organization_id"` + Name string `db:"name"` + Description *string `db:"description"` + Category ThirdPartyCategory `db:"category"` + HeadquarterAddress *string `db:"headquarter_address"` + LegalName *string `db:"legal_name"` + WebsiteURL *string `db:"website_url"` + PrivacyPolicyURL *string `db:"privacy_policy_url"` + ServiceLevelAgreementURL *string `db:"service_level_agreement_url"` + DataProcessingAgreementURL *string `db:"data_processing_agreement_url"` + BusinessAssociateAgreementURL *string `db:"business_associate_agreement_url"` + SubprocessorsListURL *string `db:"subprocessors_list_url"` + Certifications []string `db:"certifications"` + Countries CountryCodes `db:"countries"` + BusinessOwnerID *gid.GID `db:"business_owner_profile_id"` + SecurityOwnerID *gid.GID `db:"security_owner_profile_id"` + StatusPageURL *string `db:"status_page_url"` + TermsOfServiceURL *string `db:"terms_of_service_url"` + SecurityPageURL *string `db:"security_page_url"` + TrustPageURL *string `db:"trust_page_url"` + ShowOnTrustCenter bool `db:"show_on_trust_center"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` } - Vendors []*Vendor + ThirdParties []*ThirdParty ) -func (v Vendor) CursorKey(orderBy VendorOrderField) page.CursorKey { +func (v ThirdParty) CursorKey(orderBy ThirdPartyOrderField) page.CursorKey { switch orderBy { - case VendorOrderFieldCreatedAt: + case ThirdPartyOrderFieldCreatedAt: return page.NewCursorKey(v.ID, v.CreatedAt) - case VendorOrderFieldUpdatedAt: + case ThirdPartyOrderFieldUpdatedAt: return page.NewCursorKey(v.ID, v.UpdatedAt) - case VendorOrderFieldName: + case ThirdPartyOrderFieldName: return page.NewCursorKey(v.ID, v.Name) } panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } -func (v *Vendor) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { - q := `SELECT organization_id FROM vendors WHERE id = $1 LIMIT 1;` +func (v *ThirdParty) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { + q := `SELECT organization_id FROM third_parties WHERE id = $1 LIMIT 1;` var organizationID gid.GID if err := conn.QueryRow(ctx, q, v.ID).Scan(&organizationID); err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, ErrResourceNotFound } - return nil, fmt.Errorf("cannot query vendor authorization attributes: %w", err) + return nil, fmt.Errorf("cannot query thirdParty authorization attributes: %w", err) } return map[string]string{"organization_id": organizationID.String()}, nil } -func (v *Vendor) LoadByID( +func (v *ThirdParty) LoadByID( ctx context.Context, conn pg.Querier, scope Scoper, - vendorID gid.GID, + thirdPartyID gid.GID, ) error { q := ` SELECT @@ -227,43 +227,43 @@ SELECT created_at, updated_at FROM - vendors + third_parties WHERE %s - AND id = @vendor_id + AND id = @third_party_id LIMIT 1; ` q = fmt.Sprintf(q, scope.SQLFragment()) - args := pgx.StrictNamedArgs{"vendor_id": vendorID} + args := pgx.StrictNamedArgs{"third_party_id": thirdPartyID} maps.Copy(args, scope.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendor: %w", err) + return fmt.Errorf("cannot query thirdParty: %w", err) } defer rows.Close() - vendor, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Vendor]) + thirdParty, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[ThirdParty]) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return ErrResourceNotFound } - return fmt.Errorf("cannot collect vendor: %w", err) + return fmt.Errorf("cannot collect thirdParty: %w", err) } - *v = vendor + *v = thirdParty return nil } -func (v *Vendors) LoadByIDs( +func (v *ThirdParties) LoadByIDs( ctx context.Context, conn pg.Querier, scope Scoper, - vendorIDs []gid.GID, + thirdPartyIDs []gid.GID, ) error { q := ` SELECT @@ -293,40 +293,40 @@ SELECT created_at, updated_at FROM - vendors + third_parties WHERE %s - AND id = ANY(@vendor_ids) + AND id = ANY(@third_party_ids) ` q = fmt.Sprintf(q, scope.SQLFragment()) - args := pgx.StrictNamedArgs{"vendor_ids": vendorIDs} + args := pgx.StrictNamedArgs{"third_party_ids": thirdPartyIDs} maps.Copy(args, scope.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendors: %w", err) + return fmt.Errorf("cannot query thirdParties: %w", err) } - vendors, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Vendor]) + thirdParties, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ThirdParty]) if err != nil { - return fmt.Errorf("cannot collect vendors: %w", err) + return fmt.Errorf("cannot collect thirdParties: %w", err) } - *v = vendors + *v = thirdParties return nil } -func (v Vendor) Insert( +func (v ThirdParty) Insert( ctx context.Context, conn pg.Tx, scope Scoper, ) error { q := ` INSERT INTO - vendors ( + third_parties ( tenant_id, id, organization_id, @@ -355,7 +355,7 @@ INSERT INTO ) VALUES ( @tenant_id, - @vendor_id, + @third_party_id, @organization_id, @name, @description, @@ -384,7 +384,7 @@ VALUES ( args := pgx.StrictNamedArgs{ "tenant_id": scope.GetTenantID(), - "vendor_id": v.ID, + "third_party_id": v.ID, "organization_id": v.OrganizationID, "name": v.Name, "description": v.Description, @@ -413,36 +413,36 @@ VALUES ( return err } -func (v Vendor) Delete( +func (v ThirdParty) Delete( ctx context.Context, conn pg.Tx, scope Scoper, ) error { q := ` -DELETE FROM vendors WHERE %s AND id = @vendor_id +DELETE FROM third_parties WHERE %s AND id = @third_party_id ` q = fmt.Sprintf(q, scope.SQLFragment()) - args := pgx.StrictNamedArgs{"vendor_id": v.ID} + args := pgx.StrictNamedArgs{"third_party_id": v.ID} maps.Copy(args, scope.SQLArguments()) _, err := conn.Exec(ctx, q, args) return err } -func (v *Vendors) CountByOrganizationID( +func (v *ThirdParties) CountByOrganizationID( ctx context.Context, conn pg.Querier, scope Scoper, organizationID gid.GID, - filter *VendorFilter, + filter *ThirdPartyFilter, ) (int, error) { q := ` SELECT COUNT(id) FROM - vendors + third_parties WHERE %s AND organization_id = @organization_id @@ -461,13 +461,13 @@ WHERE var count int err := row.Scan(&count) if err != nil { - return 0, fmt.Errorf("cannot count vendors: %w", err) + return 0, fmt.Errorf("cannot count thirdParties: %w", err) } return count, nil } -func (v *Vendors) LoadAllByOrganizationID( +func (v *ThirdParties) LoadAllByOrganizationID( ctx context.Context, conn pg.Querier, scope Scoper, @@ -501,7 +501,7 @@ SELECT created_at, updated_at FROM - vendors + third_parties WHERE %s AND organization_id = @organization_id @@ -515,26 +515,26 @@ ORDER BY name ASC rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendors: %w", err) + return fmt.Errorf("cannot query thirdParties: %w", err) } - vendors, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Vendor]) + thirdParties, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ThirdParty]) if err != nil { - return fmt.Errorf("cannot collect vendors: %w", err) + return fmt.Errorf("cannot collect thirdParties: %w", err) } - *v = vendors + *v = thirdParties return nil } -func (v *Vendors) LoadByOrganizationID( +func (v *ThirdParties) LoadByOrganizationID( ctx context.Context, conn pg.Querier, scope Scoper, organizationID gid.GID, - cursor *page.Cursor[VendorOrderField], - filter *VendorFilter, + cursor *page.Cursor[ThirdPartyOrderField], + filter *ThirdPartyFilter, ) error { q := ` SELECT @@ -564,7 +564,7 @@ SELECT created_at, updated_at FROM - vendors + third_parties WHERE %s AND organization_id = @organization_id @@ -581,26 +581,26 @@ WHERE rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendors: %w", err) + return fmt.Errorf("cannot query thirdParties: %w", err) } - vendors, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Vendor]) + thirdParties, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ThirdParty]) if err != nil { - return fmt.Errorf("cannot collect vendors: %w", err) + return fmt.Errorf("cannot collect thirdParties: %w", err) } - *v = vendors + *v = thirdParties return nil } -func (v *Vendor) Update( +func (v *ThirdParty) Update( ctx context.Context, conn pg.Tx, scope Scoper, ) error { q := ` -UPDATE vendors +UPDATE third_parties SET name = @name, description = @description, @@ -624,12 +624,12 @@ SET show_on_trust_center = @show_on_trust_center, updated_at = @updated_at WHERE %s - AND id = @vendor_id + AND id = @third_party_id ` q = fmt.Sprintf(q, scope.SQLFragment()) args := pgx.StrictNamedArgs{ - "vendor_id": v.ID, + "third_party_id": v.ID, "updated_at": time.Now(), "name": v.Name, "description": v.Description, @@ -659,7 +659,7 @@ WHERE %s return err } -func (v Vendor) ExpireNonExpiredRiskAssessments( +func (v ThirdParty) ExpireNonExpiredRiskAssessments( ctx context.Context, conn pg.Querier, scope Scoper, @@ -667,21 +667,21 @@ func (v Vendor) ExpireNonExpiredRiskAssessments( now := time.Now() q := ` - UPDATE vendor_risk_assessments + UPDATE third_party_risk_assessments SET expires_at = @now, updated_at = @now WHERE %s - AND vendor_id = @vendor_id + AND third_party_id = @third_party_id AND expires_at > @now ` q = fmt.Sprintf(q, scope.SQLFragment()) args := pgx.StrictNamedArgs{ - "vendor_id": v.ID, - "now": now, + "third_party_id": v.ID, + "now": now, } maps.Copy(args, scope.SQLArguments()) @@ -693,7 +693,7 @@ func (v Vendor) ExpireNonExpiredRiskAssessments( return nil } -func (v *Vendors) CountByAssetID( +func (v *ThirdParties) CountByAssetID( ctx context.Context, conn pg.Querier, scope Scoper, @@ -704,9 +704,9 @@ WITH vend AS ( SELECT v.id FROM - vendors v + third_parties v INNER JOIN - asset_vendors av ON v.id = av.vendor_id + asset_third_parties av ON v.id = av.third_party_id WHERE av.asset_id = @asset_id ) @@ -726,18 +726,18 @@ WHERE %s var count int err := row.Scan(&count) if err != nil { - return 0, fmt.Errorf("cannot count vendors: %w", err) + return 0, fmt.Errorf("cannot count thirdParties: %w", err) } return count, nil } -func (v *Vendors) LoadByAssetID( +func (v *ThirdParties) LoadByAssetID( ctx context.Context, conn pg.Querier, scope Scoper, assetID gid.GID, - cursor *page.Cursor[VendorOrderField], + cursor *page.Cursor[ThirdPartyOrderField], ) error { q := ` WITH vend AS ( @@ -768,9 +768,9 @@ WITH vend AS ( v.created_at, v.updated_at FROM - vendors v + third_parties v INNER JOIN - asset_vendors av ON v.id = av.vendor_id + asset_third_parties av ON v.id = av.third_party_id WHERE av.asset_id = @asset_id ) @@ -813,20 +813,20 @@ WHERE %s rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendors: %w", err) + return fmt.Errorf("cannot query thirdParties: %w", err) } - vendors, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Vendor]) + thirdParties, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ThirdParty]) if err != nil { - return fmt.Errorf("cannot collect vendors: %w", err) + return fmt.Errorf("cannot collect thirdParties: %w", err) } - *v = vendors + *v = thirdParties return nil } -func (v *Vendors) CountByDatumID( +func (v *ThirdParties) CountByDatumID( ctx context.Context, conn pg.Querier, scope Scoper, @@ -837,9 +837,9 @@ WITH vend AS ( SELECT v.id FROM - vendors v + third_parties v INNER JOIN - data_vendors dv ON v.id = dv.vendor_id + data_third_parties dv ON v.id = dv.third_party_id WHERE dv.datum_id = @datum_id ) @@ -859,13 +859,13 @@ WHERE %s var count int err := row.Scan(&count) if err != nil { - return 0, fmt.Errorf("cannot count vendors: %w", err) + return 0, fmt.Errorf("cannot count thirdParties: %w", err) } return count, nil } -func (vs *Vendors) LoadAllByDatumID( +func (vs *ThirdParties) LoadAllByDatumID( ctx context.Context, conn pg.Querier, scope Scoper, @@ -900,9 +900,9 @@ WITH vend AS ( v.created_at, v.updated_at FROM - vendors v + third_parties v INNER JOIN - data_vendors dv ON v.id = dv.vendor_id + data_third_parties dv ON v.id = dv.third_party_id WHERE dv.datum_id = @datum_id ) @@ -944,25 +944,25 @@ ORDER BY name ASC rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendors: %w", err) + return fmt.Errorf("cannot query thirdParties: %w", err) } - vendors, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Vendor]) + thirdParties, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ThirdParty]) if err != nil { - return fmt.Errorf("cannot collect vendors: %w", err) + return fmt.Errorf("cannot collect thirdParties: %w", err) } - *vs = vendors + *vs = thirdParties return nil } -func (vs *Vendors) LoadByDatumID( +func (vs *ThirdParties) LoadByDatumID( ctx context.Context, conn pg.Querier, scope Scoper, datumID gid.GID, - cursor *page.Cursor[VendorOrderField], + cursor *page.Cursor[ThirdPartyOrderField], ) error { q := ` WITH vend AS ( @@ -993,9 +993,9 @@ WITH vend AS ( v.created_at, v.updated_at FROM - vendors v + third_parties v INNER JOIN - data_vendors dv ON v.id = dv.vendor_id + data_third_parties dv ON v.id = dv.third_party_id WHERE dv.datum_id = @datum_id ) @@ -1038,25 +1038,25 @@ WHERE %s rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendors: %w", err) + return fmt.Errorf("cannot query thirdParties: %w", err) } - vendors, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Vendor]) + thirdParties, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ThirdParty]) if err != nil { - return fmt.Errorf("cannot collect vendors: %w", err) + return fmt.Errorf("cannot collect thirdParties: %w", err) } - *vs = vendors + *vs = thirdParties return nil } -func (v *Vendors) LoadByProcessingActivityID( +func (v *ThirdParties) LoadByProcessingActivityID( ctx context.Context, conn pg.Querier, scope Scoper, processingActivityID gid.GID, - cursor *page.Cursor[VendorOrderField], + cursor *page.Cursor[ThirdPartyOrderField], ) error { q := ` WITH vend AS ( @@ -1087,9 +1087,9 @@ WITH vend AS ( v.created_at, v.updated_at FROM - vendors v + third_parties v INNER JOIN - processing_activity_vendors pav ON v.id = pav.vendor_id + processing_activity_third_parties pav ON v.id = pav.third_party_id WHERE pav.processing_activity_id = @processing_activity_id ) @@ -1132,20 +1132,20 @@ WHERE %s rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendors: %w", err) + return fmt.Errorf("cannot query thirdParties: %w", err) } - vendors, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Vendor]) + thirdParties, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ThirdParty]) if err != nil { - return fmt.Errorf("cannot collect vendors: %w", err) + return fmt.Errorf("cannot collect thirdParties: %w", err) } - *v = vendors + *v = thirdParties return nil } -func (v *Vendors) LoadAllByProcessingActivities( +func (v *ThirdParties) LoadAllByProcessingActivities( ctx context.Context, conn pg.Querier, scope Scoper, @@ -1162,12 +1162,12 @@ WITH filtered_processing_activities AS ( AND pa.organization_id = @organization_id AND pa.snapshot_id IS NULL ), -filtered_vendors AS ( +filtered_third_parties AS ( SELECT v.id, v.name FROM - vendors v + third_parties v WHERE v.tenant_id = @tenant_id AND v.snapshot_id IS NULL @@ -1176,9 +1176,9 @@ SELECT pav.processing_activity_id, fv.name FROM - processing_activity_vendors pav + processing_activity_third_parties pav INNER JOIN - filtered_vendors fv ON fv.id = pav.vendor_id + filtered_third_parties fv ON fv.id = pav.third_party_id INNER JOIN filtered_processing_activities fpa ON fpa.id = pav.processing_activity_id WHERE @@ -1194,24 +1194,24 @@ ORDER BY rows, err := conn.Query(ctx, q, args) if err != nil { - return nil, fmt.Errorf("cannot query vendors: %w", err) + return nil, fmt.Errorf("cannot query thirdParties: %w", err) } defer rows.Close() - vendorMap := make(map[gid.GID][]string) + thirdPartyMap := make(map[gid.GID][]string) for rows.Next() { var processingActivityID gid.GID - var vendorName string - if err := rows.Scan(&processingActivityID, &vendorName); err != nil { - return nil, fmt.Errorf("cannot scan vendor: %w", err) + var thirdPartyName string + if err := rows.Scan(&processingActivityID, &thirdPartyName); err != nil { + return nil, fmt.Errorf("cannot scan thirdParty: %w", err) } - vendorMap[processingActivityID] = append(vendorMap[processingActivityID], vendorName) + thirdPartyMap[processingActivityID] = append(thirdPartyMap[processingActivityID], thirdPartyName) } - return vendorMap, nil + return thirdPartyMap, nil } -func (vs *Vendors) LoadAllByAssetID( +func (vs *ThirdParties) LoadAllByAssetID( ctx context.Context, conn pg.Querier, scope Scoper, @@ -1246,9 +1246,9 @@ WITH vend AS ( v.created_at, v.updated_at FROM - vendors v + third_parties v INNER JOIN - asset_vendors av ON v.id = av.vendor_id + asset_third_parties av ON v.id = av.third_party_id WHERE av.asset_id = @asset_id ) @@ -1290,15 +1290,15 @@ ORDER BY name ASC rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendors: %w", err) + return fmt.Errorf("cannot query thirdParties: %w", err) } - vendors, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Vendor]) + thirdParties, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ThirdParty]) if err != nil { - return fmt.Errorf("cannot collect vendors: %w", err) + return fmt.Errorf("cannot collect thirdParties: %w", err) } - *vs = vendors + *vs = thirdParties return nil } diff --git a/pkg/coredata/vendor_business_associate_agreement.go b/pkg/coredata/third_party_business_associate_agreement.go similarity index 59% rename from pkg/coredata/vendor_business_associate_agreement.go rename to pkg/coredata/third_party_business_associate_agreement.go index 654c930b2..7ff3799f1 100644 --- a/pkg/coredata/vendor_business_associate_agreement.go +++ b/pkg/coredata/third_party_business_associate_agreement.go @@ -29,10 +29,10 @@ import ( ) type ( - VendorBusinessAssociateAgreement struct { + ThirdPartyBusinessAssociateAgreement struct { ID gid.GID `db:"id"` OrganizationID gid.GID `db:"organization_id"` - VendorID gid.GID `db:"vendor_id"` + ThirdPartyID gid.GID `db:"third_party_id"` ValidFrom *time.Time `db:"valid_from"` ValidUntil *time.Time `db:"valid_until"` FileID gid.GID `db:"file_id"` @@ -40,87 +40,87 @@ type ( UpdatedAt time.Time `db:"updated_at"` } - VendorBusinessAssociateAgreements []*VendorBusinessAssociateAgreement + ThirdPartyBusinessAssociateAgreements []*ThirdPartyBusinessAssociateAgreement ) -func (v VendorBusinessAssociateAgreement) CursorKey(orderBy VendorBusinessAssociateAgreementOrderField) page.CursorKey { +func (v ThirdPartyBusinessAssociateAgreement) CursorKey(orderBy ThirdPartyBusinessAssociateAgreementOrderField) page.CursorKey { switch orderBy { - case VendorBusinessAssociateAgreementOrderFieldValidFrom: + case ThirdPartyBusinessAssociateAgreementOrderFieldValidFrom: return page.NewCursorKey(v.ID, v.ValidFrom) - case VendorBusinessAssociateAgreementOrderFieldCreatedAt: + case ThirdPartyBusinessAssociateAgreementOrderFieldCreatedAt: return page.NewCursorKey(v.ID, v.CreatedAt) } panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } -func (vbaa *VendorBusinessAssociateAgreement) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { - q := `SELECT organization_id FROM vendor_business_associate_agreements WHERE id = $1 LIMIT 1;` +func (vbaa *ThirdPartyBusinessAssociateAgreement) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { + q := `SELECT organization_id FROM third_party_business_associate_agreements WHERE id = $1 LIMIT 1;` var organizationID gid.GID if err := conn.QueryRow(ctx, q, vbaa.ID).Scan(&organizationID); err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, ErrResourceNotFound } - return nil, fmt.Errorf("cannot query vendor business associate agreement authorization attributes: %w", err) + return nil, fmt.Errorf("cannot query thirdParty business associate agreement authorization attributes: %w", err) } return map[string]string{"organization_id": organizationID.String()}, nil } -func (vbaa *VendorBusinessAssociateAgreement) LoadByVendorID( +func (vbaa *ThirdPartyBusinessAssociateAgreement) LoadByThirdPartyID( ctx context.Context, conn pg.Querier, scope Scoper, - vendorID gid.GID, + thirdPartyID gid.GID, ) error { q := ` SELECT id, organization_id, - vendor_id, + third_party_id, valid_from, valid_until, file_id, created_at, updated_at FROM - vendor_business_associate_agreements + third_party_business_associate_agreements WHERE %s - AND vendor_id = @vendor_id + AND third_party_id = @third_party_id AND snapshot_id IS NULL LIMIT 1; ` q = fmt.Sprintf(q, scope.SQLFragment()) - args := pgx.NamedArgs{"vendor_id": vendorID} + args := pgx.NamedArgs{"third_party_id": thirdPartyID} maps.Copy(args, scope.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendor business associate agreement: %w", err) + return fmt.Errorf("cannot query thirdParty business associate agreement: %w", err) } - vendorBusinessAssociateAgreement, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[VendorBusinessAssociateAgreement]) + thirdPartyBusinessAssociateAgreement, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[ThirdPartyBusinessAssociateAgreement]) if err != nil { - return fmt.Errorf("cannot collect vendor business associate agreement: %w", err) + return fmt.Errorf("cannot collect thirdParty business associate agreement: %w", err) } - *vbaa = vendorBusinessAssociateAgreement + *vbaa = thirdPartyBusinessAssociateAgreement return nil } -func (vbaas *VendorBusinessAssociateAgreements) LoadByVendorIDs( +func (vbaas *ThirdPartyBusinessAssociateAgreements) LoadByThirdPartyIDs( ctx context.Context, conn pg.Querier, scope Scoper, - vendorIDs []gid.GID, + thirdPartyIDs []gid.GID, ) error { - if len(vendorIDs) == 0 { - *vbaas = VendorBusinessAssociateAgreements{} + if len(thirdPartyIDs) == 0 { + *vbaas = ThirdPartyBusinessAssociateAgreements{} return nil } @@ -128,38 +128,38 @@ func (vbaas *VendorBusinessAssociateAgreements) LoadByVendorIDs( SELECT id, organization_id, - vendor_id, + third_party_id, valid_from, valid_until, file_id, created_at, updated_at FROM - vendor_business_associate_agreements + third_party_business_associate_agreements WHERE %s - AND vendor_id = ANY(@vendor_ids) + AND third_party_id = ANY(@third_party_ids) AND snapshot_id IS NULL ` q = fmt.Sprintf(q, scope.SQLFragment()) - ids := make([]string, len(vendorIDs)) - for i, id := range vendorIDs { + ids := make([]string, len(thirdPartyIDs)) + for i, id := range thirdPartyIDs { ids[i] = id.String() } - args := pgx.NamedArgs{"vendor_ids": ids} + args := pgx.NamedArgs{"third_party_ids": ids} maps.Copy(args, scope.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendor business associate agreements: %w", err) + return fmt.Errorf("cannot query thirdParty business associate agreements: %w", err) } - agreements, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[VendorBusinessAssociateAgreement]) + agreements, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ThirdPartyBusinessAssociateAgreement]) if err != nil { - return fmt.Errorf("cannot collect vendor business associate agreements: %w", err) + return fmt.Errorf("cannot collect thirdParty business associate agreements: %w", err) } *vbaas = agreements @@ -167,24 +167,24 @@ WHERE return nil } -func (vbaa *VendorBusinessAssociateAgreement) LoadByID( +func (vbaa *ThirdPartyBusinessAssociateAgreement) LoadByID( ctx context.Context, conn pg.Querier, scope Scoper, - vendorBusinessAssociateAgreementID gid.GID, + thirdPartyBusinessAssociateAgreementID gid.GID, ) error { q := ` SELECT id, organization_id, - vendor_id, + third_party_id, valid_from, valid_until, file_id, created_at, updated_at FROM - vendor_business_associate_agreements + third_party_business_associate_agreements WHERE %s AND id = @id @@ -193,32 +193,32 @@ LIMIT 1; q = fmt.Sprintf(q, scope.SQLFragment()) - args := pgx.NamedArgs{"id": vendorBusinessAssociateAgreementID} + args := pgx.NamedArgs{"id": thirdPartyBusinessAssociateAgreementID} maps.Copy(args, scope.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendor business associate agreement: %w", err) + return fmt.Errorf("cannot query thirdParty business associate agreement: %w", err) } - vendorBusinessAssociateAgreement, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[VendorBusinessAssociateAgreement]) + thirdPartyBusinessAssociateAgreement, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[ThirdPartyBusinessAssociateAgreement]) if err != nil { - return fmt.Errorf("cannot collect vendor business associate agreement: %w", err) + return fmt.Errorf("cannot collect thirdParty business associate agreement: %w", err) } - *vbaa = vendorBusinessAssociateAgreement + *vbaa = thirdPartyBusinessAssociateAgreement return nil } -func (vbaa *VendorBusinessAssociateAgreement) Update( +func (vbaa *ThirdPartyBusinessAssociateAgreement) Update( ctx context.Context, conn pg.Tx, scope Scoper, ) error { q := ` UPDATE - vendor_business_associate_agreements + third_party_business_associate_agreements SET valid_from = @valid_from, valid_until = @valid_until, @@ -243,24 +243,24 @@ WHERE _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot update vendor business associate agreement: %w", err) + return fmt.Errorf("cannot update thirdParty business associate agreement: %w", err) } return nil } -func (vbaa *VendorBusinessAssociateAgreement) Upsert( +func (vbaa *ThirdPartyBusinessAssociateAgreement) Upsert( ctx context.Context, conn pg.Querier, scope Scoper, ) error { q := ` INSERT INTO - vendor_business_associate_agreements ( + third_party_business_associate_agreements ( id, tenant_id, organization_id, - vendor_id, + third_party_id, valid_from, valid_until, file_id, @@ -271,14 +271,14 @@ VALUES ( @id, @tenant_id, @organization_id, - @vendor_id, + @third_party_id, @valid_from, @valid_until, @file_id, @created_at, @updated_at ) -ON CONFLICT (organization_id, vendor_id) DO UPDATE SET +ON CONFLICT (organization_id, third_party_id) DO UPDATE SET id = EXCLUDED.id, valid_from = EXCLUDED.valid_from, valid_until = EXCLUDED.valid_until, @@ -288,7 +288,7 @@ ON CONFLICT (organization_id, vendor_id) DO UPDATE SET args := pgx.StrictNamedArgs{ "id": vbaa.ID, "tenant_id": scope.GetTenantID(), - "vendor_id": vbaa.VendorID, + "third_party_id": vbaa.ThirdPartyID, "organization_id": vbaa.OrganizationID, "valid_from": vbaa.ValidFrom, "valid_until": vbaa.ValidUntil, @@ -301,16 +301,16 @@ ON CONFLICT (organization_id, vendor_id) DO UPDATE SET if err != nil { var pgErr *pgconn.PgError if errors.As(err, &pgErr) { - if pgErr.Code == "23505" && pgErr.ConstraintName == "vendor_business_associate_agreements_source_id_snapshot_id_key" { + if pgErr.Code == "23505" && pgErr.ConstraintName == "third_party_business_associate_agreements_source_id_snapshot_id_key" { return ErrResourceAlreadyExists } } - return fmt.Errorf("cannot upsert vendor business associate agreement: %w", err) + return fmt.Errorf("cannot upsert thirdParty business associate agreement: %w", err) } return nil } -func (vbaa *VendorBusinessAssociateAgreement) Delete( +func (vbaa *ThirdPartyBusinessAssociateAgreement) Delete( ctx context.Context, conn pg.Tx, scope Scoper, @@ -318,7 +318,7 @@ func (vbaa *VendorBusinessAssociateAgreement) Delete( q := ` DELETE FROM - vendor_business_associate_agreements + third_party_business_associate_agreements WHERE %s AND id = @id @@ -334,25 +334,25 @@ WHERE return err } -func (vbaa *VendorBusinessAssociateAgreement) DeleteByVendorID( +func (vbaa *ThirdPartyBusinessAssociateAgreement) DeleteByThirdPartyID( ctx context.Context, conn pg.Tx, scope Scoper, - vendorID gid.GID, + thirdPartyID gid.GID, ) error { q := ` DELETE FROM - vendor_business_associate_agreements + third_party_business_associate_agreements WHERE %s - AND vendor_id = @vendor_id + AND third_party_id = @third_party_id AND snapshot_id IS NULL ` q = fmt.Sprintf(q, scope.SQLFragment()) - args := pgx.StrictNamedArgs{"vendor_id": vendorID} + args := pgx.StrictNamedArgs{"third_party_id": thirdPartyID} maps.Copy(args, scope.SQLArguments()) _, err := conn.Exec(ctx, q, args) diff --git a/pkg/coredata/vendor_business_associate_agreement_order_field.go b/pkg/coredata/third_party_business_associate_agreement_order_field.go similarity index 57% rename from pkg/coredata/vendor_business_associate_agreement_order_field.go rename to pkg/coredata/third_party_business_associate_agreement_order_field.go index 303c85a76..77047cdce 100644 --- a/pkg/coredata/vendor_business_associate_agreement_order_field.go +++ b/pkg/coredata/third_party_business_associate_agreement_order_field.go @@ -15,27 +15,27 @@ package coredata type ( - VendorBusinessAssociateAgreementOrderField string + ThirdPartyBusinessAssociateAgreementOrderField string ) const ( - VendorBusinessAssociateAgreementOrderFieldValidFrom VendorBusinessAssociateAgreementOrderField = "VALID_FROM" - VendorBusinessAssociateAgreementOrderFieldCreatedAt VendorBusinessAssociateAgreementOrderField = "CREATED_AT" + ThirdPartyBusinessAssociateAgreementOrderFieldValidFrom ThirdPartyBusinessAssociateAgreementOrderField = "VALID_FROM" + ThirdPartyBusinessAssociateAgreementOrderFieldCreatedAt ThirdPartyBusinessAssociateAgreementOrderField = "CREATED_AT" ) -func (p VendorBusinessAssociateAgreementOrderField) Column() string { +func (p ThirdPartyBusinessAssociateAgreementOrderField) Column() string { return string(p) } -func (p VendorBusinessAssociateAgreementOrderField) String() string { +func (p ThirdPartyBusinessAssociateAgreementOrderField) String() string { return string(p) } -func (p VendorBusinessAssociateAgreementOrderField) MarshalText() ([]byte, error) { +func (p ThirdPartyBusinessAssociateAgreementOrderField) MarshalText() ([]byte, error) { return []byte(p.String()), nil } -func (p *VendorBusinessAssociateAgreementOrderField) UnmarshalText(text []byte) error { - *p = VendorBusinessAssociateAgreementOrderField(text) +func (p *ThirdPartyBusinessAssociateAgreementOrderField) UnmarshalText(text []byte) error { + *p = ThirdPartyBusinessAssociateAgreementOrderField(text) return nil } diff --git a/pkg/coredata/third_party_category.go b/pkg/coredata/third_party_category.go new file mode 100644 index 000000000..cfdf2c08b --- /dev/null +++ b/pkg/coredata/third_party_category.go @@ -0,0 +1,255 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +import ( + "database/sql/driver" + "encoding/json" + "fmt" +) + +type ThirdPartyCategory string + +const ( + ThirdPartyCategoryAnalytics ThirdPartyCategory = "ANALYTICS" + ThirdPartyCategoryCloudMonitoring ThirdPartyCategory = "CLOUD_MONITORING" + ThirdPartyCategoryCloudProvider ThirdPartyCategory = "CLOUD_PROVIDER" + ThirdPartyCategoryCollaboration ThirdPartyCategory = "COLLABORATION" + ThirdPartyCategoryCustomerSupport ThirdPartyCategory = "CUSTOMER_SUPPORT" + ThirdPartyCategoryDataStorageAndProcessing ThirdPartyCategory = "DATA_STORAGE_AND_PROCESSING" + ThirdPartyCategoryDocumentManagement ThirdPartyCategory = "DOCUMENT_MANAGEMENT" + ThirdPartyCategoryEmployeeManagement ThirdPartyCategory = "EMPLOYEE_MANAGEMENT" + ThirdPartyCategoryEngineering ThirdPartyCategory = "ENGINEERING" + ThirdPartyCategoryFinance ThirdPartyCategory = "FINANCE" + ThirdPartyCategoryIdentityProvider ThirdPartyCategory = "IDENTITY_PROVIDER" + ThirdPartyCategoryIT ThirdPartyCategory = "IT" + ThirdPartyCategoryMarketing ThirdPartyCategory = "MARKETING" + ThirdPartyCategoryOfficeOperations ThirdPartyCategory = "OFFICE_OPERATIONS" + ThirdPartyCategoryOther ThirdPartyCategory = "OTHER" + ThirdPartyCategoryPasswordManagement ThirdPartyCategory = "PASSWORD_MANAGEMENT" + ThirdPartyCategoryProductAndDesign ThirdPartyCategory = "PRODUCT_AND_DESIGN" + ThirdPartyCategoryProfessionalServices ThirdPartyCategory = "PROFESSIONAL_SERVICES" + ThirdPartyCategoryRecruiting ThirdPartyCategory = "RECRUITING" + ThirdPartyCategorySales ThirdPartyCategory = "SALES" + ThirdPartyCategorySecurity ThirdPartyCategory = "SECURITY" + ThirdPartyCategoryVersionControl ThirdPartyCategory = "VERSION_CONTROL" +) + +func ThirdPartyCategories() []ThirdPartyCategory { + return []ThirdPartyCategory{ + ThirdPartyCategoryAnalytics, + ThirdPartyCategoryCloudMonitoring, + ThirdPartyCategoryCloudProvider, + ThirdPartyCategoryCollaboration, + ThirdPartyCategoryCustomerSupport, + ThirdPartyCategoryDataStorageAndProcessing, + ThirdPartyCategoryDocumentManagement, + ThirdPartyCategoryEmployeeManagement, + ThirdPartyCategoryEngineering, + ThirdPartyCategoryFinance, + ThirdPartyCategoryIdentityProvider, + ThirdPartyCategoryIT, + ThirdPartyCategoryMarketing, + ThirdPartyCategoryOfficeOperations, + ThirdPartyCategoryOther, + ThirdPartyCategoryPasswordManagement, + ThirdPartyCategoryProductAndDesign, + ThirdPartyCategoryProfessionalServices, + ThirdPartyCategoryRecruiting, + ThirdPartyCategorySales, + ThirdPartyCategorySecurity, + ThirdPartyCategoryVersionControl, + } +} + +func (i ThirdPartyCategory) String() string { + return string(i) +} + +func (i *ThirdPartyCategory) Scan(value any) error { + switch v := value.(type) { + case string: + switch v { + case "ANALYTICS": + *i = ThirdPartyCategoryAnalytics + case "CLOUD_MONITORING": + *i = ThirdPartyCategoryCloudMonitoring + case "CLOUD_PROVIDER": + *i = ThirdPartyCategoryCloudProvider + case "COLLABORATION": + *i = ThirdPartyCategoryCollaboration + case "CUSTOMER_SUPPORT": + *i = ThirdPartyCategoryCustomerSupport + case "DATA_STORAGE_AND_PROCESSING": + *i = ThirdPartyCategoryDataStorageAndProcessing + case "DOCUMENT_MANAGEMENT": + *i = ThirdPartyCategoryDocumentManagement + case "EMPLOYEE_MANAGEMENT": + *i = ThirdPartyCategoryEmployeeManagement + case "ENGINEERING": + *i = ThirdPartyCategoryEngineering + case "FINANCE": + *i = ThirdPartyCategoryFinance + case "IDENTITY_PROVIDER": + *i = ThirdPartyCategoryIdentityProvider + case "IT": + *i = ThirdPartyCategoryIT + case "MARKETING": + *i = ThirdPartyCategoryMarketing + case "OFFICE_OPERATIONS": + *i = ThirdPartyCategoryOfficeOperations + case "OTHER": + *i = ThirdPartyCategoryOther + case "PASSWORD_MANAGEMENT": + *i = ThirdPartyCategoryPasswordManagement + case "PRODUCT_AND_DESIGN": + *i = ThirdPartyCategoryProductAndDesign + case "PROFESSIONAL_SERVICES": + *i = ThirdPartyCategoryProfessionalServices + case "RECRUITING": + *i = ThirdPartyCategoryRecruiting + case "SALES": + *i = ThirdPartyCategorySales + case "SECURITY": + *i = ThirdPartyCategorySecurity + case "VERSION_CONTROL": + *i = ThirdPartyCategoryVersionControl + default: + return fmt.Errorf("invalid ThirdPartyCategory value: %q", v) + } + default: + return fmt.Errorf("unsupported type for ThirdPartyCategory: %T", value) + } + return nil +} + +func (i ThirdPartyCategory) Value() (driver.Value, error) { + return i.String(), nil +} + +func (i ThirdPartyCategory) MarshalJSON() ([]byte, error) { + return json.Marshal(i.String()) +} + +func (i *ThirdPartyCategory) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + + switch s { + case "ANALYTICS": + *i = ThirdPartyCategoryAnalytics + case "CLOUD_MONITORING": + *i = ThirdPartyCategoryCloudMonitoring + case "CLOUD_PROVIDER": + *i = ThirdPartyCategoryCloudProvider + case "COLLABORATION": + *i = ThirdPartyCategoryCollaboration + case "CUSTOMER_SUPPORT": + *i = ThirdPartyCategoryCustomerSupport + case "DATA_STORAGE_AND_PROCESSING": + *i = ThirdPartyCategoryDataStorageAndProcessing + case "DOCUMENT_MANAGEMENT": + *i = ThirdPartyCategoryDocumentManagement + case "EMPLOYEE_MANAGEMENT": + *i = ThirdPartyCategoryEmployeeManagement + case "ENGINEERING": + *i = ThirdPartyCategoryEngineering + case "FINANCE": + *i = ThirdPartyCategoryFinance + case "IDENTITY_PROVIDER": + *i = ThirdPartyCategoryIdentityProvider + case "IT": + *i = ThirdPartyCategoryIT + case "MARKETING": + *i = ThirdPartyCategoryMarketing + case "OFFICE_OPERATIONS": + *i = ThirdPartyCategoryOfficeOperations + case "OTHER": + *i = ThirdPartyCategoryOther + case "PASSWORD_MANAGEMENT": + *i = ThirdPartyCategoryPasswordManagement + case "PRODUCT_AND_DESIGN": + *i = ThirdPartyCategoryProductAndDesign + case "PROFESSIONAL_SERVICES": + *i = ThirdPartyCategoryProfessionalServices + case "RECRUITING": + *i = ThirdPartyCategoryRecruiting + case "SALES": + *i = ThirdPartyCategorySales + case "SECURITY": + *i = ThirdPartyCategorySecurity + case "VERSION_CONTROL": + *i = ThirdPartyCategoryVersionControl + default: + return fmt.Errorf("invalid ThirdPartyCategory value: %q", s) + } + return nil +} + +func (i *ThirdPartyCategory) UnmarshalText(text []byte) error { + s := string(text) + + switch s { + case "ANALYTICS": + *i = ThirdPartyCategoryAnalytics + case "CLOUD_MONITORING": + *i = ThirdPartyCategoryCloudMonitoring + case "CLOUD_PROVIDER": + *i = ThirdPartyCategoryCloudProvider + case "COLLABORATION": + *i = ThirdPartyCategoryCollaboration + case "CUSTOMER_SUPPORT": + *i = ThirdPartyCategoryCustomerSupport + case "DATA_STORAGE_AND_PROCESSING": + *i = ThirdPartyCategoryDataStorageAndProcessing + case "DOCUMENT_MANAGEMENT": + *i = ThirdPartyCategoryDocumentManagement + case "EMPLOYEE_MANAGEMENT": + *i = ThirdPartyCategoryEmployeeManagement + case "ENGINEERING": + *i = ThirdPartyCategoryEngineering + case "FINANCE": + *i = ThirdPartyCategoryFinance + case "IDENTITY_PROVIDER": + *i = ThirdPartyCategoryIdentityProvider + case "IT": + *i = ThirdPartyCategoryIT + case "MARKETING": + *i = ThirdPartyCategoryMarketing + case "OFFICE_OPERATIONS": + *i = ThirdPartyCategoryOfficeOperations + case "OTHER": + *i = ThirdPartyCategoryOther + case "PASSWORD_MANAGEMENT": + *i = ThirdPartyCategoryPasswordManagement + case "PRODUCT_AND_DESIGN": + *i = ThirdPartyCategoryProductAndDesign + case "PROFESSIONAL_SERVICES": + *i = ThirdPartyCategoryProfessionalServices + case "RECRUITING": + *i = ThirdPartyCategoryRecruiting + case "SALES": + *i = ThirdPartyCategorySales + case "SECURITY": + *i = ThirdPartyCategorySecurity + case "VERSION_CONTROL": + *i = ThirdPartyCategoryVersionControl + default: + return fmt.Errorf("invalid ThirdPartyCategory value: %q", s) + } + return nil +} diff --git a/pkg/coredata/vendor_compliance_report.go b/pkg/coredata/third_party_compliance_report.go similarity index 60% rename from pkg/coredata/vendor_compliance_report.go rename to pkg/coredata/third_party_compliance_report.go index cb69ea8e5..8c6930147 100644 --- a/pkg/coredata/vendor_compliance_report.go +++ b/pkg/coredata/third_party_compliance_report.go @@ -28,10 +28,10 @@ import ( ) type ( - VendorComplianceReport struct { + ThirdPartyComplianceReport struct { ID gid.GID `db:"id"` OrganizationID gid.GID `db:"organization_id"` - VendorID gid.GID `db:"vendor_id"` + ThirdPartyID gid.GID `db:"third_party_id"` ReportDate time.Time `db:"report_date"` ValidUntil *time.Time `db:"valid_until"` ReportName string `db:"report_name"` @@ -40,46 +40,46 @@ type ( UpdatedAt time.Time `db:"updated_at"` } - VendorComplianceReports []*VendorComplianceReport + ThirdPartyComplianceReports []*ThirdPartyComplianceReport ) -func (c VendorComplianceReport) CursorKey(orderBy VendorComplianceReportOrderField) page.CursorKey { +func (c ThirdPartyComplianceReport) CursorKey(orderBy ThirdPartyComplianceReportOrderField) page.CursorKey { switch orderBy { - case VendorComplianceReportOrderFieldReportDate: + case ThirdPartyComplianceReportOrderFieldReportDate: return page.NewCursorKey(c.ID, c.ReportDate) - case VendorComplianceReportOrderFieldCreatedAt: + case ThirdPartyComplianceReportOrderFieldCreatedAt: return page.NewCursorKey(c.ID, c.CreatedAt) } panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } -func (v *VendorComplianceReport) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { - q := `SELECT organization_id FROM vendor_compliance_reports WHERE id = $1 LIMIT 1;` +func (v *ThirdPartyComplianceReport) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { + q := `SELECT organization_id FROM third_party_compliance_reports WHERE id = $1 LIMIT 1;` var organizationID gid.GID if err := conn.QueryRow(ctx, q, v.ID).Scan(&organizationID); err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, ErrResourceNotFound } - return nil, fmt.Errorf("cannot query vendor compliance report authorization attributes: %w", err) + return nil, fmt.Errorf("cannot query thirdParty compliance report authorization attributes: %w", err) } return map[string]string{"organization_id": organizationID.String()}, nil } -func (vcs *VendorComplianceReports) LoadForVendorID( +func (vcs *ThirdPartyComplianceReports) LoadForThirdPartyID( ctx context.Context, conn pg.Querier, scope Scoper, - vendorID gid.GID, - cursor *page.Cursor[VendorComplianceReportOrderField], + thirdPartyID gid.GID, + cursor *page.Cursor[ThirdPartyComplianceReportOrderField], ) error { q := ` SELECT id, organization_id, - vendor_id, + third_party_id, report_date, valid_until, report_name, @@ -87,43 +87,43 @@ SELECT created_at, updated_at FROM - vendor_compliance_reports + third_party_compliance_reports WHERE %s - AND vendor_id = @vendor_id + AND third_party_id = @third_party_id AND snapshot_id IS NULL AND %s ` q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) - args := pgx.NamedArgs{"vendor_id": vendorID} + args := pgx.NamedArgs{"third_party_id": thirdPartyID} maps.Copy(args, scope.SQLArguments()) maps.Copy(args, cursor.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendor compliance reports: %w", err) + return fmt.Errorf("cannot query thirdParty compliance reports: %w", err) } - vendorComplianceReports, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[VendorComplianceReport]) + thirdPartyComplianceReports, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ThirdPartyComplianceReport]) if err != nil { - return fmt.Errorf("cannot collect vendor compliance reports: %w", err) + return fmt.Errorf("cannot collect thirdParty compliance reports: %w", err) } - *vcs = vendorComplianceReports + *vcs = thirdPartyComplianceReports return nil } -func (vcs *VendorComplianceReports) LoadByVendorIDs( +func (vcs *ThirdPartyComplianceReports) LoadByThirdPartyIDs( ctx context.Context, conn pg.Querier, scope Scoper, - vendorIDs []gid.GID, + thirdPartyIDs []gid.GID, ) error { - if len(vendorIDs) == 0 { - *vcs = VendorComplianceReports{} + if len(thirdPartyIDs) == 0 { + *vcs = ThirdPartyComplianceReports{} return nil } @@ -131,7 +131,7 @@ func (vcs *VendorComplianceReports) LoadByVendorIDs( SELECT id, organization_id, - vendor_id, + third_party_id, report_date, valid_until, report_name, @@ -139,51 +139,51 @@ SELECT created_at, updated_at FROM - vendor_compliance_reports + third_party_compliance_reports WHERE %s - AND vendor_id = ANY(@vendor_ids) + AND third_party_id = ANY(@third_party_ids) AND snapshot_id IS NULL ORDER BY - vendor_id, report_date DESC + third_party_id, report_date DESC ` q = fmt.Sprintf(q, scope.SQLFragment()) - ids := make([]string, len(vendorIDs)) - for i, id := range vendorIDs { + ids := make([]string, len(thirdPartyIDs)) + for i, id := range thirdPartyIDs { ids[i] = id.String() } - args := pgx.NamedArgs{"vendor_ids": ids} + args := pgx.NamedArgs{"third_party_ids": ids} maps.Copy(args, scope.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendor compliance reports: %w", err) + return fmt.Errorf("cannot query thirdParty compliance reports: %w", err) } - vendorComplianceReports, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[VendorComplianceReport]) + thirdPartyComplianceReports, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ThirdPartyComplianceReport]) if err != nil { - return fmt.Errorf("cannot collect vendor compliance reports: %w", err) + return fmt.Errorf("cannot collect thirdParty compliance reports: %w", err) } - *vcs = vendorComplianceReports + *vcs = thirdPartyComplianceReports return nil } -func (vcr *VendorComplianceReport) LoadByID( +func (vcr *ThirdPartyComplianceReport) LoadByID( ctx context.Context, conn pg.Querier, scope Scoper, - vendorComplianceReportID gid.GID, + thirdPartyComplianceReportID gid.GID, ) error { q := ` SELECT id, organization_id, - vendor_id, + third_party_id, report_date, valid_until, report_name, @@ -191,7 +191,7 @@ SELECT created_at, updated_at FROM - vendor_compliance_reports + third_party_compliance_reports WHERE %s AND id = @id @@ -200,36 +200,36 @@ LIMIT 1; q = fmt.Sprintf(q, scope.SQLFragment()) - args := pgx.NamedArgs{"id": vendorComplianceReportID} + args := pgx.NamedArgs{"id": thirdPartyComplianceReportID} maps.Copy(args, scope.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendor compliance report: %w", err) + return fmt.Errorf("cannot query thirdParty compliance report: %w", err) } - vendorComplianceReport, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[VendorComplianceReport]) + thirdPartyComplianceReport, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[ThirdPartyComplianceReport]) if err != nil { - return fmt.Errorf("cannot collect vendor compliance report: %w", err) + return fmt.Errorf("cannot collect thirdParty compliance report: %w", err) } - *vcr = vendorComplianceReport + *vcr = thirdPartyComplianceReport return nil } -func (vcr *VendorComplianceReport) Insert( +func (vcr *ThirdPartyComplianceReport) Insert( ctx context.Context, conn pg.Tx, scope Scoper, ) error { q := ` INSERT INTO - vendor_compliance_reports ( + third_party_compliance_reports ( id, organization_id, tenant_id, - vendor_id, + third_party_id, report_date, valid_until, report_name, @@ -241,7 +241,7 @@ VALUES ( @id, @organization_id, @tenant_id, - @vendor_id, + @third_party_id, @report_date, @valid_until, @report_name, @@ -254,7 +254,7 @@ VALUES ( "id": vcr.ID, "organization_id": vcr.OrganizationID, "tenant_id": scope.GetTenantID(), - "vendor_id": vcr.VendorID, + "third_party_id": vcr.ThirdPartyID, "report_date": vcr.ReportDate, "valid_until": vcr.ValidUntil, "report_name": vcr.ReportName, @@ -267,7 +267,7 @@ VALUES ( return err } -func (vcr *VendorComplianceReport) Delete( +func (vcr *ThirdPartyComplianceReport) Delete( ctx context.Context, conn pg.Tx, scope Scoper, @@ -275,7 +275,7 @@ func (vcr *VendorComplianceReport) Delete( q := ` DELETE FROM - vendor_compliance_reports + third_party_compliance_reports WHERE %s AND id = @id @@ -292,13 +292,13 @@ RETURNING report_file_id err := conn.QueryRow(ctx, q, args).Scan(&vcrFileId) if err != nil { - return fmt.Errorf("cannot delete vendor compliance report: %w", err) + return fmt.Errorf("cannot delete thirdParty compliance report: %w", err) } if vcrFileId != nil { file := &File{ID: *vcrFileId} if err = file.SoftDelete(ctx, conn, scope); err != nil { - return fmt.Errorf("cannot soft delete vendor compliance file: %w", err) + return fmt.Errorf("cannot soft delete thirdParty compliance file: %w", err) } } return nil diff --git a/pkg/coredata/vendor_data_privacy_agreement_order_field.go b/pkg/coredata/third_party_compliance_report_order_field.go similarity index 63% rename from pkg/coredata/vendor_data_privacy_agreement_order_field.go rename to pkg/coredata/third_party_compliance_report_order_field.go index a96a8e303..9a42929ca 100644 --- a/pkg/coredata/vendor_data_privacy_agreement_order_field.go +++ b/pkg/coredata/third_party_compliance_report_order_field.go @@ -15,27 +15,27 @@ package coredata type ( - VendorDataPrivacyAgreementOrderField string + ThirdPartyComplianceReportOrderField string ) const ( - VendorDataPrivacyAgreementOrderFieldValidFrom VendorDataPrivacyAgreementOrderField = "VALID_FROM" - VendorDataPrivacyAgreementOrderFieldCreatedAt VendorDataPrivacyAgreementOrderField = "CREATED_AT" + ThirdPartyComplianceReportOrderFieldReportDate ThirdPartyComplianceReportOrderField = "REPORT_DATE" + ThirdPartyComplianceReportOrderFieldCreatedAt ThirdPartyComplianceReportOrderField = "CREATED_AT" ) -func (p VendorDataPrivacyAgreementOrderField) Column() string { +func (p ThirdPartyComplianceReportOrderField) Column() string { return string(p) } -func (p VendorDataPrivacyAgreementOrderField) String() string { +func (p ThirdPartyComplianceReportOrderField) String() string { return string(p) } -func (p VendorDataPrivacyAgreementOrderField) MarshalText() ([]byte, error) { +func (p ThirdPartyComplianceReportOrderField) MarshalText() ([]byte, error) { return []byte(p.String()), nil } -func (p *VendorDataPrivacyAgreementOrderField) UnmarshalText(text []byte) error { - *p = VendorDataPrivacyAgreementOrderField(text) +func (p *ThirdPartyComplianceReportOrderField) UnmarshalText(text []byte) error { + *p = ThirdPartyComplianceReportOrderField(text) return nil } diff --git a/pkg/coredata/vendor_contact.go b/pkg/coredata/third_party_contact.go similarity index 56% rename from pkg/coredata/vendor_contact.go rename to pkg/coredata/third_party_contact.go index 96ee9f49f..ef5694c90 100644 --- a/pkg/coredata/vendor_contact.go +++ b/pkg/coredata/third_party_contact.go @@ -29,10 +29,10 @@ import ( ) type ( - VendorContact struct { + ThirdPartyContact struct { ID gid.GID `db:"id"` OrganizationID gid.GID `db:"organization_id"` - VendorID gid.GID `db:"vendor_id"` + ThirdPartyID gid.GID `db:"third_party_id"` FullName *string `db:"full_name"` Email *mail.Addr `db:"email"` Phone *string `db:"phone"` @@ -41,47 +41,47 @@ type ( UpdatedAt time.Time `db:"updated_at"` } - VendorContacts []*VendorContact + ThirdPartyContacts []*ThirdPartyContact ) -func (vc VendorContact) CursorKey(orderBy VendorContactOrderField) page.CursorKey { +func (vc ThirdPartyContact) CursorKey(orderBy ThirdPartyContactOrderField) page.CursorKey { switch orderBy { - case VendorContactOrderFieldCreatedAt: + case ThirdPartyContactOrderFieldCreatedAt: return page.CursorKey{ID: vc.ID, Value: vc.CreatedAt} - case VendorContactOrderFieldFullName: + case ThirdPartyContactOrderFieldFullName: return page.CursorKey{ID: vc.ID, Value: vc.FullName} - case VendorContactOrderFieldEmail: + case ThirdPartyContactOrderFieldEmail: return page.CursorKey{ID: vc.ID, Value: vc.Email} } panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } -func (vc *VendorContact) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { - q := `SELECT organization_id FROM vendor_contacts WHERE id = $1 LIMIT 1;` +func (vc *ThirdPartyContact) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { + q := `SELECT organization_id FROM third_party_contacts WHERE id = $1 LIMIT 1;` var organizationID gid.GID if err := conn.QueryRow(ctx, q, vc.ID).Scan(&organizationID); err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, ErrResourceNotFound } - return nil, fmt.Errorf("cannot query vendor contact authorization attributes: %w", err) + return nil, fmt.Errorf("cannot query thirdParty contact authorization attributes: %w", err) } return map[string]string{"organization_id": organizationID.String()}, nil } -func (vc *VendorContact) LoadByID( +func (vc *ThirdPartyContact) LoadByID( ctx context.Context, conn pg.Querier, scope Scoper, - vendorContactID gid.GID, + thirdPartyContactID gid.GID, ) error { q := ` SELECT id, organization_id, - vendor_id, + third_party_id, full_name, email, phone, @@ -89,50 +89,50 @@ SELECT created_at, updated_at FROM - vendor_contacts + third_party_contacts WHERE %s - AND id = @vendor_contact_id + AND id = @third_party_contact_id LIMIT 1; ` q = fmt.Sprintf(q, scope.SQLFragment()) - args := pgx.StrictNamedArgs{"vendor_contact_id": vendorContactID} + args := pgx.StrictNamedArgs{"third_party_contact_id": thirdPartyContactID} maps.Copy(args, scope.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendor contact: %w", err) + return fmt.Errorf("cannot query thirdParty contact: %w", err) } defer rows.Close() - vendorContact, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[VendorContact]) + thirdPartyContact, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[ThirdPartyContact]) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return ErrResourceNotFound } - return fmt.Errorf("cannot collect vendor contact: %w", err) + return fmt.Errorf("cannot collect thirdParty contact: %w", err) } - *vc = vendorContact + *vc = thirdPartyContact return nil } -func (vc *VendorContacts) LoadByVendorID( +func (vc *ThirdPartyContacts) LoadByThirdPartyID( ctx context.Context, conn pg.Querier, scope Scoper, - vendorID gid.GID, - cursor *page.Cursor[VendorContactOrderField], + thirdPartyID gid.GID, + cursor *page.Cursor[ThirdPartyContactOrderField], ) error { q := ` SELECT id, organization_id, - vendor_id, + third_party_id, full_name, email, phone, @@ -140,45 +140,45 @@ SELECT created_at, updated_at FROM - vendor_contacts + third_party_contacts WHERE %s - AND vendor_id = @vendor_id + AND third_party_id = @third_party_id AND snapshot_id IS NULL AND %s ` q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) args := pgx.StrictNamedArgs{ - "vendor_id": vendorID, + "third_party_id": thirdPartyID, } maps.Copy(args, scope.SQLArguments()) maps.Copy(args, cursor.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendor contacts: %w", err) + return fmt.Errorf("cannot query thirdParty contacts: %w", err) } defer rows.Close() - vendorContacts, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[VendorContact]) + thirdPartyContacts, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ThirdPartyContact]) if err != nil { - return fmt.Errorf("cannot collect vendor contacts: %w", err) + return fmt.Errorf("cannot collect thirdParty contacts: %w", err) } - *vc = vendorContacts + *vc = thirdPartyContacts return nil } -func (vc *VendorContacts) LoadByVendorIDs( +func (vc *ThirdPartyContacts) LoadByThirdPartyIDs( ctx context.Context, conn pg.Querier, scope Scoper, - vendorIDs []gid.GID, + thirdPartyIDs []gid.GID, ) error { - if len(vendorIDs) == 0 { - *vc = VendorContacts{} + if len(thirdPartyIDs) == 0 { + *vc = ThirdPartyContacts{} return nil } @@ -186,7 +186,7 @@ func (vc *VendorContacts) LoadByVendorIDs( SELECT id, organization_id, - vendor_id, + third_party_id, full_name, email, phone, @@ -194,52 +194,52 @@ SELECT created_at, updated_at FROM - vendor_contacts + third_party_contacts WHERE %s - AND vendor_id = ANY(@vendor_ids) + AND third_party_id = ANY(@third_party_ids) AND snapshot_id IS NULL ORDER BY - vendor_id, full_name ASC + third_party_id, full_name ASC ` q = fmt.Sprintf(q, scope.SQLFragment()) - ids := make([]string, len(vendorIDs)) - for i, id := range vendorIDs { + ids := make([]string, len(thirdPartyIDs)) + for i, id := range thirdPartyIDs { ids[i] = id.String() } - args := pgx.StrictNamedArgs{"vendor_ids": ids} + args := pgx.StrictNamedArgs{"third_party_ids": ids} maps.Copy(args, scope.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendor contacts: %w", err) + return fmt.Errorf("cannot query thirdParty contacts: %w", err) } defer rows.Close() - vendorContacts, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[VendorContact]) + thirdPartyContacts, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ThirdPartyContact]) if err != nil { - return fmt.Errorf("cannot collect vendor contacts: %w", err) + return fmt.Errorf("cannot collect thirdParty contacts: %w", err) } - *vc = vendorContacts + *vc = thirdPartyContacts return nil } -func (vc VendorContact) Insert( +func (vc ThirdPartyContact) Insert( ctx context.Context, conn pg.Tx, scope Scoper, ) error { q := ` INSERT INTO - vendor_contacts ( + third_party_contacts ( tenant_id, id, organization_id, - vendor_id, + third_party_id, full_name, email, phone, @@ -249,9 +249,9 @@ INSERT INTO ) VALUES ( @tenant_id, - @vendor_contact_id, + @third_party_contact_id, @organization_id, - @vendor_id, + @third_party_id, @full_name, @email, @phone, @@ -262,34 +262,34 @@ VALUES ( ` args := pgx.StrictNamedArgs{ - "tenant_id": scope.GetTenantID(), - "vendor_contact_id": vc.ID, - "organization_id": vc.OrganizationID, - "vendor_id": vc.VendorID, - "full_name": vc.FullName, - "email": vc.Email, - "phone": vc.Phone, - "role": vc.Role, - "created_at": vc.CreatedAt, - "updated_at": vc.UpdatedAt, + "tenant_id": scope.GetTenantID(), + "third_party_contact_id": vc.ID, + "organization_id": vc.OrganizationID, + "third_party_id": vc.ThirdPartyID, + "full_name": vc.FullName, + "email": vc.Email, + "phone": vc.Phone, + "role": vc.Role, + "created_at": vc.CreatedAt, + "updated_at": vc.UpdatedAt, } _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot insert vendor contact: %w", err) + return fmt.Errorf("cannot insert thirdParty contact: %w", err) } return nil } -func (vc VendorContact) Update( +func (vc ThirdPartyContact) Update( ctx context.Context, conn pg.Tx, scope Scoper, ) error { q := ` UPDATE - vendor_contacts + third_party_contacts SET full_name = @full_name, email = @email, @@ -298,52 +298,52 @@ SET updated_at = @updated_at WHERE %s - AND id = @vendor_contact_id + AND id = @third_party_contact_id AND snapshot_id IS NULL ` q = fmt.Sprintf(q, scope.SQLFragment()) args := pgx.StrictNamedArgs{ - "vendor_contact_id": vc.ID, - "full_name": vc.FullName, - "email": vc.Email, - "phone": vc.Phone, - "role": vc.Role, - "updated_at": vc.UpdatedAt, + "third_party_contact_id": vc.ID, + "full_name": vc.FullName, + "email": vc.Email, + "phone": vc.Phone, + "role": vc.Role, + "updated_at": vc.UpdatedAt, } maps.Copy(args, scope.SQLArguments()) _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot update vendor contact: %w", err) + return fmt.Errorf("cannot update thirdParty contact: %w", err) } return nil } -func (vc VendorContact) Delete( +func (vc ThirdPartyContact) Delete( ctx context.Context, conn pg.Tx, scope Scoper, ) error { q := ` DELETE FROM - vendor_contacts + third_party_contacts WHERE %s - AND id = @vendor_contact_id + AND id = @third_party_contact_id AND snapshot_id IS NULL ` q = fmt.Sprintf(q, scope.SQLFragment()) - args := pgx.StrictNamedArgs{"vendor_contact_id": vc.ID} + args := pgx.StrictNamedArgs{"third_party_contact_id": vc.ID} maps.Copy(args, scope.SQLArguments()) _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot delete vendor contact: %w", err) + return fmt.Errorf("cannot delete thirdParty contact: %w", err) } return nil diff --git a/pkg/coredata/vendor_compliance_report_order_field.go b/pkg/coredata/third_party_contact_order_field.go similarity index 62% rename from pkg/coredata/vendor_compliance_report_order_field.go rename to pkg/coredata/third_party_contact_order_field.go index a488b69ba..c1b800a3f 100644 --- a/pkg/coredata/vendor_compliance_report_order_field.go +++ b/pkg/coredata/third_party_contact_order_field.go @@ -15,27 +15,28 @@ package coredata type ( - VendorComplianceReportOrderField string + ThirdPartyContactOrderField string ) const ( - VendorComplianceReportOrderFieldReportDate VendorComplianceReportOrderField = "REPORT_DATE" - VendorComplianceReportOrderFieldCreatedAt VendorComplianceReportOrderField = "CREATED_AT" + ThirdPartyContactOrderFieldCreatedAt ThirdPartyContactOrderField = "CREATED_AT" + ThirdPartyContactOrderFieldFullName ThirdPartyContactOrderField = "FULL_NAME" + ThirdPartyContactOrderFieldEmail ThirdPartyContactOrderField = "EMAIL" ) -func (p VendorComplianceReportOrderField) Column() string { +func (p ThirdPartyContactOrderField) Column() string { return string(p) } -func (p VendorComplianceReportOrderField) String() string { +func (p ThirdPartyContactOrderField) String() string { return string(p) } -func (p VendorComplianceReportOrderField) MarshalText() ([]byte, error) { +func (p ThirdPartyContactOrderField) MarshalText() ([]byte, error) { return []byte(p.String()), nil } -func (p *VendorComplianceReportOrderField) UnmarshalText(text []byte) error { - *p = VendorComplianceReportOrderField(text) +func (p *ThirdPartyContactOrderField) UnmarshalText(text []byte) error { + *p = ThirdPartyContactOrderField(text) return nil } diff --git a/pkg/coredata/vendor_data_privacy_agreement.go b/pkg/coredata/third_party_data_privacy_agreement.go similarity index 60% rename from pkg/coredata/vendor_data_privacy_agreement.go rename to pkg/coredata/third_party_data_privacy_agreement.go index 648d4ff23..a8cdc4297 100644 --- a/pkg/coredata/vendor_data_privacy_agreement.go +++ b/pkg/coredata/third_party_data_privacy_agreement.go @@ -29,10 +29,10 @@ import ( ) type ( - VendorDataPrivacyAgreement struct { + ThirdPartyDataPrivacyAgreement struct { ID gid.GID `db:"id"` OrganizationID gid.GID `db:"organization_id"` - VendorID gid.GID `db:"vendor_id"` + ThirdPartyID gid.GID `db:"third_party_id"` ValidFrom *time.Time `db:"valid_from"` ValidUntil *time.Time `db:"valid_until"` FileID gid.GID `db:"file_id"` @@ -40,87 +40,87 @@ type ( UpdatedAt time.Time `db:"updated_at"` } - VendorDataPrivacyAgreements []*VendorDataPrivacyAgreement + ThirdPartyDataPrivacyAgreements []*ThirdPartyDataPrivacyAgreement ) -func (v VendorDataPrivacyAgreement) CursorKey(orderBy VendorDataPrivacyAgreementOrderField) page.CursorKey { +func (v ThirdPartyDataPrivacyAgreement) CursorKey(orderBy ThirdPartyDataPrivacyAgreementOrderField) page.CursorKey { switch orderBy { - case VendorDataPrivacyAgreementOrderFieldValidFrom: + case ThirdPartyDataPrivacyAgreementOrderFieldValidFrom: return page.NewCursorKey(v.ID, v.ValidFrom) - case VendorDataPrivacyAgreementOrderFieldCreatedAt: + case ThirdPartyDataPrivacyAgreementOrderFieldCreatedAt: return page.NewCursorKey(v.ID, v.CreatedAt) } panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } -func (vdpa *VendorDataPrivacyAgreement) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { - q := `SELECT organization_id FROM vendor_data_privacy_agreements WHERE id = $1 LIMIT 1;` +func (vdpa *ThirdPartyDataPrivacyAgreement) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { + q := `SELECT organization_id FROM third_party_data_privacy_agreements WHERE id = $1 LIMIT 1;` var organizationID gid.GID if err := conn.QueryRow(ctx, q, vdpa.ID).Scan(&organizationID); err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, ErrResourceNotFound } - return nil, fmt.Errorf("cannot query vendor data privacy agreement authorization attributes: %w", err) + return nil, fmt.Errorf("cannot query thirdParty data privacy agreement authorization attributes: %w", err) } return map[string]string{"organization_id": organizationID.String()}, nil } -func (vdpa *VendorDataPrivacyAgreement) LoadByVendorID( +func (vdpa *ThirdPartyDataPrivacyAgreement) LoadByThirdPartyID( ctx context.Context, conn pg.Querier, scope Scoper, - vendorID gid.GID, + thirdPartyID gid.GID, ) error { q := ` SELECT id, organization_id, - vendor_id, + third_party_id, valid_from, valid_until, file_id, created_at, updated_at FROM - vendor_data_privacy_agreements + third_party_data_privacy_agreements WHERE %s - AND vendor_id = @vendor_id + AND third_party_id = @third_party_id AND snapshot_id IS NULL LIMIT 1; ` q = fmt.Sprintf(q, scope.SQLFragment()) - args := pgx.NamedArgs{"vendor_id": vendorID} + args := pgx.NamedArgs{"third_party_id": thirdPartyID} maps.Copy(args, scope.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendor data privacy agreement: %w", err) + return fmt.Errorf("cannot query thirdParty data privacy agreement: %w", err) } - vendorDataPrivacyAgreement, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[VendorDataPrivacyAgreement]) + thirdPartyDataPrivacyAgreement, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[ThirdPartyDataPrivacyAgreement]) if err != nil { - return fmt.Errorf("cannot collect vendor data privacy agreement: %w", err) + return fmt.Errorf("cannot collect thirdParty data privacy agreement: %w", err) } - *vdpa = vendorDataPrivacyAgreement + *vdpa = thirdPartyDataPrivacyAgreement return nil } -func (vdpas *VendorDataPrivacyAgreements) LoadByVendorIDs( +func (vdpas *ThirdPartyDataPrivacyAgreements) LoadByThirdPartyIDs( ctx context.Context, conn pg.Querier, scope Scoper, - vendorIDs []gid.GID, + thirdPartyIDs []gid.GID, ) error { - if len(vendorIDs) == 0 { - *vdpas = VendorDataPrivacyAgreements{} + if len(thirdPartyIDs) == 0 { + *vdpas = ThirdPartyDataPrivacyAgreements{} return nil } @@ -128,38 +128,38 @@ func (vdpas *VendorDataPrivacyAgreements) LoadByVendorIDs( SELECT id, organization_id, - vendor_id, + third_party_id, valid_from, valid_until, file_id, created_at, updated_at FROM - vendor_data_privacy_agreements + third_party_data_privacy_agreements WHERE %s - AND vendor_id = ANY(@vendor_ids) + AND third_party_id = ANY(@third_party_ids) AND snapshot_id IS NULL ` q = fmt.Sprintf(q, scope.SQLFragment()) - ids := make([]string, len(vendorIDs)) - for i, id := range vendorIDs { + ids := make([]string, len(thirdPartyIDs)) + for i, id := range thirdPartyIDs { ids[i] = id.String() } - args := pgx.NamedArgs{"vendor_ids": ids} + args := pgx.NamedArgs{"third_party_ids": ids} maps.Copy(args, scope.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendor data privacy agreements: %w", err) + return fmt.Errorf("cannot query thirdParty data privacy agreements: %w", err) } - agreements, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[VendorDataPrivacyAgreement]) + agreements, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ThirdPartyDataPrivacyAgreement]) if err != nil { - return fmt.Errorf("cannot collect vendor data privacy agreements: %w", err) + return fmt.Errorf("cannot collect thirdParty data privacy agreements: %w", err) } *vdpas = agreements @@ -167,24 +167,24 @@ WHERE return nil } -func (vdpa *VendorDataPrivacyAgreement) LoadByID( +func (vdpa *ThirdPartyDataPrivacyAgreement) LoadByID( ctx context.Context, conn pg.Querier, scope Scoper, - vendorDataPrivacyAgreementID gid.GID, + thirdPartyDataPrivacyAgreementID gid.GID, ) error { q := ` SELECT id, organization_id, - vendor_id, + third_party_id, valid_from, valid_until, file_id, created_at, updated_at FROM - vendor_data_privacy_agreements + third_party_data_privacy_agreements WHERE %s AND id = @id @@ -193,32 +193,32 @@ LIMIT 1; q = fmt.Sprintf(q, scope.SQLFragment()) - args := pgx.NamedArgs{"id": vendorDataPrivacyAgreementID} + args := pgx.NamedArgs{"id": thirdPartyDataPrivacyAgreementID} maps.Copy(args, scope.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendor data privacy agreement: %w", err) + return fmt.Errorf("cannot query thirdParty data privacy agreement: %w", err) } - vendorDataPrivacyAgreement, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[VendorDataPrivacyAgreement]) + thirdPartyDataPrivacyAgreement, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[ThirdPartyDataPrivacyAgreement]) if err != nil { - return fmt.Errorf("cannot collect vendor data privacy agreement: %w", err) + return fmt.Errorf("cannot collect thirdParty data privacy agreement: %w", err) } - *vdpa = vendorDataPrivacyAgreement + *vdpa = thirdPartyDataPrivacyAgreement return nil } -func (vdpa *VendorDataPrivacyAgreement) Update( +func (vdpa *ThirdPartyDataPrivacyAgreement) Update( ctx context.Context, conn pg.Tx, scope Scoper, ) error { q := ` UPDATE - vendor_data_privacy_agreements + third_party_data_privacy_agreements SET valid_from = @valid_from, valid_until = @valid_until, @@ -243,24 +243,24 @@ WHERE _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot update vendor data privacy agreement: %w", err) + return fmt.Errorf("cannot update thirdParty data privacy agreement: %w", err) } return nil } -func (vdpa *VendorDataPrivacyAgreement) Upsert( +func (vdpa *ThirdPartyDataPrivacyAgreement) Upsert( ctx context.Context, conn pg.Querier, scope Scoper, ) error { q := ` INSERT INTO - vendor_data_privacy_agreements ( + third_party_data_privacy_agreements ( id, tenant_id, organization_id, - vendor_id, + third_party_id, valid_from, valid_until, file_id, @@ -271,14 +271,14 @@ VALUES ( @id, @tenant_id, @organization_id, - @vendor_id, + @third_party_id, @valid_from, @valid_until, @file_id, @created_at, @updated_at ) -ON CONFLICT (organization_id, vendor_id) DO UPDATE SET +ON CONFLICT (organization_id, third_party_id) DO UPDATE SET id = EXCLUDED.id, valid_from = EXCLUDED.valid_from, valid_until = EXCLUDED.valid_until, @@ -288,7 +288,7 @@ ON CONFLICT (organization_id, vendor_id) DO UPDATE SET args := pgx.StrictNamedArgs{ "id": vdpa.ID, "tenant_id": scope.GetTenantID(), - "vendor_id": vdpa.VendorID, + "third_party_id": vdpa.ThirdPartyID, "organization_id": vdpa.OrganizationID, "valid_from": vdpa.ValidFrom, "valid_until": vdpa.ValidUntil, @@ -301,16 +301,16 @@ ON CONFLICT (organization_id, vendor_id) DO UPDATE SET if err != nil { var pgErr *pgconn.PgError if errors.As(err, &pgErr) { - if pgErr.Code == "23505" && pgErr.ConstraintName == "vendor_data_privacy_agreements_source_id_snapshot_id_key" { + if pgErr.Code == "23505" && pgErr.ConstraintName == "third_party_data_privacy_agreements_source_id_snapshot_id_key" { return ErrResourceAlreadyExists } } - return fmt.Errorf("cannot upsert vendor data privacy agreement: %w", err) + return fmt.Errorf("cannot upsert thirdParty data privacy agreement: %w", err) } return nil } -func (vdpa *VendorDataPrivacyAgreement) Delete( +func (vdpa *ThirdPartyDataPrivacyAgreement) Delete( ctx context.Context, conn pg.Tx, scope Scoper, @@ -318,7 +318,7 @@ func (vdpa *VendorDataPrivacyAgreement) Delete( q := ` DELETE FROM - vendor_data_privacy_agreements + third_party_data_privacy_agreements WHERE %s AND id = @id @@ -334,24 +334,24 @@ WHERE return err } -func (vdpa *VendorDataPrivacyAgreement) DeleteByVendorID( +func (vdpa *ThirdPartyDataPrivacyAgreement) DeleteByThirdPartyID( ctx context.Context, conn pg.Tx, scope Scoper, - vendorID gid.GID, + thirdPartyID gid.GID, ) error { q := ` DELETE FROM - vendor_data_privacy_agreements + third_party_data_privacy_agreements WHERE %s - AND vendor_id = @vendor_id + AND third_party_id = @third_party_id ` q = fmt.Sprintf(q, scope.SQLFragment()) - args := pgx.StrictNamedArgs{"vendor_id": vendorID} + args := pgx.StrictNamedArgs{"third_party_id": thirdPartyID} maps.Copy(args, scope.SQLArguments()) _, err := conn.Exec(ctx, q, args) diff --git a/pkg/coredata/vendor_contact_order_field.go b/pkg/coredata/third_party_data_privacy_agreement_order_field.go similarity index 59% rename from pkg/coredata/vendor_contact_order_field.go rename to pkg/coredata/third_party_data_privacy_agreement_order_field.go index 23b7b1ff7..83edd0568 100644 --- a/pkg/coredata/vendor_contact_order_field.go +++ b/pkg/coredata/third_party_data_privacy_agreement_order_field.go @@ -15,28 +15,27 @@ package coredata type ( - VendorContactOrderField string + ThirdPartyDataPrivacyAgreementOrderField string ) const ( - VendorContactOrderFieldCreatedAt VendorContactOrderField = "CREATED_AT" - VendorContactOrderFieldFullName VendorContactOrderField = "FULL_NAME" - VendorContactOrderFieldEmail VendorContactOrderField = "EMAIL" + ThirdPartyDataPrivacyAgreementOrderFieldValidFrom ThirdPartyDataPrivacyAgreementOrderField = "VALID_FROM" + ThirdPartyDataPrivacyAgreementOrderFieldCreatedAt ThirdPartyDataPrivacyAgreementOrderField = "CREATED_AT" ) -func (p VendorContactOrderField) Column() string { +func (p ThirdPartyDataPrivacyAgreementOrderField) Column() string { return string(p) } -func (p VendorContactOrderField) String() string { +func (p ThirdPartyDataPrivacyAgreementOrderField) String() string { return string(p) } -func (p VendorContactOrderField) MarshalText() ([]byte, error) { +func (p ThirdPartyDataPrivacyAgreementOrderField) MarshalText() ([]byte, error) { return []byte(p.String()), nil } -func (p *VendorContactOrderField) UnmarshalText(text []byte) error { - *p = VendorContactOrderField(text) +func (p *ThirdPartyDataPrivacyAgreementOrderField) UnmarshalText(text []byte) error { + *p = ThirdPartyDataPrivacyAgreementOrderField(text) return nil } diff --git a/pkg/coredata/vendor_filter.go b/pkg/coredata/third_party_filter.go similarity index 84% rename from pkg/coredata/vendor_filter.go rename to pkg/coredata/third_party_filter.go index 0df713933..141c73d66 100644 --- a/pkg/coredata/vendor_filter.go +++ b/pkg/coredata/third_party_filter.go @@ -19,18 +19,18 @@ import ( ) type ( - VendorFilter struct { + ThirdPartyFilter struct { showOnTrustCenter *bool } ) -func NewVendorFilter(showOnTrustCenter *bool) *VendorFilter { - return &VendorFilter{ +func NewThirdPartyFilter(showOnTrustCenter *bool) *ThirdPartyFilter { + return &ThirdPartyFilter{ showOnTrustCenter: showOnTrustCenter, } } -func (f *VendorFilter) SQLArguments() pgx.StrictNamedArgs { +func (f *ThirdPartyFilter) SQLArguments() pgx.StrictNamedArgs { args := pgx.StrictNamedArgs{} if f.showOnTrustCenter != nil { @@ -42,7 +42,7 @@ func (f *VendorFilter) SQLArguments() pgx.StrictNamedArgs { return args } -func (f *VendorFilter) SQLFragment() string { +func (f *ThirdPartyFilter) SQLFragment() string { return ` ( CASE diff --git a/pkg/coredata/vendor_order_field.go b/pkg/coredata/third_party_order_field.go similarity index 65% rename from pkg/coredata/vendor_order_field.go rename to pkg/coredata/third_party_order_field.go index 2d72e88a8..e131dace1 100644 --- a/pkg/coredata/vendor_order_field.go +++ b/pkg/coredata/third_party_order_field.go @@ -15,28 +15,28 @@ package coredata type ( - VendorOrderField string + ThirdPartyOrderField string ) const ( - VendorOrderFieldCreatedAt VendorOrderField = "CREATED_AT" - VendorOrderFieldUpdatedAt VendorOrderField = "UPDATED_AT" - VendorOrderFieldName VendorOrderField = "NAME" + ThirdPartyOrderFieldCreatedAt ThirdPartyOrderField = "CREATED_AT" + ThirdPartyOrderFieldUpdatedAt ThirdPartyOrderField = "UPDATED_AT" + ThirdPartyOrderFieldName ThirdPartyOrderField = "NAME" ) -func (p VendorOrderField) Column() string { +func (p ThirdPartyOrderField) Column() string { return string(p) } -func (p VendorOrderField) String() string { +func (p ThirdPartyOrderField) String() string { return string(p) } -func (p VendorOrderField) MarshalText() ([]byte, error) { +func (p ThirdPartyOrderField) MarshalText() ([]byte, error) { return []byte(p.String()), nil } -func (p *VendorOrderField) UnmarshalText(text []byte) error { - *p = VendorOrderField(text) +func (p *ThirdPartyOrderField) UnmarshalText(text []byte) error { + *p = ThirdPartyOrderField(text) return nil } diff --git a/pkg/coredata/vendor_risk_assessment.go b/pkg/coredata/third_party_risk_assessment.go similarity index 72% rename from pkg/coredata/vendor_risk_assessment.go rename to pkg/coredata/third_party_risk_assessment.go index 64b06702c..819fe3e58 100644 --- a/pkg/coredata/vendor_risk_assessment.go +++ b/pkg/coredata/third_party_risk_assessment.go @@ -28,11 +28,11 @@ import ( ) type ( - // RiskAssessment represents a point-in-time risk assessment for a vendor - VendorRiskAssessment struct { + // RiskAssessment represents a point-in-time risk assessment for a thirdParty + ThirdPartyRiskAssessment struct { ID gid.GID `db:"id"` OrganizationID gid.GID `db:"organization_id"` - VendorID gid.GID `db:"vendor_id"` + ThirdPartyID gid.GID `db:"third_party_id"` ExpiresAt time.Time `db:"expires_at"` DataSensitivity DataSensitivity `db:"data_sensitivity"` BusinessImpact BusinessImpact `db:"business_impact"` @@ -41,47 +41,47 @@ type ( UpdatedAt time.Time `db:"updated_at"` } - VendorRiskAssessments []*VendorRiskAssessment + ThirdPartyRiskAssessments []*ThirdPartyRiskAssessment ) -func (v VendorRiskAssessment) CursorKey(orderBy VendorRiskAssessmentOrderField) page.CursorKey { +func (v ThirdPartyRiskAssessment) CursorKey(orderBy ThirdPartyRiskAssessmentOrderField) page.CursorKey { switch orderBy { - case VendorRiskAssessmentOrderFieldCreatedAt: + case ThirdPartyRiskAssessmentOrderFieldCreatedAt: return page.NewCursorKey(v.ID, v.CreatedAt) - case VendorRiskAssessmentOrderFieldExpiresAt: + case ThirdPartyRiskAssessmentOrderFieldExpiresAt: return page.NewCursorKey(v.ID, v.ExpiresAt) } panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } -func (v *VendorRiskAssessment) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { - q := `SELECT organization_id FROM vendor_risk_assessments WHERE id = $1 LIMIT 1;` +func (v *ThirdPartyRiskAssessment) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { + q := `SELECT organization_id FROM third_party_risk_assessments WHERE id = $1 LIMIT 1;` var organizationID gid.GID if err := conn.QueryRow(ctx, q, v.ID).Scan(&organizationID); err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, ErrResourceNotFound } - return nil, fmt.Errorf("cannot query vendor risk assessment authorization attributes: %w", err) + return nil, fmt.Errorf("cannot query thirdParty risk assessment authorization attributes: %w", err) } return map[string]string{"organization_id": organizationID.String()}, nil } // Insert adds a new risk assessment to the database -func (r VendorRiskAssessment) Insert( +func (r ThirdPartyRiskAssessment) Insert( ctx context.Context, conn pg.Tx, scope Scoper, ) error { q := ` INSERT INTO - vendor_risk_assessments ( + third_party_risk_assessments ( tenant_id, id, organization_id, - vendor_id, + third_party_id, expires_at, data_sensitivity, business_impact, @@ -93,7 +93,7 @@ VALUES ( @tenant_id, @id, @organization_id, - @vendor_id, + @third_party_id, @expires_at, @data_sensitivity, @business_impact, @@ -107,7 +107,7 @@ VALUES ( "tenant_id": scope.GetTenantID(), "id": r.ID, "organization_id": r.OrganizationID, - "vendor_id": r.VendorID, + "third_party_id": r.ThirdPartyID, "expires_at": r.ExpiresAt, "data_sensitivity": r.DataSensitivity, "business_impact": r.BusinessImpact, @@ -120,7 +120,7 @@ VALUES ( } // LoadByID loads a risk assessment by its ID -func (r *VendorRiskAssessment) LoadByID( +func (r *ThirdPartyRiskAssessment) LoadByID( ctx context.Context, conn pg.Querier, scope Scoper, @@ -130,7 +130,7 @@ func (r *VendorRiskAssessment) LoadByID( SELECT id, organization_id, - vendor_id, + third_party_id, expires_at, data_sensitivity, business_impact, @@ -138,7 +138,7 @@ SELECT created_at, updated_at FROM - vendor_risk_assessments + third_party_risk_assessments WHERE %s AND id = @id @@ -156,7 +156,7 @@ LIMIT 1; } defer rows.Close() - assessment, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[VendorRiskAssessment]) + assessment, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[ThirdPartyRiskAssessment]) if err != nil { return fmt.Errorf("cannot collect risk assessment: %w", err) } @@ -166,18 +166,18 @@ LIMIT 1; return nil } -// LoadLatestByVendorID loads the most recent risk assessment for a vendor -func (r *VendorRiskAssessment) LoadLatestByVendorID( +// LoadLatestByThirdPartyID loads the most recent risk assessment for a thirdParty +func (r *ThirdPartyRiskAssessment) LoadLatestByThirdPartyID( ctx context.Context, conn pg.Querier, scope Scoper, - vendorID gid.GID, + thirdPartyID gid.GID, ) error { q := ` SELECT id, organization_id, - vendor_id, + third_party_id, expires_at, data_sensitivity, business_impact, @@ -185,10 +185,10 @@ SELECT created_at, updated_at FROM - vendor_risk_assessments + third_party_risk_assessments WHERE %s - AND vendor_id = @vendor_id + AND third_party_id = @third_party_id AND snapshot_id IS NULL ORDER BY created_at DESC @@ -197,7 +197,7 @@ LIMIT 1; q = fmt.Sprintf(q, scope.SQLFragment()) - args := pgx.StrictNamedArgs{"vendor_id": vendorID} + args := pgx.StrictNamedArgs{"third_party_id": thirdPartyID} maps.Copy(args, scope.SQLArguments()) rows, err := conn.Query(ctx, q, args) @@ -206,7 +206,7 @@ LIMIT 1; } defer rows.Close() - assessment, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[VendorRiskAssessment]) + assessment, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[ThirdPartyRiskAssessment]) if err != nil { return fmt.Errorf("cannot collect risk assessment: %w", err) } @@ -216,19 +216,19 @@ LIMIT 1; return nil } -// LoadByVendorID loads all risk assessments for a vendor, ordered by assessment date -func (r *VendorRiskAssessments) LoadByVendorID( +// LoadByThirdPartyID loads all risk assessments for a thirdParty, ordered by assessment date +func (r *ThirdPartyRiskAssessments) LoadByThirdPartyID( ctx context.Context, conn pg.Querier, scope Scoper, - vendorID gid.GID, - cursor *page.Cursor[VendorRiskAssessmentOrderField], + thirdPartyID gid.GID, + cursor *page.Cursor[ThirdPartyRiskAssessmentOrderField], ) error { q := ` SELECT id, organization_id, - vendor_id, + third_party_id, expires_at, data_sensitivity, business_impact, @@ -236,17 +236,17 @@ SELECT created_at, updated_at FROM - vendor_risk_assessments + third_party_risk_assessments WHERE %s - AND vendor_id = @vendor_id + AND third_party_id = @third_party_id AND snapshot_id IS NULL AND %s ` q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) - args := pgx.StrictNamedArgs{"vendor_id": vendorID} + args := pgx.StrictNamedArgs{"third_party_id": thirdPartyID} maps.Copy(args, scope.SQLArguments()) maps.Copy(args, cursor.SQLArguments()) @@ -255,7 +255,7 @@ WHERE return fmt.Errorf("cannot query risk assessments: %w", err) } - assessments, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[VendorRiskAssessment]) + assessments, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ThirdPartyRiskAssessment]) if err != nil { return fmt.Errorf("cannot collect risk assessments: %w", err) } @@ -265,14 +265,14 @@ WHERE return nil } -func (r *VendorRiskAssessments) LoadByVendorIDs( +func (r *ThirdPartyRiskAssessments) LoadByThirdPartyIDs( ctx context.Context, conn pg.Querier, scope Scoper, - vendorIDs []gid.GID, + thirdPartyIDs []gid.GID, ) error { - if len(vendorIDs) == 0 { - *r = VendorRiskAssessments{} + if len(thirdPartyIDs) == 0 { + *r = ThirdPartyRiskAssessments{} return nil } @@ -280,7 +280,7 @@ func (r *VendorRiskAssessments) LoadByVendorIDs( SELECT id, organization_id, - vendor_id, + third_party_id, expires_at, data_sensitivity, business_impact, @@ -288,23 +288,23 @@ SELECT created_at, updated_at FROM - vendor_risk_assessments + third_party_risk_assessments WHERE %s - AND vendor_id = ANY(@vendor_ids) + AND third_party_id = ANY(@third_party_ids) AND snapshot_id IS NULL ORDER BY - vendor_id, created_at DESC + third_party_id, created_at DESC ` q = fmt.Sprintf(q, scope.SQLFragment()) - ids := make([]string, len(vendorIDs)) - for i, id := range vendorIDs { + ids := make([]string, len(thirdPartyIDs)) + for i, id := range thirdPartyIDs { ids[i] = id.String() } - args := pgx.StrictNamedArgs{"vendor_ids": ids} + args := pgx.StrictNamedArgs{"third_party_ids": ids} maps.Copy(args, scope.SQLArguments()) rows, err := conn.Query(ctx, q, args) @@ -312,7 +312,7 @@ ORDER BY return fmt.Errorf("cannot query risk assessments: %w", err) } - assessments, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[VendorRiskAssessment]) + assessments, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ThirdPartyRiskAssessment]) if err != nil { return fmt.Errorf("cannot collect risk assessments: %w", err) } diff --git a/pkg/coredata/vendor_risk_assessment_order_field.go b/pkg/coredata/third_party_risk_assessment_order_field.go similarity index 62% rename from pkg/coredata/vendor_risk_assessment_order_field.go rename to pkg/coredata/third_party_risk_assessment_order_field.go index cea140b90..6cb853fdc 100644 --- a/pkg/coredata/vendor_risk_assessment_order_field.go +++ b/pkg/coredata/third_party_risk_assessment_order_field.go @@ -15,27 +15,27 @@ package coredata type ( - VendorRiskAssessmentOrderField string + ThirdPartyRiskAssessmentOrderField string ) const ( - VendorRiskAssessmentOrderFieldCreatedAt VendorRiskAssessmentOrderField = "CREATED_AT" - VendorRiskAssessmentOrderFieldExpiresAt VendorRiskAssessmentOrderField = "EXPIRES_AT" + ThirdPartyRiskAssessmentOrderFieldCreatedAt ThirdPartyRiskAssessmentOrderField = "CREATED_AT" + ThirdPartyRiskAssessmentOrderFieldExpiresAt ThirdPartyRiskAssessmentOrderField = "EXPIRES_AT" ) -func (p VendorRiskAssessmentOrderField) Column() string { +func (p ThirdPartyRiskAssessmentOrderField) Column() string { return string(p) } -func (p VendorRiskAssessmentOrderField) String() string { +func (p ThirdPartyRiskAssessmentOrderField) String() string { return string(p) } -func (p VendorRiskAssessmentOrderField) MarshalText() ([]byte, error) { +func (p ThirdPartyRiskAssessmentOrderField) MarshalText() ([]byte, error) { return []byte(p.String()), nil } -func (p *VendorRiskAssessmentOrderField) UnmarshalText(text []byte) error { - *p = VendorRiskAssessmentOrderField(text) +func (p *ThirdPartyRiskAssessmentOrderField) UnmarshalText(text []byte) error { + *p = ThirdPartyRiskAssessmentOrderField(text) return nil } diff --git a/pkg/coredata/vendor_service.go b/pkg/coredata/third_party_service.go similarity index 55% rename from pkg/coredata/vendor_service.go rename to pkg/coredata/third_party_service.go index 79170d135..362df8086 100644 --- a/pkg/coredata/vendor_service.go +++ b/pkg/coredata/third_party_service.go @@ -28,148 +28,148 @@ import ( ) type ( - VendorService struct { + ThirdPartyService struct { ID gid.GID `db:"id"` OrganizationID gid.GID `db:"organization_id"` - VendorID gid.GID `db:"vendor_id"` + ThirdPartyID gid.GID `db:"third_party_id"` Name string `db:"name"` Description *string `db:"description"` CreatedAt time.Time `db:"created_at"` UpdatedAt time.Time `db:"updated_at"` } - VendorServices []*VendorService + ThirdPartyServices []*ThirdPartyService ) -func (vs VendorService) CursorKey(orderBy VendorServiceOrderField) page.CursorKey { +func (vs ThirdPartyService) CursorKey(orderBy ThirdPartyServiceOrderField) page.CursorKey { switch orderBy { - case VendorServiceOrderFieldCreatedAt: + case ThirdPartyServiceOrderFieldCreatedAt: return page.CursorKey{ID: vs.ID, Value: vs.CreatedAt} - case VendorServiceOrderFieldName: + case ThirdPartyServiceOrderFieldName: return page.CursorKey{ID: vs.ID, Value: vs.Name} } panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } -func (vs *VendorService) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { - q := `SELECT organization_id FROM vendor_services WHERE id = $1 LIMIT 1;` +func (vs *ThirdPartyService) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { + q := `SELECT organization_id FROM third_party_services WHERE id = $1 LIMIT 1;` var organizationID gid.GID if err := conn.QueryRow(ctx, q, vs.ID).Scan(&organizationID); err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, ErrResourceNotFound } - return nil, fmt.Errorf("cannot query vendor service authorization attributes: %w", err) + return nil, fmt.Errorf("cannot query thirdParty service authorization attributes: %w", err) } return map[string]string{"organization_id": organizationID.String()}, nil } -func (vs *VendorService) LoadByID( +func (vs *ThirdPartyService) LoadByID( ctx context.Context, conn pg.Querier, scope Scoper, - vendorServiceID gid.GID, + thirdPartyServiceID gid.GID, ) error { q := ` SELECT id, organization_id, - vendor_id, + third_party_id, name, description, created_at, updated_at FROM - vendor_services + third_party_services WHERE %s - AND id = @vendor_service_id + AND id = @third_party_service_id LIMIT 1; ` q = fmt.Sprintf(q, scope.SQLFragment()) - args := pgx.StrictNamedArgs{"vendor_service_id": vendorServiceID} + args := pgx.StrictNamedArgs{"third_party_service_id": thirdPartyServiceID} maps.Copy(args, scope.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendor service: %w", err) + return fmt.Errorf("cannot query thirdParty service: %w", err) } defer rows.Close() - vendorService, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[VendorService]) + thirdPartyService, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[ThirdPartyService]) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return ErrResourceNotFound } - return fmt.Errorf("cannot collect vendor service: %w", err) + return fmt.Errorf("cannot collect thirdParty service: %w", err) } - *vs = vendorService + *vs = thirdPartyService return nil } -func (vs *VendorServices) LoadByVendorID( +func (vs *ThirdPartyServices) LoadByThirdPartyID( ctx context.Context, conn pg.Querier, scope Scoper, - vendorID gid.GID, - cursor *page.Cursor[VendorServiceOrderField], + thirdPartyID gid.GID, + cursor *page.Cursor[ThirdPartyServiceOrderField], ) error { q := ` SELECT id, organization_id, - vendor_id, + third_party_id, name, description, created_at, updated_at FROM - vendor_services + third_party_services WHERE %s - AND vendor_id = @vendor_id + AND third_party_id = @third_party_id AND snapshot_id IS NULL AND %s ` q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) args := pgx.StrictNamedArgs{ - "vendor_id": vendorID, + "third_party_id": thirdPartyID, } maps.Copy(args, scope.SQLArguments()) maps.Copy(args, cursor.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendor services: %w", err) + return fmt.Errorf("cannot query thirdParty services: %w", err) } defer rows.Close() - vendorServices, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[VendorService]) + thirdPartyServices, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ThirdPartyService]) if err != nil { - return fmt.Errorf("cannot collect vendor services: %w", err) + return fmt.Errorf("cannot collect thirdParty services: %w", err) } - *vs = vendorServices + *vs = thirdPartyServices return nil } -func (vs *VendorServices) LoadByVendorIDs( +func (vs *ThirdPartyServices) LoadByThirdPartyIDs( ctx context.Context, conn pg.Querier, scope Scoper, - vendorIDs []gid.GID, + thirdPartyIDs []gid.GID, ) error { - if len(vendorIDs) == 0 { - *vs = VendorServices{} + if len(thirdPartyIDs) == 0 { + *vs = ThirdPartyServices{} return nil } @@ -177,58 +177,58 @@ func (vs *VendorServices) LoadByVendorIDs( SELECT id, organization_id, - vendor_id, + third_party_id, name, description, created_at, updated_at FROM - vendor_services + third_party_services WHERE %s - AND vendor_id = ANY(@vendor_ids) + AND third_party_id = ANY(@third_party_ids) AND snapshot_id IS NULL ORDER BY - vendor_id, name ASC + third_party_id, name ASC ` q = fmt.Sprintf(q, scope.SQLFragment()) - ids := make([]string, len(vendorIDs)) - for i, id := range vendorIDs { + ids := make([]string, len(thirdPartyIDs)) + for i, id := range thirdPartyIDs { ids[i] = id.String() } - args := pgx.StrictNamedArgs{"vendor_ids": ids} + args := pgx.StrictNamedArgs{"third_party_ids": ids} maps.Copy(args, scope.SQLArguments()) rows, err := conn.Query(ctx, q, args) if err != nil { - return fmt.Errorf("cannot query vendor services: %w", err) + return fmt.Errorf("cannot query thirdParty services: %w", err) } defer rows.Close() - vendorServices, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[VendorService]) + thirdPartyServices, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ThirdPartyService]) if err != nil { - return fmt.Errorf("cannot collect vendor services: %w", err) + return fmt.Errorf("cannot collect thirdParty services: %w", err) } - *vs = vendorServices + *vs = thirdPartyServices return nil } -func (vs VendorService) Insert( +func (vs ThirdPartyService) Insert( ctx context.Context, conn pg.Tx, scope Scoper, ) error { q := ` INSERT INTO - vendor_services ( + third_party_services ( tenant_id, id, organization_id, - vendor_id, + third_party_id, name, description, created_at, @@ -236,9 +236,9 @@ INSERT INTO ) VALUES ( @tenant_id, - @vendor_service_id, + @third_party_service_id, @organization_id, - @vendor_id, + @third_party_id, @name, @description, @created_at, @@ -247,82 +247,82 @@ VALUES ( ` args := pgx.StrictNamedArgs{ - "tenant_id": scope.GetTenantID(), - "vendor_service_id": vs.ID, - "organization_id": vs.OrganizationID, - "vendor_id": vs.VendorID, - "name": vs.Name, - "description": vs.Description, - "created_at": vs.CreatedAt, - "updated_at": vs.UpdatedAt, + "tenant_id": scope.GetTenantID(), + "third_party_service_id": vs.ID, + "organization_id": vs.OrganizationID, + "third_party_id": vs.ThirdPartyID, + "name": vs.Name, + "description": vs.Description, + "created_at": vs.CreatedAt, + "updated_at": vs.UpdatedAt, } _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot insert vendor service: %w", err) + return fmt.Errorf("cannot insert thirdParty service: %w", err) } return nil } -func (vs VendorService) Update( +func (vs ThirdPartyService) Update( ctx context.Context, conn pg.Tx, scope Scoper, ) error { q := ` UPDATE - vendor_services + third_party_services SET name = @name, description = @description, updated_at = @updated_at WHERE %s - AND id = @vendor_service_id + AND id = @third_party_service_id AND snapshot_id IS NULL ` q = fmt.Sprintf(q, scope.SQLFragment()) args := pgx.StrictNamedArgs{ - "vendor_service_id": vs.ID, - "name": vs.Name, - "description": vs.Description, - "updated_at": vs.UpdatedAt, + "third_party_service_id": vs.ID, + "name": vs.Name, + "description": vs.Description, + "updated_at": vs.UpdatedAt, } maps.Copy(args, scope.SQLArguments()) _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot update vendor service: %w", err) + return fmt.Errorf("cannot update thirdParty service: %w", err) } return nil } -func (vs VendorService) Delete( +func (vs ThirdPartyService) Delete( ctx context.Context, conn pg.Tx, scope Scoper, ) error { q := ` DELETE FROM - vendor_services + third_party_services WHERE %s - AND id = @vendor_service_id + AND id = @third_party_service_id AND snapshot_id IS NULL ` q = fmt.Sprintf(q, scope.SQLFragment()) - args := pgx.StrictNamedArgs{"vendor_service_id": vs.ID} + args := pgx.StrictNamedArgs{"third_party_service_id": vs.ID} maps.Copy(args, scope.SQLArguments()) _, err := conn.Exec(ctx, q, args) if err != nil { - return fmt.Errorf("cannot delete vendor service: %w", err) + return fmt.Errorf("cannot delete thirdParty service: %w", err) } return nil diff --git a/pkg/coredata/vendor_service_order_field.go b/pkg/coredata/third_party_service_order_field.go similarity index 60% rename from pkg/coredata/vendor_service_order_field.go rename to pkg/coredata/third_party_service_order_field.go index 2e21e55c6..a32f50019 100644 --- a/pkg/coredata/vendor_service_order_field.go +++ b/pkg/coredata/third_party_service_order_field.go @@ -19,33 +19,33 @@ import ( ) type ( - VendorServiceOrderField string + ThirdPartyServiceOrderField string ) const ( - VendorServiceOrderFieldCreatedAt VendorServiceOrderField = "CREATED_AT" - VendorServiceOrderFieldName VendorServiceOrderField = "NAME" + ThirdPartyServiceOrderFieldCreatedAt ThirdPartyServiceOrderField = "CREATED_AT" + ThirdPartyServiceOrderFieldName ThirdPartyServiceOrderField = "NAME" ) -func (p VendorServiceOrderField) Column() string { +func (p ThirdPartyServiceOrderField) Column() string { return string(p) } -func (p VendorServiceOrderField) String() string { +func (p ThirdPartyServiceOrderField) String() string { return string(p) } -func (p VendorServiceOrderField) MarshalText() ([]byte, error) { +func (p ThirdPartyServiceOrderField) MarshalText() ([]byte, error) { return []byte(p.String()), nil } -func (p *VendorServiceOrderField) UnmarshalText(text []byte) error { +func (p *ThirdPartyServiceOrderField) UnmarshalText(text []byte) error { val := string(text) switch val { - case string(VendorServiceOrderFieldCreatedAt), - string(VendorServiceOrderFieldName): - *p = VendorServiceOrderField(val) + case string(ThirdPartyServiceOrderFieldCreatedAt), + string(ThirdPartyServiceOrderFieldName): + *p = ThirdPartyServiceOrderField(val) return nil } - return fmt.Errorf("invalid VendorServiceOrderField value: %q", val) + return fmt.Errorf("invalid ThirdPartyServiceOrderField value: %q", val) } diff --git a/pkg/coredata/vendor_category.go b/pkg/coredata/vendor_category.go deleted file mode 100644 index 7c56015f1..000000000 --- a/pkg/coredata/vendor_category.go +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (c) 2025-2026 Probo Inc . -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -package coredata - -import ( - "database/sql/driver" - "encoding/json" - "fmt" -) - -type VendorCategory string - -const ( - VendorCategoryAnalytics VendorCategory = "ANALYTICS" - VendorCategoryCloudMonitoring VendorCategory = "CLOUD_MONITORING" - VendorCategoryCloudProvider VendorCategory = "CLOUD_PROVIDER" - VendorCategoryCollaboration VendorCategory = "COLLABORATION" - VendorCategoryCustomerSupport VendorCategory = "CUSTOMER_SUPPORT" - VendorCategoryDataStorageAndProcessing VendorCategory = "DATA_STORAGE_AND_PROCESSING" - VendorCategoryDocumentManagement VendorCategory = "DOCUMENT_MANAGEMENT" - VendorCategoryEmployeeManagement VendorCategory = "EMPLOYEE_MANAGEMENT" - VendorCategoryEngineering VendorCategory = "ENGINEERING" - VendorCategoryFinance VendorCategory = "FINANCE" - VendorCategoryIdentityProvider VendorCategory = "IDENTITY_PROVIDER" - VendorCategoryIT VendorCategory = "IT" - VendorCategoryMarketing VendorCategory = "MARKETING" - VendorCategoryOfficeOperations VendorCategory = "OFFICE_OPERATIONS" - VendorCategoryOther VendorCategory = "OTHER" - VendorCategoryPasswordManagement VendorCategory = "PASSWORD_MANAGEMENT" - VendorCategoryProductAndDesign VendorCategory = "PRODUCT_AND_DESIGN" - VendorCategoryProfessionalServices VendorCategory = "PROFESSIONAL_SERVICES" - VendorCategoryRecruiting VendorCategory = "RECRUITING" - VendorCategorySales VendorCategory = "SALES" - VendorCategorySecurity VendorCategory = "SECURITY" - VendorCategoryVersionControl VendorCategory = "VERSION_CONTROL" -) - -func VendorCategories() []VendorCategory { - return []VendorCategory{ - VendorCategoryAnalytics, - VendorCategoryCloudMonitoring, - VendorCategoryCloudProvider, - VendorCategoryCollaboration, - VendorCategoryCustomerSupport, - VendorCategoryDataStorageAndProcessing, - VendorCategoryDocumentManagement, - VendorCategoryEmployeeManagement, - VendorCategoryEngineering, - VendorCategoryFinance, - VendorCategoryIdentityProvider, - VendorCategoryIT, - VendorCategoryMarketing, - VendorCategoryOfficeOperations, - VendorCategoryOther, - VendorCategoryPasswordManagement, - VendorCategoryProductAndDesign, - VendorCategoryProfessionalServices, - VendorCategoryRecruiting, - VendorCategorySales, - VendorCategorySecurity, - VendorCategoryVersionControl, - } -} - -func (i VendorCategory) String() string { - return string(i) -} - -func (i *VendorCategory) Scan(value any) error { - switch v := value.(type) { - case string: - switch v { - case "ANALYTICS": - *i = VendorCategoryAnalytics - case "CLOUD_MONITORING": - *i = VendorCategoryCloudMonitoring - case "CLOUD_PROVIDER": - *i = VendorCategoryCloudProvider - case "COLLABORATION": - *i = VendorCategoryCollaboration - case "CUSTOMER_SUPPORT": - *i = VendorCategoryCustomerSupport - case "DATA_STORAGE_AND_PROCESSING": - *i = VendorCategoryDataStorageAndProcessing - case "DOCUMENT_MANAGEMENT": - *i = VendorCategoryDocumentManagement - case "EMPLOYEE_MANAGEMENT": - *i = VendorCategoryEmployeeManagement - case "ENGINEERING": - *i = VendorCategoryEngineering - case "FINANCE": - *i = VendorCategoryFinance - case "IDENTITY_PROVIDER": - *i = VendorCategoryIdentityProvider - case "IT": - *i = VendorCategoryIT - case "MARKETING": - *i = VendorCategoryMarketing - case "OFFICE_OPERATIONS": - *i = VendorCategoryOfficeOperations - case "OTHER": - *i = VendorCategoryOther - case "PASSWORD_MANAGEMENT": - *i = VendorCategoryPasswordManagement - case "PRODUCT_AND_DESIGN": - *i = VendorCategoryProductAndDesign - case "PROFESSIONAL_SERVICES": - *i = VendorCategoryProfessionalServices - case "RECRUITING": - *i = VendorCategoryRecruiting - case "SALES": - *i = VendorCategorySales - case "SECURITY": - *i = VendorCategorySecurity - case "VERSION_CONTROL": - *i = VendorCategoryVersionControl - default: - return fmt.Errorf("invalid VendorCategory value: %q", v) - } - default: - return fmt.Errorf("unsupported type for VendorCategory: %T", value) - } - return nil -} - -func (i VendorCategory) Value() (driver.Value, error) { - return i.String(), nil -} - -func (i VendorCategory) MarshalJSON() ([]byte, error) { - return json.Marshal(i.String()) -} - -func (i *VendorCategory) UnmarshalJSON(data []byte) error { - var s string - if err := json.Unmarshal(data, &s); err != nil { - return err - } - - switch s { - case "ANALYTICS": - *i = VendorCategoryAnalytics - case "CLOUD_MONITORING": - *i = VendorCategoryCloudMonitoring - case "CLOUD_PROVIDER": - *i = VendorCategoryCloudProvider - case "COLLABORATION": - *i = VendorCategoryCollaboration - case "CUSTOMER_SUPPORT": - *i = VendorCategoryCustomerSupport - case "DATA_STORAGE_AND_PROCESSING": - *i = VendorCategoryDataStorageAndProcessing - case "DOCUMENT_MANAGEMENT": - *i = VendorCategoryDocumentManagement - case "EMPLOYEE_MANAGEMENT": - *i = VendorCategoryEmployeeManagement - case "ENGINEERING": - *i = VendorCategoryEngineering - case "FINANCE": - *i = VendorCategoryFinance - case "IDENTITY_PROVIDER": - *i = VendorCategoryIdentityProvider - case "IT": - *i = VendorCategoryIT - case "MARKETING": - *i = VendorCategoryMarketing - case "OFFICE_OPERATIONS": - *i = VendorCategoryOfficeOperations - case "OTHER": - *i = VendorCategoryOther - case "PASSWORD_MANAGEMENT": - *i = VendorCategoryPasswordManagement - case "PRODUCT_AND_DESIGN": - *i = VendorCategoryProductAndDesign - case "PROFESSIONAL_SERVICES": - *i = VendorCategoryProfessionalServices - case "RECRUITING": - *i = VendorCategoryRecruiting - case "SALES": - *i = VendorCategorySales - case "SECURITY": - *i = VendorCategorySecurity - case "VERSION_CONTROL": - *i = VendorCategoryVersionControl - default: - return fmt.Errorf("invalid VendorCategory value: %q", s) - } - return nil -} - -func (i *VendorCategory) UnmarshalText(text []byte) error { - s := string(text) - - switch s { - case "ANALYTICS": - *i = VendorCategoryAnalytics - case "CLOUD_MONITORING": - *i = VendorCategoryCloudMonitoring - case "CLOUD_PROVIDER": - *i = VendorCategoryCloudProvider - case "COLLABORATION": - *i = VendorCategoryCollaboration - case "CUSTOMER_SUPPORT": - *i = VendorCategoryCustomerSupport - case "DATA_STORAGE_AND_PROCESSING": - *i = VendorCategoryDataStorageAndProcessing - case "DOCUMENT_MANAGEMENT": - *i = VendorCategoryDocumentManagement - case "EMPLOYEE_MANAGEMENT": - *i = VendorCategoryEmployeeManagement - case "ENGINEERING": - *i = VendorCategoryEngineering - case "FINANCE": - *i = VendorCategoryFinance - case "IDENTITY_PROVIDER": - *i = VendorCategoryIdentityProvider - case "IT": - *i = VendorCategoryIT - case "MARKETING": - *i = VendorCategoryMarketing - case "OFFICE_OPERATIONS": - *i = VendorCategoryOfficeOperations - case "OTHER": - *i = VendorCategoryOther - case "PASSWORD_MANAGEMENT": - *i = VendorCategoryPasswordManagement - case "PRODUCT_AND_DESIGN": - *i = VendorCategoryProductAndDesign - case "PROFESSIONAL_SERVICES": - *i = VendorCategoryProfessionalServices - case "RECRUITING": - *i = VendorCategoryRecruiting - case "SALES": - *i = VendorCategorySales - case "SECURITY": - *i = VendorCategorySecurity - case "VERSION_CONTROL": - *i = VendorCategoryVersionControl - default: - return fmt.Errorf("invalid VendorCategory value: %q", s) - } - return nil -} diff --git a/pkg/coredata/webhook_event_type.go b/pkg/coredata/webhook_event_type.go index 320154116..772a9d922 100644 --- a/pkg/coredata/webhook_event_type.go +++ b/pkg/coredata/webhook_event_type.go @@ -23,9 +23,9 @@ import ( type WebhookEventType string const ( - WebhookEventTypeVendorCreated WebhookEventType = "vendor:created" - WebhookEventTypeVendorUpdated WebhookEventType = "vendor:updated" - WebhookEventTypeVendorDeleted WebhookEventType = "vendor:deleted" + WebhookEventTypeThirdPartyCreated WebhookEventType = "third-party:created" + WebhookEventTypeThirdPartyUpdated WebhookEventType = "third-party:updated" + WebhookEventTypeThirdPartyDeleted WebhookEventType = "third-party:deleted" WebhookEventTypeUserCreated WebhookEventType = "user:created" WebhookEventTypeUserUpdated WebhookEventType = "user:updated" WebhookEventTypeUserDeleted WebhookEventType = "user:deleted" @@ -40,7 +40,7 @@ func (w WebhookEventType) String() string { func (w WebhookEventType) IsValid() bool { switch w { - case WebhookEventTypeVendorCreated, WebhookEventTypeVendorUpdated, WebhookEventTypeVendorDeleted, + case WebhookEventTypeThirdPartyCreated, WebhookEventTypeThirdPartyUpdated, WebhookEventTypeThirdPartyDeleted, WebhookEventTypeUserCreated, WebhookEventTypeUserUpdated, WebhookEventTypeUserDeleted, WebhookEventTypeObligationCreated, WebhookEventTypeObligationUpdated, WebhookEventTypeObligationDeleted: return true diff --git a/pkg/docgen/generator.go b/pkg/docgen/generator.go index 81499f39e..2f2608ee9 100644 --- a/pkg/docgen/generator.go +++ b/pkg/docgen/generator.go @@ -239,7 +239,7 @@ type ( Name string Classification string Owner string - Vendors string + ThirdParties string } AssetListData struct { @@ -256,7 +256,7 @@ type ( Amount int DataTypesStored string Owner string - Vendors string + ThirdParties string } RiskListData struct { @@ -359,7 +359,7 @@ type ( LastReviewDate string NextReviewDate string DataProtectionOfficer string - Vendors string + ThirdParties string } DataProtectionImpactAssessmentListData struct { @@ -396,15 +396,15 @@ type ( SupplementaryMeasures string } - VendorListData struct { - Title string - OrganizationName string - CreatedAt time.Time - TotalVendors int - Rows []VendorListRow + ThirdPartyListData struct { + Title string + OrganizationName string + CreatedAt time.Time + TotalThirdParties int + Rows []ThirdPartyListRow } - VendorListRow struct { + ThirdPartyListRow struct { Name string LegalName string Description string @@ -424,27 +424,27 @@ type ( Countries string BusinessOwner string SecurityOwner string - Services []VendorListService - Contacts []VendorListContact - RiskAssessments []VendorListRiskAssessment - ComplianceReports []VendorListComplianceReport - BusinessAssociateAgreement *VendorListAgreement - DataPrivacyAgreement *VendorListAgreement + Services []ThirdPartyListService + Contacts []ThirdPartyListContact + RiskAssessments []ThirdPartyListRiskAssessment + ComplianceReports []ThirdPartyListComplianceReport + BusinessAssociateAgreement *ThirdPartyListAgreement + DataPrivacyAgreement *ThirdPartyListAgreement } - VendorListService struct { + ThirdPartyListService struct { Name string Description string } - VendorListContact struct { + ThirdPartyListContact struct { FullName string Email string Phone string Role string } - VendorListRiskAssessment struct { + ThirdPartyListRiskAssessment struct { AssessedAt string ExpiresAt string DataSensitivity string @@ -452,13 +452,13 @@ type ( Notes string } - VendorListComplianceReport struct { + ThirdPartyListComplianceReport struct { ReportName string ReportDate string ValidUntil string } - VendorListAgreement struct { + ThirdPartyListAgreement struct { ValidFrom string ValidUntil string } diff --git a/pkg/iam/authorizer.go b/pkg/iam/authorizer.go index c92995037..9fe590f40 100644 --- a/pkg/iam/authorizer.go +++ b/pkg/iam/authorizer.go @@ -272,7 +272,7 @@ func (a *Authorizer) buildPoliciesForRole(role string) []*policy.Policy { } // resourceTypeFromAction extracts the resource type name from an action -// string. For example, "core:vendor:create" returns "Vendor" and +// string. For example, "core:thirdParty:create" returns "ThirdParty" and // "core:webhook-subscription:delete" returns "WebhookSubscription". func resourceTypeFromAction(action string) string { parts := strings.Split(action, ":") diff --git a/pkg/iam/organization_service.go b/pkg/iam/organization_service.go index 4f1a594f1..0b72e3fac 100644 --- a/pkg/iam/organization_service.go +++ b/pkg/iam/organization_service.go @@ -123,7 +123,7 @@ type ( ) var ( - proboVendor = struct { + proboThirdParty = struct { Name string Description string LegalName string @@ -134,7 +134,7 @@ var ( SubprocessorsListURL string }{ Name: "Probo", - Description: "Probo is an open-source compliance platform that helps startups achieve SOC 2 and ISO 27001 certifications quickly and affordably, with expert guidance and no vendor lock-in.", + Description: "Probo is an open-source compliance platform that helps startups achieve SOC 2 and ISO 27001 certifications quickly and affordably, with expert guidance and no thirdParty lock-in.", LegalName: "Probo Inc.", HeadquarterAddress: "490 Post St, Suite 640,San Francisco, CA 94102, United States", WebsiteURL: "https://www.getprobo.com/", @@ -659,26 +659,26 @@ func (s *OrganizationService) CreateOrganization( return fmt.Errorf("cannot insert trust center: %w", err) } - proboData := &coredata.Vendor{ - ID: gid.New(scope.GetTenantID(), coredata.VendorEntityType), + proboData := &coredata.ThirdParty{ + ID: gid.New(scope.GetTenantID(), coredata.ThirdPartyEntityType), TenantID: organization.TenantID, OrganizationID: organization.ID, - Name: proboVendor.Name, - Description: &proboVendor.Description, - Category: coredata.VendorCategorySecurity, - HeadquarterAddress: &proboVendor.HeadquarterAddress, - LegalName: &proboVendor.LegalName, - WebsiteURL: &proboVendor.WebsiteURL, - PrivacyPolicyURL: &proboVendor.PrivacyPolicyURL, - TermsOfServiceURL: &proboVendor.TermsOfServiceURL, - SubprocessorsListURL: &proboVendor.SubprocessorsListURL, + Name: proboThirdParty.Name, + Description: &proboThirdParty.Description, + Category: coredata.ThirdPartyCategorySecurity, + HeadquarterAddress: &proboThirdParty.HeadquarterAddress, + LegalName: &proboThirdParty.LegalName, + WebsiteURL: &proboThirdParty.WebsiteURL, + PrivacyPolicyURL: &proboThirdParty.PrivacyPolicyURL, + TermsOfServiceURL: &proboThirdParty.TermsOfServiceURL, + SubprocessorsListURL: &proboThirdParty.SubprocessorsListURL, ShowOnTrustCenter: false, CreatedAt: now, UpdatedAt: now, } if err := proboData.Insert(ctx, tx, scope); err != nil { - return fmt.Errorf("cannot insert vendor: %w", err) + return fmt.Errorf("cannot insert thirdParty: %w", err) } return nil diff --git a/pkg/probo/actions.go b/pkg/probo/actions.go index f75976954..890e4d176 100644 --- a/pkg/probo/actions.go +++ b/pkg/probo/actions.go @@ -83,50 +83,50 @@ const ( ActionTrustCenterFileDelete = "core:trust-center-file:delete" ActionTrustCenterFileCreate = "core:trust-center-file:create" - // Vendor actions - ActionVendorList = "core:vendor:list" - ActionVendorGet = "core:vendor:get" - ActionVendorCreate = "core:vendor:create" - ActionVendorUpdate = "core:vendor:update" - ActionVendorDelete = "core:vendor:delete" - ActionVendorAssess = "core:vendor:assess" - ActionVendorPublish = "core:vendor:publish" + // ThirdParty actions + ActionThirdPartyList = "core:thirdParty:list" + ActionThirdPartyGet = "core:thirdParty:get" + ActionThirdPartyCreate = "core:thirdParty:create" + ActionThirdPartyUpdate = "core:thirdParty:update" + ActionThirdPartyDelete = "core:thirdParty:delete" + ActionThirdPartyAssess = "core:thirdParty:assess" + ActionThirdPartyPublish = "core:thirdParty:publish" - // VendorContact actions - ActionVendorContactGet = "core:vendor-contact:get" - ActionVendorContactList = "core:vendor-contact:list" - ActionVendorContactCreate = "core:vendor-contact:create" - ActionVendorContactUpdate = "core:vendor-contact:update" - ActionVendorContactDelete = "core:vendor-contact:delete" + // ThirdPartyContact actions + ActionThirdPartyContactGet = "core:thirdParty-contact:get" + ActionThirdPartyContactList = "core:thirdParty-contact:list" + ActionThirdPartyContactCreate = "core:thirdParty-contact:create" + ActionThirdPartyContactUpdate = "core:thirdParty-contact:update" + ActionThirdPartyContactDelete = "core:thirdParty-contact:delete" - // VendorService actions - ActionVendorServiceGet = "core:vendor-service:get" - ActionVendorServiceList = "core:vendor-service:list" - ActionVendorServiceCreate = "core:vendor-service:create" - ActionVendorServiceUpdate = "core:vendor-service:update" - ActionVendorServiceDelete = "core:vendor-service:delete" + // ThirdPartyService actions + ActionThirdPartyServiceGet = "core:thirdParty-service:get" + ActionThirdPartyServiceList = "core:thirdParty-service:list" + ActionThirdPartyServiceCreate = "core:thirdParty-service:create" + ActionThirdPartyServiceUpdate = "core:thirdParty-service:update" + ActionThirdPartyServiceDelete = "core:thirdParty-service:delete" - // VendorComplianceReport actions - ActionVendorComplianceReportGet = "core:vendor-compliance-report:get" - ActionVendorComplianceReportList = "core:vendor-compliance-report:list" - ActionVendorComplianceReportUpload = "core:vendor-compliance-report:upload" - ActionVendorComplianceReportDelete = "core:vendor-compliance-report:delete" + // ThirdPartyComplianceReport actions + ActionThirdPartyComplianceReportGet = "core:thirdParty-compliance-report:get" + ActionThirdPartyComplianceReportList = "core:thirdParty-compliance-report:list" + ActionThirdPartyComplianceReportUpload = "core:thirdParty-compliance-report:upload" + ActionThirdPartyComplianceReportDelete = "core:thirdParty-compliance-report:delete" - // VendorBusinessAssociateAgreement actions - ActionVendorBusinessAssociateAgreementGet = "core:vendor-business-associate-agreement:get" - ActionVendorBusinessAssociateAgreementUpload = "core:vendor-business-associate-agreement:upload" - ActionVendorBusinessAssociateAgreementUpdate = "core:vendor-business-associate-agreement:update" - ActionVendorBusinessAssociateAgreementDelete = "core:vendor-business-associate-agreement:delete" + // ThirdPartyBusinessAssociateAgreement actions + ActionThirdPartyBusinessAssociateAgreementGet = "core:thirdParty-business-associate-agreement:get" + ActionThirdPartyBusinessAssociateAgreementUpload = "core:thirdParty-business-associate-agreement:upload" + ActionThirdPartyBusinessAssociateAgreementUpdate = "core:thirdParty-business-associate-agreement:update" + ActionThirdPartyBusinessAssociateAgreementDelete = "core:thirdParty-business-associate-agreement:delete" - // VendorDataPrivacyAgreement actions - ActionVendorDataPrivacyAgreementGet = "core:vendor-data-privacy-agreement:get" - ActionVendorDataPrivacyAgreementUpload = "core:vendor-data-privacy-agreement:upload" - ActionVendorDataPrivacyAgreementUpdate = "core:vendor-data-privacy-agreement:update" - ActionVendorDataPrivacyAgreementDelete = "core:vendor-data-privacy-agreement:delete" + // ThirdPartyDataPrivacyAgreement actions + ActionThirdPartyDataPrivacyAgreementGet = "core:thirdParty-data-privacy-agreement:get" + ActionThirdPartyDataPrivacyAgreementUpload = "core:thirdParty-data-privacy-agreement:upload" + ActionThirdPartyDataPrivacyAgreementUpdate = "core:thirdParty-data-privacy-agreement:update" + ActionThirdPartyDataPrivacyAgreementDelete = "core:thirdParty-data-privacy-agreement:delete" - // VendorRiskAssessment actions - ActionVendorRiskAssessmentCreate = "core:vendor-risk-assessment:create" - ActionVendorRiskAssessmentList = "core:vendor-risk-assessment:list" + // ThirdPartyRiskAssessment actions + ActionThirdPartyRiskAssessmentCreate = "core:thirdParty-risk-assessment:create" + ActionThirdPartyRiskAssessmentList = "core:thirdParty-risk-assessment:list" // Framework actions ActionFrameworkGet = "core:framework:get" diff --git a/pkg/probo/asset_service.go b/pkg/probo/asset_service.go index 795810479..17c757306 100644 --- a/pkg/probo/asset_service.go +++ b/pkg/probo/asset_service.go @@ -37,7 +37,7 @@ type CreateAssetRequest struct { OwnerID gid.GID AssetType coredata.AssetType DataTypesStored string - VendorIDs []gid.GID + ThirdPartyIDs []gid.GID } type UpdateAssetRequest struct { @@ -47,7 +47,7 @@ type UpdateAssetRequest struct { OwnerID *gid.GID AssetType *coredata.AssetType DataTypesStored *string - VendorIDs []gid.GID + ThirdPartyIDs []gid.GID } func (car *CreateAssetRequest) Validate() error { @@ -59,8 +59,8 @@ func (car *CreateAssetRequest) Validate() error { v.Check(car.OwnerID, "owner_id", validator.Required(), validator.GID(coredata.MembershipProfileEntityType)) v.Check(car.AssetType, "asset_type", validator.Required(), validator.OneOfSlice(coredata.AssetTypes())) v.Check(car.DataTypesStored, "data_types_stored", validator.Required(), validator.SafeText(ContentMaxLength)) - v.CheckEach(car.VendorIDs, "vendor_ids", func(index int, item any) { - v.Check(item, fmt.Sprintf("vendor_ids[%d]", index), validator.Required(), validator.GID(coredata.VendorEntityType)) + v.CheckEach(car.ThirdPartyIDs, "third_party_ids", func(index int, item any) { + v.Check(item, fmt.Sprintf("third_party_ids[%d]", index), validator.Required(), validator.GID(coredata.ThirdPartyEntityType)) }) return v.Error() @@ -75,8 +75,8 @@ func (uar *UpdateAssetRequest) Validate() error { v.Check(uar.OwnerID, "owner_id", validator.GID(coredata.MembershipProfileEntityType)) v.Check(uar.AssetType, "asset_type", validator.OneOfSlice(coredata.AssetTypes())) v.Check(uar.DataTypesStored, "data_types_stored", validator.SafeText(ContentMaxLength)) - v.CheckEach(uar.VendorIDs, "vendor_ids", func(index int, item any) { - v.Check(item, fmt.Sprintf("vendor_ids[%d]", index), validator.GID(coredata.VendorEntityType)) + v.CheckEach(uar.ThirdPartyIDs, "third_party_ids", func(index int, item any) { + v.Check(item, fmt.Sprintf("third_party_ids[%d]", index), validator.GID(coredata.ThirdPartyEntityType)) }) return v.Error() @@ -185,7 +185,7 @@ func (s AssetService) Update( now := time.Now() asset := &coredata.Asset{ID: req.ID} - assetVendors := &coredata.AssetVendors{} + assetThirdParties := &coredata.AssetThirdParties{} err := s.svc.pg.WithTx(ctx, func(ctx context.Context, conn pg.Tx) error { if err := asset.LoadByID(ctx, conn, s.svc.scope, req.ID); err != nil { @@ -217,9 +217,9 @@ func (s AssetService) Update( return fmt.Errorf("cannot update asset: %w", err) } - if req.VendorIDs != nil { - if err := assetVendors.Merge(ctx, conn, s.svc.scope, asset.ID, asset.OrganizationID, req.VendorIDs); err != nil { - return fmt.Errorf("cannot update asset vendors: %w", err) + if req.ThirdPartyIDs != nil { + if err := assetThirdParties.Merge(ctx, conn, s.svc.scope, asset.ID, asset.OrganizationID, req.ThirdPartyIDs); err != nil { + return fmt.Errorf("cannot update asset thirdParties: %w", err) } } @@ -243,7 +243,7 @@ func (s AssetService) Create( now := time.Now() assetID := gid.New(s.svc.scope.GetTenantID(), coredata.AssetEntityType) - assetVendors := &coredata.AssetVendors{} + assetThirdParties := &coredata.AssetThirdParties{} asset := &coredata.Asset{ ID: assetID, @@ -267,9 +267,9 @@ func (s AssetService) Create( return fmt.Errorf("cannot insert asset: %w", err) } - if len(req.VendorIDs) > 0 { - if err := assetVendors.Insert(ctx, conn, s.svc.scope, asset.ID, asset.OrganizationID, req.VendorIDs); err != nil { - return fmt.Errorf("cannot create asset vendors: %w", err) + if len(req.ThirdPartyIDs) > 0 { + if err := assetThirdParties.Insert(ctx, conn, s.svc.scope, asset.ID, asset.OrganizationID, req.ThirdPartyIDs); err != nil { + return fmt.Errorf("cannot create asset thirdParties: %w", err) } } diff --git a/pkg/probo/datum_service.go b/pkg/probo/datum_service.go index cb1bd2f6d..4993b5dbd 100644 --- a/pkg/probo/datum_service.go +++ b/pkg/probo/datum_service.go @@ -36,7 +36,7 @@ type ( Name string DataClassification coredata.DataClassification OwnerID gid.GID - VendorIDs []gid.GID + ThirdPartyIDs []gid.GID } UpdateDatumRequest struct { @@ -44,7 +44,7 @@ type ( Name *string DataClassification *coredata.DataClassification OwnerID *gid.GID - VendorIDs []gid.GID + ThirdPartyIDs []gid.GID } ) @@ -55,8 +55,8 @@ func (cdr *CreateDatumRequest) Validate() error { v.Check(cdr.Name, "name", validator.SafeTextNoNewLine(NameMaxLength)) v.Check(cdr.DataClassification, "data_classification", validator.Required(), validator.OneOfSlice(coredata.DataClassifications())) v.Check(cdr.OwnerID, "owner_id", validator.Required(), validator.GID(coredata.MembershipProfileEntityType)) - v.CheckEach(cdr.VendorIDs, "vendor_ids", func(index int, item any) { - v.Check(item, fmt.Sprintf("vendor_ids[%d]", index), validator.Required(), validator.GID(coredata.VendorEntityType)) + v.CheckEach(cdr.ThirdPartyIDs, "third_party_ids", func(index int, item any) { + v.Check(item, fmt.Sprintf("third_party_ids[%d]", index), validator.Required(), validator.GID(coredata.ThirdPartyEntityType)) }) return v.Error() @@ -69,8 +69,8 @@ func (udr *UpdateDatumRequest) Validate() error { v.Check(udr.Name, "name", validator.SafeTextNoNewLine(NameMaxLength)) v.Check(udr.DataClassification, "data_classification", validator.OneOfSlice(coredata.DataClassifications())) v.Check(udr.OwnerID, "owner_id", validator.GID(coredata.MembershipProfileEntityType)) - v.CheckEach(udr.VendorIDs, "vendor_ids", func(index int, item any) { - v.Check(item, fmt.Sprintf("vendor_ids[%d]", index), validator.Required(), validator.GID(coredata.VendorEntityType)) + v.CheckEach(udr.ThirdPartyIDs, "third_party_ids", func(index int, item any) { + v.Check(item, fmt.Sprintf("third_party_ids[%d]", index), validator.Required(), validator.GID(coredata.ThirdPartyEntityType)) }) return v.Error() @@ -179,7 +179,7 @@ func (s DatumService) Update( now := time.Now() datum := &coredata.Datum{} - datumVendors := &coredata.DatumVendors{} + datumThirdParties := &coredata.DatumThirdParties{} err := s.svc.pg.WithTx(ctx, func(ctx context.Context, conn pg.Tx) error { if err := datum.LoadByID(ctx, conn, s.svc.scope, req.ID); err != nil { @@ -205,9 +205,9 @@ func (s DatumService) Update( return fmt.Errorf("cannot update data: %w", err) } - if req.VendorIDs != nil { - if err := datumVendors.Merge(ctx, conn, s.svc.scope, datum.ID, datum.OrganizationID, req.VendorIDs); err != nil { - return fmt.Errorf("cannot update data vendors: %w", err) + if req.ThirdPartyIDs != nil { + if err := datumThirdParties.Merge(ctx, conn, s.svc.scope, datum.ID, datum.OrganizationID, req.ThirdPartyIDs); err != nil { + return fmt.Errorf("cannot update data thirdParties: %w", err) } } @@ -231,7 +231,7 @@ func (s DatumService) Create( now := time.Now() datumID := gid.New(s.svc.scope.GetTenantID(), coredata.DatumEntityType) - datumVendors := &coredata.DatumVendors{} + datumThirdParties := &coredata.DatumThirdParties{} datum := &coredata.Datum{ ID: datumID, @@ -255,9 +255,9 @@ func (s DatumService) Create( return fmt.Errorf("cannot insert datum: %w", err) } - if len(req.VendorIDs) > 0 { - if err := datumVendors.Insert(ctx, conn, s.svc.scope, datum.ID, datum.OrganizationID, req.VendorIDs); err != nil { - return fmt.Errorf("cannot create data vendors: %w", err) + if len(req.ThirdPartyIDs) > 0 { + if err := datumThirdParties.Insert(ctx, conn, s.svc.scope, datum.ID, datum.OrganizationID, req.ThirdPartyIDs); err != nil { + return fmt.Errorf("cannot create data thirdParties: %w", err) } } @@ -286,17 +286,17 @@ func (s DatumService) Delete( ) } -func (s DatumService) ListVendors( +func (s DatumService) ListThirdParties( ctx context.Context, datumID gid.GID, - cursor *page.Cursor[coredata.VendorOrderField], -) (*page.Page[*coredata.Vendor, coredata.VendorOrderField], error) { - var vendors coredata.Vendors + cursor *page.Cursor[coredata.ThirdPartyOrderField], +) (*page.Page[*coredata.ThirdParty, coredata.ThirdPartyOrderField], error) { + var thirdParties coredata.ThirdParties err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - return vendors.LoadByDatumID(ctx, conn, s.svc.scope, datumID, cursor) + return thirdParties.LoadByDatumID(ctx, conn, s.svc.scope, datumID, cursor) }, ) @@ -304,5 +304,5 @@ func (s DatumService) ListVendors( return nil, err } - return page.NewPage(vendors, cursor), nil + return page.NewPage(thirdParties, cursor), nil } diff --git a/pkg/probo/generated_document_service.go b/pkg/probo/generated_document_service.go index c8115bdd7..dc8f4901d 100644 --- a/pkg/probo/generated_document_service.go +++ b/pkg/probo/generated_document_service.go @@ -449,26 +449,26 @@ func (s *GeneratedDocumentService) buildDataListDocumentData( ownerName = p.FullName } - var vendors coredata.Vendors - if err := vendors.LoadAllByDatumID(ctx, conn, s.svc.scope, d.ID); err != nil { - return docgen.DataListData{}, fmt.Errorf("cannot load vendors for datum %s: %w", d.ID, err) + var thirdParties coredata.ThirdParties + if err := thirdParties.LoadAllByDatumID(ctx, conn, s.svc.scope, d.ID); err != nil { + return docgen.DataListData{}, fmt.Errorf("cannot load thirdParties for datum %s: %w", d.ID, err) } - vendorNames := make([]string, 0, len(vendors)) - for _, v := range vendors { - vendorNames = append(vendorNames, v.Name) + thirdPartyNames := make([]string, 0, len(thirdParties)) + for _, v := range thirdParties { + thirdPartyNames = append(thirdPartyNames, v.Name) } - vendorStr := "-" - if len(vendorNames) > 0 { - vendorStr = strings.Join(vendorNames, ", ") + thirdPartyStr := "-" + if len(thirdPartyNames) > 0 { + thirdPartyStr = strings.Join(thirdPartyNames, ", ") } rows = append(rows, docgen.DataListRow{ Name: d.Name, Classification: formatClassification(d.DataClassification), Owner: ownerName, - Vendors: vendorStr, + ThirdParties: thirdPartyStr, }) } @@ -685,19 +685,19 @@ func (s *GeneratedDocumentService) buildAssetListDocumentData( ownerName = p.FullName } - var vendors coredata.Vendors - if err := vendors.LoadAllByAssetID(ctx, conn, s.svc.scope, a.ID); err != nil { - return docgen.AssetListData{}, fmt.Errorf("cannot load vendors for asset %s: %w", a.ID, err) + var thirdParties coredata.ThirdParties + if err := thirdParties.LoadAllByAssetID(ctx, conn, s.svc.scope, a.ID); err != nil { + return docgen.AssetListData{}, fmt.Errorf("cannot load thirdParties for asset %s: %w", a.ID, err) } - vendorNames := make([]string, 0, len(vendors)) - for _, v := range vendors { - vendorNames = append(vendorNames, v.Name) + thirdPartyNames := make([]string, 0, len(thirdParties)) + for _, v := range thirdParties { + thirdPartyNames = append(thirdPartyNames, v.Name) } - vendorStr := "-" - if len(vendorNames) > 0 { - vendorStr = strings.Join(vendorNames, ", ") + thirdPartyStr := "-" + if len(thirdPartyNames) > 0 { + thirdPartyStr = strings.Join(thirdPartyNames, ", ") } rows = append(rows, docgen.AssetListRow{ @@ -706,7 +706,7 @@ func (s *GeneratedDocumentService) buildAssetListDocumentData( Amount: a.Amount, DataTypesStored: stringOrNotSpecified(a.DataTypesStored), Owner: ownerName, - Vendors: vendorStr, + ThirdParties: thirdPartyStr, }) } @@ -1488,10 +1488,10 @@ func (s *GeneratedDocumentService) buildProcessingActivityListDocumentData( }, nil } - var vendors coredata.Vendors - vendorMap, err := vendors.LoadAllByProcessingActivities(ctx, conn, s.svc.scope, organization.ID) + var thirdParties coredata.ThirdParties + thirdPartyMap, err := thirdParties.LoadAllByProcessingActivities(ctx, conn, s.svc.scope, organization.ID) if err != nil { - return docgen.ProcessingActivityListData{}, fmt.Errorf("cannot load vendors: %w", err) + return docgen.ProcessingActivityListData{}, fmt.Errorf("cannot load thirdParties: %w", err) } dpoIDs := make([]gid.GID, 0, len(processingActivities)) @@ -1526,9 +1526,9 @@ func (s *GeneratedDocumentService) buildProcessingActivityListDocumentData( } } - vendorStr := "None" - if vendorNames, ok := vendorMap[pa.ID]; ok && len(vendorNames) > 0 { - vendorStr = strings.Join(vendorNames, ", ") + thirdPartyStr := "None" + if thirdPartyNames, ok := thirdPartyMap[pa.ID]; ok && len(thirdPartyNames) > 0 { + thirdPartyStr = strings.Join(thirdPartyNames, ", ") } rows = append(rows, docgen.ProcessingActivityListRow{ @@ -1551,7 +1551,7 @@ func (s *GeneratedDocumentService) buildProcessingActivityListDocumentData( LastReviewDate: formatDateOrNotSpecified(pa.LastReviewDate), NextReviewDate: formatDateOrNotSpecified(pa.NextReviewDate), DataProtectionOfficer: dpoName, - Vendors: vendorStr, + ThirdParties: thirdPartyStr, }) } @@ -2132,17 +2132,17 @@ func BuildTransferImpactAssessmentListDocument(data docgen.TransferImpactAssessm return buf.String(), nil } -func (s *GeneratedDocumentService) PublishVendorList( +func (s *GeneratedDocumentService) PublishThirdPartyList( ctx context.Context, organizationID gid.GID, approverIDs []gid.GID, minor bool, ) (*coredata.Document, *coredata.DocumentVersion, error) { // Phase 1: collect data and render the prosemirror document outside any - // write transaction. Both the bulk reads of vendors + sub-entities and the + // write transaction. Both the bulk reads of thirdParties + sub-entities and the // JSON template rendering are slow enough that holding write locks across // them would needlessly block other writers. - var documentData docgen.VendorListData + var documentData docgen.ThirdPartyListData err := s.svc.pg.WithConn(ctx, func(ctx context.Context, conn pg.Querier) error { organization := &coredata.Organization{} if err := organization.LoadByID(ctx, conn, s.svc.scope, organizationID); err != nil { @@ -2150,7 +2150,7 @@ func (s *GeneratedDocumentService) PublishVendorList( } var err error - documentData, err = s.buildVendorListDocumentData(ctx, conn, organization) + documentData, err = s.buildThirdPartyListDocumentData(ctx, conn, organization) if err != nil { return fmt.Errorf("cannot build document data: %w", err) } @@ -2160,7 +2160,7 @@ func (s *GeneratedDocumentService) PublishVendorList( return nil, nil, err } - prosemirrorJSON, err := BuildVendorListDocument(documentData) + prosemirrorJSON, err := BuildThirdPartyListDocument(documentData) if err != nil { return nil, nil, fmt.Errorf("cannot build prosemirror document: %w", err) } @@ -2176,24 +2176,24 @@ func (s *GeneratedDocumentService) PublishVendorList( func(ctx context.Context, tx pg.Tx) error { now := time.Now() - vendor := coredata.Vendor{} - vendorDocumentID, err := vendor.GetGeneratedDocumentID(ctx, tx, organizationID) + thirdParty := coredata.ThirdParty{} + thirdPartyDocumentID, err := thirdParty.GetGeneratedDocumentID(ctx, tx, organizationID) if err != nil { return fmt.Errorf("cannot query generated documents: %w", err) } var existingDoc *coredata.Document - if vendorDocumentID != nil { + if thirdPartyDocumentID != nil { doc := &coredata.Document{} - err = doc.LoadByID(ctx, tx, s.svc.scope, *vendorDocumentID) + err = doc.LoadByID(ctx, tx, s.svc.scope, *thirdPartyDocumentID) if err != nil && !errors.Is(err, coredata.ErrResourceNotFound) { - return fmt.Errorf("cannot load vendor list document: %w", err) + return fmt.Errorf("cannot load thirdParty list document: %w", err) } if err == nil && doc.ArchivedAt == nil { existingDoc = doc } else { - if err := vendor.ClearGeneratedDocumentID(ctx, tx, []gid.GID{*vendorDocumentID}); err != nil { + if err := thirdParty.ClearGeneratedDocumentID(ctx, tx, []gid.GID{*thirdPartyDocumentID}); err != nil { return fmt.Errorf("cannot clear document reference: %w", err) } } @@ -2216,7 +2216,7 @@ func (s *GeneratedDocumentService) PublishVendorList( return fmt.Errorf("cannot insert document: %w", err) } - if err := vendor.UpsertGeneratedDocumentID(ctx, tx, organizationID, s.svc.scope.GetTenantID(), documentID); err != nil { + if err := thirdParty.UpsertGeneratedDocumentID(ctx, tx, organizationID, s.svc.scope.GetTenantID(), documentID); err != nil { return fmt.Errorf("cannot upsert generated documents: %w", err) } } else { @@ -2228,7 +2228,7 @@ func (s *GeneratedDocumentService) PublishVendorList( ID: documentVersionID, OrganizationID: organizationID, DocumentID: document.ID, - Title: "Vendors", + Title: "ThirdParties", Content: prosemirrorJSON, Classification: coredata.DocumentClassificationConfidential, DocumentType: coredata.DocumentTypeRegister, @@ -2248,47 +2248,47 @@ func (s *GeneratedDocumentService) PublishVendorList( return document, documentVersion, nil } -func (s *GeneratedDocumentService) GetVendorsDocumentID( +func (s *GeneratedDocumentService) GetThirdPartiesDocumentID( ctx context.Context, organizationID gid.GID, ) (*gid.GID, error) { var documentID *gid.GID err := s.svc.pg.WithConn(ctx, func(ctx context.Context, conn pg.Querier) error { - vendor := coredata.Vendor{} + thirdParty := coredata.ThirdParty{} var err error - documentID, err = vendor.GetGeneratedDocumentID(ctx, conn, organizationID) + documentID, err = thirdParty.GetGeneratedDocumentID(ctx, conn, organizationID) return err }) if err != nil { - return nil, fmt.Errorf("cannot get vendor list document ID: %w", err) + return nil, fmt.Errorf("cannot get thirdParty list document ID: %w", err) } return documentID, nil } -func (s *GeneratedDocumentService) buildVendorListDocumentData( +func (s *GeneratedDocumentService) buildThirdPartyListDocumentData( ctx context.Context, conn pg.Querier, organization *coredata.Organization, -) (docgen.VendorListData, error) { - var vendors coredata.Vendors - if err := vendors.LoadAllByOrganizationID(ctx, conn, s.svc.scope, organization.ID); err != nil { - return docgen.VendorListData{}, fmt.Errorf("cannot load vendors: %w", err) +) (docgen.ThirdPartyListData, error) { + var thirdParties coredata.ThirdParties + if err := thirdParties.LoadAllByOrganizationID(ctx, conn, s.svc.scope, organization.ID); err != nil { + return docgen.ThirdPartyListData{}, fmt.Errorf("cannot load thirdParties: %w", err) } - if len(vendors) == 0 { - return docgen.VendorListData{ - Title: "Vendors", - OrganizationName: organization.Name, - CreatedAt: time.Now(), - TotalVendors: 0, + if len(thirdParties) == 0 { + return docgen.ThirdPartyListData{ + Title: "ThirdParties", + OrganizationName: organization.Name, + CreatedAt: time.Now(), + TotalThirdParties: 0, }, nil } ownerIDSet := make(map[gid.GID]struct{}) ownerIDs := make([]gid.GID, 0) - for _, v := range vendors { + for _, v := range thirdParties { if v.BusinessOwnerID != nil { if _, ok := ownerIDSet[*v.BusinessOwnerID]; !ok { ownerIDs = append(ownerIDs, *v.BusinessOwnerID) @@ -2307,79 +2307,79 @@ func (s *GeneratedDocumentService) buildVendorListDocumentData( if len(ownerIDs) > 0 { var profiles coredata.MembershipProfiles if err := profiles.LoadByIDs(ctx, conn, s.svc.scope, ownerIDs); err != nil { - return docgen.VendorListData{}, fmt.Errorf("cannot load owner profiles: %w", err) + return docgen.ThirdPartyListData{}, fmt.Errorf("cannot load owner profiles: %w", err) } for _, p := range profiles { profileMap[p.ID] = p } } - vendorIDs := make([]gid.GID, len(vendors)) - for i, v := range vendors { - vendorIDs[i] = v.ID + thirdPartyIDs := make([]gid.GID, len(thirdParties)) + for i, v := range thirdParties { + thirdPartyIDs[i] = v.ID } - var allServices coredata.VendorServices - if err := allServices.LoadByVendorIDs(ctx, conn, s.svc.scope, vendorIDs); err != nil { - return docgen.VendorListData{}, fmt.Errorf("cannot load vendor services: %w", err) + var allServices coredata.ThirdPartyServices + if err := allServices.LoadByThirdPartyIDs(ctx, conn, s.svc.scope, thirdPartyIDs); err != nil { + return docgen.ThirdPartyListData{}, fmt.Errorf("cannot load thirdParty services: %w", err) } - servicesByVendor := make(map[gid.GID]coredata.VendorServices, len(vendors)) + servicesByThirdParty := make(map[gid.GID]coredata.ThirdPartyServices, len(thirdParties)) for _, vs := range allServices { - servicesByVendor[vs.VendorID] = append(servicesByVendor[vs.VendorID], vs) + servicesByThirdParty[vs.ThirdPartyID] = append(servicesByThirdParty[vs.ThirdPartyID], vs) } - var allContacts coredata.VendorContacts - if err := allContacts.LoadByVendorIDs(ctx, conn, s.svc.scope, vendorIDs); err != nil { - return docgen.VendorListData{}, fmt.Errorf("cannot load vendor contacts: %w", err) + var allContacts coredata.ThirdPartyContacts + if err := allContacts.LoadByThirdPartyIDs(ctx, conn, s.svc.scope, thirdPartyIDs); err != nil { + return docgen.ThirdPartyListData{}, fmt.Errorf("cannot load thirdParty contacts: %w", err) } - contactsByVendor := make(map[gid.GID]coredata.VendorContacts, len(vendors)) + contactsByThirdParty := make(map[gid.GID]coredata.ThirdPartyContacts, len(thirdParties)) for _, c := range allContacts { - contactsByVendor[c.VendorID] = append(contactsByVendor[c.VendorID], c) + contactsByThirdParty[c.ThirdPartyID] = append(contactsByThirdParty[c.ThirdPartyID], c) } - var allAssessments coredata.VendorRiskAssessments - if err := allAssessments.LoadByVendorIDs(ctx, conn, s.svc.scope, vendorIDs); err != nil { - return docgen.VendorListData{}, fmt.Errorf("cannot load vendor risk assessments: %w", err) + var allAssessments coredata.ThirdPartyRiskAssessments + if err := allAssessments.LoadByThirdPartyIDs(ctx, conn, s.svc.scope, thirdPartyIDs); err != nil { + return docgen.ThirdPartyListData{}, fmt.Errorf("cannot load thirdParty risk assessments: %w", err) } - assessmentsByVendor := make(map[gid.GID]coredata.VendorRiskAssessments, len(vendors)) + assessmentsByThirdParty := make(map[gid.GID]coredata.ThirdPartyRiskAssessments, len(thirdParties)) for _, ra := range allAssessments { - assessmentsByVendor[ra.VendorID] = append(assessmentsByVendor[ra.VendorID], ra) + assessmentsByThirdParty[ra.ThirdPartyID] = append(assessmentsByThirdParty[ra.ThirdPartyID], ra) } - var allReports coredata.VendorComplianceReports - if err := allReports.LoadByVendorIDs(ctx, conn, s.svc.scope, vendorIDs); err != nil { - return docgen.VendorListData{}, fmt.Errorf("cannot load vendor compliance reports: %w", err) + var allReports coredata.ThirdPartyComplianceReports + if err := allReports.LoadByThirdPartyIDs(ctx, conn, s.svc.scope, thirdPartyIDs); err != nil { + return docgen.ThirdPartyListData{}, fmt.Errorf("cannot load thirdParty compliance reports: %w", err) } - reportsByVendor := make(map[gid.GID]coredata.VendorComplianceReports, len(vendors)) + reportsByThirdParty := make(map[gid.GID]coredata.ThirdPartyComplianceReports, len(thirdParties)) for _, r := range allReports { - reportsByVendor[r.VendorID] = append(reportsByVendor[r.VendorID], r) + reportsByThirdParty[r.ThirdPartyID] = append(reportsByThirdParty[r.ThirdPartyID], r) } - var allBAAs coredata.VendorBusinessAssociateAgreements - if err := allBAAs.LoadByVendorIDs(ctx, conn, s.svc.scope, vendorIDs); err != nil { - return docgen.VendorListData{}, fmt.Errorf("cannot load vendor business associate agreements: %w", err) + var allBAAs coredata.ThirdPartyBusinessAssociateAgreements + if err := allBAAs.LoadByThirdPartyIDs(ctx, conn, s.svc.scope, thirdPartyIDs); err != nil { + return docgen.ThirdPartyListData{}, fmt.Errorf("cannot load thirdParty business associate agreements: %w", err) } - baaByVendor := make(map[gid.GID]*coredata.VendorBusinessAssociateAgreement, len(allBAAs)) + baaByThirdParty := make(map[gid.GID]*coredata.ThirdPartyBusinessAssociateAgreement, len(allBAAs)) for _, b := range allBAAs { - baaByVendor[b.VendorID] = b + baaByThirdParty[b.ThirdPartyID] = b } - var allDPAs coredata.VendorDataPrivacyAgreements - if err := allDPAs.LoadByVendorIDs(ctx, conn, s.svc.scope, vendorIDs); err != nil { - return docgen.VendorListData{}, fmt.Errorf("cannot load vendor data privacy agreements: %w", err) + var allDPAs coredata.ThirdPartyDataPrivacyAgreements + if err := allDPAs.LoadByThirdPartyIDs(ctx, conn, s.svc.scope, thirdPartyIDs); err != nil { + return docgen.ThirdPartyListData{}, fmt.Errorf("cannot load thirdParty data privacy agreements: %w", err) } - dpaByVendor := make(map[gid.GID]*coredata.VendorDataPrivacyAgreement, len(allDPAs)) + dpaByThirdParty := make(map[gid.GID]*coredata.ThirdPartyDataPrivacyAgreement, len(allDPAs)) for _, d := range allDPAs { - dpaByVendor[d.VendorID] = d + dpaByThirdParty[d.ThirdPartyID] = d } - rows := make([]docgen.VendorListRow, 0, len(vendors)) - for _, v := range vendors { - row := docgen.VendorListRow{ + rows := make([]docgen.ThirdPartyListRow, 0, len(thirdParties)) + for _, v := range thirdParties { + row := docgen.ThirdPartyListRow{ Name: v.Name, LegalName: derefStringOrNotSpecified(v.LegalName), Description: derefStringOrNotSpecified(v.Description), - Category: formatVendorCategory(v.Category), + Category: formatThirdPartyCategory(v.Category), HeadquarterAddress: derefStringOrNotSpecified(v.HeadquarterAddress), WebsiteURL: derefStringOrNotSpecified(v.WebsiteURL), PrivacyPolicyURL: derefStringOrNotSpecified(v.PrivacyPolicyURL), @@ -2397,19 +2397,19 @@ func (s *GeneratedDocumentService) buildVendorListDocumentData( SecurityOwner: lookupProfileName(profileMap, v.SecurityOwnerID), } - for _, vs := range servicesByVendor[v.ID] { - row.Services = append(row.Services, docgen.VendorListService{ + for _, vs := range servicesByThirdParty[v.ID] { + row.Services = append(row.Services, docgen.ThirdPartyListService{ Name: vs.Name, Description: derefStringOrNotSpecified(vs.Description), }) } - for _, c := range contactsByVendor[v.ID] { + for _, c := range contactsByThirdParty[v.ID] { email := "" if c.Email != nil { email = c.Email.String() } - row.Contacts = append(row.Contacts, docgen.VendorListContact{ + row.Contacts = append(row.Contacts, docgen.ThirdPartyListContact{ FullName: derefStringOrNotSpecified(c.FullName), Email: stringOrNotSpecified(email), Phone: derefStringOrNotSpecified(c.Phone), @@ -2417,8 +2417,8 @@ func (s *GeneratedDocumentService) buildVendorListDocumentData( }) } - for _, ra := range assessmentsByVendor[v.ID] { - row.RiskAssessments = append(row.RiskAssessments, docgen.VendorListRiskAssessment{ + for _, ra := range assessmentsByThirdParty[v.ID] { + row.RiskAssessments = append(row.RiskAssessments, docgen.ThirdPartyListRiskAssessment{ AssessedAt: ra.CreatedAt.Format("2006-01-02"), ExpiresAt: ra.ExpiresAt.Format("2006-01-02"), DataSensitivity: formatDataSensitivity(ra.DataSensitivity), @@ -2427,23 +2427,23 @@ func (s *GeneratedDocumentService) buildVendorListDocumentData( }) } - for _, r := range reportsByVendor[v.ID] { - row.ComplianceReports = append(row.ComplianceReports, docgen.VendorListComplianceReport{ + for _, r := range reportsByThirdParty[v.ID] { + row.ComplianceReports = append(row.ComplianceReports, docgen.ThirdPartyListComplianceReport{ ReportName: r.ReportName, ReportDate: r.ReportDate.Format("2006-01-02"), ValidUntil: formatTimeOrNotSpecified(r.ValidUntil), }) } - if baa := baaByVendor[v.ID]; baa != nil { - row.BusinessAssociateAgreement = &docgen.VendorListAgreement{ + if baa := baaByThirdParty[v.ID]; baa != nil { + row.BusinessAssociateAgreement = &docgen.ThirdPartyListAgreement{ ValidFrom: formatTimeOrNotSpecified(baa.ValidFrom), ValidUntil: formatTimeOrNotSpecified(baa.ValidUntil), } } - if dpa := dpaByVendor[v.ID]; dpa != nil { - row.DataPrivacyAgreement = &docgen.VendorListAgreement{ + if dpa := dpaByThirdParty[v.ID]; dpa != nil { + row.DataPrivacyAgreement = &docgen.ThirdPartyListAgreement{ ValidFrom: formatTimeOrNotSpecified(dpa.ValidFrom), ValidUntil: formatTimeOrNotSpecified(dpa.ValidUntil), } @@ -2452,12 +2452,12 @@ func (s *GeneratedDocumentService) buildVendorListDocumentData( rows = append(rows, row) } - return docgen.VendorListData{ - Title: "Vendors", - OrganizationName: organization.Name, - CreatedAt: time.Now(), - TotalVendors: len(vendors), - Rows: rows, + return docgen.ThirdPartyListData{ + Title: "ThirdParties", + OrganizationName: organization.Name, + CreatedAt: time.Now(), + TotalThirdParties: len(thirdParties), + Rows: rows, }, nil } @@ -2535,59 +2535,59 @@ func formatBusinessImpact(b coredata.BusinessImpact) string { } } -func formatVendorCategory(c coredata.VendorCategory) string { +func formatThirdPartyCategory(c coredata.ThirdPartyCategory) string { switch c { - case coredata.VendorCategoryAnalytics: + case coredata.ThirdPartyCategoryAnalytics: return "Analytics" - case coredata.VendorCategoryCloudMonitoring: + case coredata.ThirdPartyCategoryCloudMonitoring: return "Cloud Monitoring" - case coredata.VendorCategoryCloudProvider: + case coredata.ThirdPartyCategoryCloudProvider: return "Cloud Provider" - case coredata.VendorCategoryCollaboration: + case coredata.ThirdPartyCategoryCollaboration: return "Collaboration" - case coredata.VendorCategoryCustomerSupport: + case coredata.ThirdPartyCategoryCustomerSupport: return "Customer Support" - case coredata.VendorCategoryDataStorageAndProcessing: + case coredata.ThirdPartyCategoryDataStorageAndProcessing: return "Data Storage and Processing" - case coredata.VendorCategoryDocumentManagement: + case coredata.ThirdPartyCategoryDocumentManagement: return "Document Management" - case coredata.VendorCategoryEmployeeManagement: + case coredata.ThirdPartyCategoryEmployeeManagement: return "Employee Management" - case coredata.VendorCategoryEngineering: + case coredata.ThirdPartyCategoryEngineering: return "Engineering" - case coredata.VendorCategoryFinance: + case coredata.ThirdPartyCategoryFinance: return "Finance" - case coredata.VendorCategoryIdentityProvider: + case coredata.ThirdPartyCategoryIdentityProvider: return "Identity Provider" - case coredata.VendorCategoryIT: + case coredata.ThirdPartyCategoryIT: return "IT" - case coredata.VendorCategoryMarketing: + case coredata.ThirdPartyCategoryMarketing: return "Marketing" - case coredata.VendorCategoryOfficeOperations: + case coredata.ThirdPartyCategoryOfficeOperations: return "Office Operations" - case coredata.VendorCategoryOther: + case coredata.ThirdPartyCategoryOther: return "Other" - case coredata.VendorCategoryPasswordManagement: + case coredata.ThirdPartyCategoryPasswordManagement: return "Password Management" - case coredata.VendorCategoryProductAndDesign: + case coredata.ThirdPartyCategoryProductAndDesign: return "Product and Design" - case coredata.VendorCategoryProfessionalServices: + case coredata.ThirdPartyCategoryProfessionalServices: return "Professional Services" - case coredata.VendorCategoryRecruiting: + case coredata.ThirdPartyCategoryRecruiting: return "Recruiting" - case coredata.VendorCategorySales: + case coredata.ThirdPartyCategorySales: return "Sales" - case coredata.VendorCategorySecurity: + case coredata.ThirdPartyCategorySecurity: return "Security" - case coredata.VendorCategoryVersionControl: + case coredata.ThirdPartyCategoryVersionControl: return "Version Control" default: return stringOrNotSpecified(string(c)) } } -var vendorListTemplate = template.Must( - template.New("vendor_list.json.tmpl"). +var thirdPartyListTemplate = template.Must( + template.New("third_party_list.json.tmpl"). Funcs(template.FuncMap{ "json": func(v any) (string, error) { b, err := json.Marshal(v) @@ -2599,13 +2599,13 @@ var vendorListTemplate = template.Must( "printf": fmt.Sprintf, "add": func(a, b int) int { return a + b }, }). - ParseFS(Templates, "templates/vendor_list.json.tmpl"), + ParseFS(Templates, "templates/third_party_list.json.tmpl"), ) -func BuildVendorListDocument(data docgen.VendorListData) (string, error) { +func BuildThirdPartyListDocument(data docgen.ThirdPartyListData) (string, error) { var buf bytes.Buffer - if err := vendorListTemplate.Execute(&buf, data); err != nil { - return "", fmt.Errorf("cannot execute vendor list template: %w", err) + if err := thirdPartyListTemplate.Execute(&buf, data); err != nil { + return "", fmt.Errorf("cannot execute thirdParty list template: %w", err) } return buf.String(), nil } diff --git a/pkg/probo/policies.go b/pkg/probo/policies.go index 63067df74..85d29a516 100644 --- a/pkg/probo/policies.go +++ b/pkg/probo/policies.go @@ -48,13 +48,13 @@ var ViewerPolicy = policy.NewPolicy( ).WithSID("org-read-access").When(organizationCondition), policy.Allow( - ActionVendorGet, ActionVendorList, - ActionVendorContactGet, ActionVendorContactList, - ActionVendorServiceGet, ActionVendorServiceList, - ActionVendorComplianceReportGet, ActionVendorComplianceReportList, - ActionVendorBusinessAssociateAgreementGet, - ActionVendorDataPrivacyAgreementGet, - ActionVendorRiskAssessmentList, + ActionThirdPartyGet, ActionThirdPartyList, + ActionThirdPartyContactGet, ActionThirdPartyContactList, + ActionThirdPartyServiceGet, ActionThirdPartyServiceList, + ActionThirdPartyComplianceReportGet, ActionThirdPartyComplianceReportList, + ActionThirdPartyBusinessAssociateAgreementGet, + ActionThirdPartyDataPrivacyAgreementGet, + ActionThirdPartyRiskAssessmentList, ActionFrameworkGet, ActionFrameworkList, ActionControlGet, ActionControlList, ActionMeasureGet, ActionMeasureList, @@ -126,13 +126,13 @@ var AuditorPolicy = policy.NewPolicy( ).WithSID("org-read-access").When(organizationCondition), policy.Allow( - ActionVendorGet, ActionVendorList, - ActionVendorContactGet, ActionVendorContactList, - ActionVendorServiceGet, ActionVendorServiceList, - ActionVendorComplianceReportGet, ActionVendorComplianceReportList, - ActionVendorBusinessAssociateAgreementGet, - ActionVendorDataPrivacyAgreementGet, - ActionVendorRiskAssessmentList, + ActionThirdPartyGet, ActionThirdPartyList, + ActionThirdPartyContactGet, ActionThirdPartyContactList, + ActionThirdPartyServiceGet, ActionThirdPartyServiceList, + ActionThirdPartyComplianceReportGet, ActionThirdPartyComplianceReportList, + ActionThirdPartyBusinessAssociateAgreementGet, + ActionThirdPartyDataPrivacyAgreementGet, + ActionThirdPartyRiskAssessmentList, ActionFrameworkGet, ActionFrameworkList, ActionControlGet, ActionControlList, ActionMeasureGet, ActionMeasureList, diff --git a/pkg/probo/processing_activity_service.go b/pkg/probo/processing_activity_service.go index 0099d7702..06bcbc54c 100644 --- a/pkg/probo/processing_activity_service.go +++ b/pkg/probo/processing_activity_service.go @@ -52,7 +52,7 @@ type ( NextReviewDate *time.Time Role coredata.ProcessingActivityRole DataProtectionOfficerID *gid.GID - VendorIDs []gid.GID + ThirdPartyIDs []gid.GID } UpdateProcessingActivityRequest struct { @@ -76,7 +76,7 @@ type ( NextReviewDate **time.Time Role *coredata.ProcessingActivityRole DataProtectionOfficerID **gid.GID - VendorIDs *[]gid.GID + ThirdPartyIDs *[]gid.GID } ) @@ -101,8 +101,8 @@ func (cpar *CreateProcessingActivityRequest) Validate() error { v.Check(cpar.TransferImpactAssessmentNeeded, "transfer_impact_assessment_needed", validator.Required(), validator.OneOfSlice(coredata.ProcessingActivityTransferImpactAssessments())) v.Check(cpar.Role, "role", validator.Required(), validator.OneOfSlice(coredata.ProcessingActivityRoles())) v.Check(cpar.DataProtectionOfficerID, "data_protection_officer_id", validator.GID(coredata.MembershipProfileEntityType)) - v.CheckEach(cpar.VendorIDs, "vendor_ids", func(index int, item any) { - v.Check(item, fmt.Sprintf("vendor_ids[%d]", index), validator.Required(), validator.GID(coredata.VendorEntityType)) + v.CheckEach(cpar.ThirdPartyIDs, "third_party_ids", func(index int, item any) { + v.Check(item, fmt.Sprintf("third_party_ids[%d]", index), validator.Required(), validator.GID(coredata.ThirdPartyEntityType)) }) return v.Error() @@ -128,8 +128,8 @@ func (upar *UpdateProcessingActivityRequest) Validate() error { v.Check(upar.TransferImpactAssessmentNeeded, "transfer_impact_assessment_needed", validator.OneOfSlice(coredata.ProcessingActivityTransferImpactAssessments())) v.Check(upar.Role, "role", validator.OneOfSlice(coredata.ProcessingActivityRoles())) v.Check(upar.DataProtectionOfficerID, "data_protection_officer_id", validator.GID(coredata.MembershipProfileEntityType)) - v.CheckEach(upar.VendorIDs, "vendor_ids", func(index int, item any) { - v.Check(item, fmt.Sprintf("vendor_ids[%d]", index), validator.GID(coredata.VendorEntityType)) + v.CheckEach(upar.ThirdPartyIDs, "third_party_ids", func(index int, item any) { + v.Check(item, fmt.Sprintf("third_party_ids[%d]", index), validator.GID(coredata.ThirdPartyEntityType)) }) return v.Error() @@ -160,7 +160,7 @@ func (s *ProcessingActivityService) Create( req *CreateProcessingActivityRequest, ) (*coredata.ProcessingActivity, error) { now := time.Now() - processingActivityVendors := &coredata.ProcessingActivityVendors{} + processingActivityThirdParties := &coredata.ProcessingActivityThirdParties{} processingActivity := &coredata.ProcessingActivity{ ID: gid.New(s.svc.scope.GetTenantID(), coredata.ProcessingActivityEntityType), @@ -200,9 +200,9 @@ func (s *ProcessingActivityService) Create( return fmt.Errorf("cannot insert processing activity: %w", err) } - if len(req.VendorIDs) > 0 { - if err := processingActivityVendors.Insert(ctx, conn, s.svc.scope, processingActivity.ID, req.OrganizationID, req.VendorIDs); err != nil { - return fmt.Errorf("cannot create processing activity vendors: %w", err) + if len(req.ThirdPartyIDs) > 0 { + if err := processingActivityThirdParties.Insert(ctx, conn, s.svc.scope, processingActivity.ID, req.OrganizationID, req.ThirdPartyIDs); err != nil { + return fmt.Errorf("cannot create processing activity thirdParties: %w", err) } } @@ -222,7 +222,7 @@ func (s *ProcessingActivityService) Update( req *UpdateProcessingActivityRequest, ) (*coredata.ProcessingActivity, error) { processingActivity := &coredata.ProcessingActivity{} - processingActivityVendors := &coredata.ProcessingActivityVendors{} + processingActivityThirdParties := &coredata.ProcessingActivityThirdParties{} err := s.svc.pg.WithTx( ctx, @@ -295,9 +295,9 @@ func (s *ProcessingActivityService) Update( return fmt.Errorf("cannot update processing activity: %w", err) } - if req.VendorIDs != nil { - if err := processingActivityVendors.Merge(ctx, conn, s.svc.scope, processingActivity.ID, processingActivity.OrganizationID, *req.VendorIDs); err != nil { - return fmt.Errorf("cannot update processing activity vendors: %w", err) + if req.ThirdPartyIDs != nil { + if err := processingActivityThirdParties.Merge(ctx, conn, s.svc.scope, processingActivity.ID, processingActivity.OrganizationID, *req.ThirdPartyIDs); err != nil { + return fmt.Errorf("cannot update processing activity thirdParties: %w", err) } } diff --git a/pkg/probo/service.go b/pkg/probo/service.go index c95e49214..53e856d16 100644 --- a/pkg/probo/service.go +++ b/pkg/probo/service.go @@ -68,62 +68,62 @@ type ( esign *esign.Service connectorRegistry *connector.ConnectorRegistry invitationTokenValidity time.Duration - vendorAssessor VendorAssessor + thirdPartyAssessor ThirdPartyAssessor } TenantService struct { - pg *pg.Client - s3 *s3.Client - bucket string - encryptionKey cipher.EncryptionKey - scope coredata.Scoper - baseURL string - tokenSecret string - llmClient *llm.Client - llmModel string - llmTemperature float64 - llmMaxTokens int - vendorAssessor VendorAssessor - fileManager *filemanager.Service - esign *esign.Service - Frameworks *FrameworkService - Measures *MeasureService - Tasks *TaskService - Evidences *EvidenceService - Organizations *OrganizationService - Vendors *VendorService - Documents *DocumentService - DocumentApprovals *DocumentApprovalService - Controls *ControlService - Risks *RiskService - VendorComplianceReports *VendorComplianceReportService - VendorBusinessAssociateAgreements *VendorBusinessAssociateAgreementService - VendorContacts *VendorContactService - VendorDataPrivacyAgreements *VendorDataPrivacyAgreementService - VendorServices *VendorServiceService - Connectors *ConnectorService - Assets *AssetService - Data *DatumService - Audits *AuditService - WebhookSubscriptions *WebhookSubscriptionService - Reports *ReportService - TrustCenters *TrustCenterService - TrustCenterAccesses *TrustCenterAccessService - TrustCenterReferences *TrustCenterReferenceService - TrustCenterFiles *TrustCenterFileService - ComplianceFrameworks *ComplianceFrameworkService - ComplianceExternalURLs *ComplianceExternalURLService - Findings *FindingService - Obligations *ObligationService - RightsRequests *RightsRequestService - ProcessingActivities *ProcessingActivityService - DataProtectionImpactAssessments *DataProtectionImpactAssessmentService - TransferImpactAssessments *TransferImpactAssessmentService - StatementsOfApplicability *StatementOfApplicabilityService - GeneratedDocuments *GeneratedDocumentService - Files *FileService - CustomDomains *CustomDomainService - SlackMessages *slack.SlackMessageService + pg *pg.Client + s3 *s3.Client + bucket string + encryptionKey cipher.EncryptionKey + scope coredata.Scoper + baseURL string + tokenSecret string + llmClient *llm.Client + llmModel string + llmTemperature float64 + llmMaxTokens int + thirdPartyAssessor ThirdPartyAssessor + fileManager *filemanager.Service + esign *esign.Service + Frameworks *FrameworkService + Measures *MeasureService + Tasks *TaskService + Evidences *EvidenceService + Organizations *OrganizationService + ThirdParties *ThirdPartyService + Documents *DocumentService + DocumentApprovals *DocumentApprovalService + Controls *ControlService + Risks *RiskService + ThirdPartyComplianceReports *ThirdPartyComplianceReportService + ThirdPartyBusinessAssociateAgreements *ThirdPartyBusinessAssociateAgreementService + ThirdPartyContacts *ThirdPartyContactService + ThirdPartyDataPrivacyAgreements *ThirdPartyDataPrivacyAgreementService + ThirdPartyServices *ThirdPartyServiceService + Connectors *ConnectorService + Assets *AssetService + Data *DatumService + Audits *AuditService + WebhookSubscriptions *WebhookSubscriptionService + Reports *ReportService + TrustCenters *TrustCenterService + TrustCenterAccesses *TrustCenterAccessService + TrustCenterReferences *TrustCenterReferenceService + TrustCenterFiles *TrustCenterFileService + ComplianceFrameworks *ComplianceFrameworkService + ComplianceExternalURLs *ComplianceExternalURLService + Findings *FindingService + Obligations *ObligationService + RightsRequests *RightsRequestService + ProcessingActivities *ProcessingActivityService + DataProtectionImpactAssessments *DataProtectionImpactAssessmentService + TransferImpactAssessments *TransferImpactAssessmentService + StatementsOfApplicability *StatementOfApplicabilityService + GeneratedDocuments *GeneratedDocumentService + Files *FileService + CustomDomains *CustomDomainService + SlackMessages *slack.SlackMessageService } ) @@ -148,7 +148,7 @@ func NewService( esignService *esign.Service, connectorRegistry *connector.ConnectorRegistry, invitationTokenValidity time.Duration, - vendorAssessor VendorAssessor, + thirdPartyAssessor ThirdPartyAssessor, ) (*Service, error) { if bucket == "" { return nil, fmt.Errorf("bucket is required") @@ -175,7 +175,7 @@ func NewService( esign: esignService, connectorRegistry: connectorRegistry, invitationTokenValidity: invitationTokenValidity, - vendorAssessor: vendorAssessor, + thirdPartyAssessor: thirdPartyAssessor, } return svc, nil @@ -183,20 +183,20 @@ func NewService( func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService { tenantService := &TenantService{ - pg: s.pg, - s3: s.s3, - bucket: s.bucket, - encryptionKey: s.encryptionKey, - baseURL: s.baseURL, - scope: coredata.NewScope(tenantID), - tokenSecret: s.tokenSecret, - llmClient: s.llmClient, - llmModel: s.llmModel, - llmTemperature: s.llmTemperature, - llmMaxTokens: s.llmMaxTokens, - vendorAssessor: s.vendorAssessor, - fileManager: s.fileManager, - esign: s.esign, + pg: s.pg, + s3: s.s3, + bucket: s.bucket, + encryptionKey: s.encryptionKey, + baseURL: s.baseURL, + scope: coredata.NewScope(tenantID), + tokenSecret: s.tokenSecret, + llmClient: s.llmClient, + llmModel: s.llmModel, + llmTemperature: s.llmTemperature, + llmMaxTokens: s.llmMaxTokens, + thirdPartyAssessor: s.thirdPartyAssessor, + fileManager: s.fileManager, + esign: s.esign, } tenantService.Frameworks = &FrameworkService{ @@ -219,7 +219,7 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService { ), ), } - tenantService.Vendors = &VendorService{svc: tenantService} + tenantService.ThirdParties = &ThirdPartyService{svc: tenantService} tenantService.Documents = &DocumentService{ svc: tenantService, html2pdfConverter: s.html2pdfConverter, @@ -240,16 +240,16 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService { } tenantService.Controls = &ControlService{svc: tenantService} tenantService.Risks = &RiskService{svc: tenantService} - tenantService.VendorComplianceReports = &VendorComplianceReportService{ + tenantService.ThirdPartyComplianceReports = &ThirdPartyComplianceReportService{ svc: tenantService, fileValidator: filevalidation.NewValidator( filevalidation.WithCategories(filevalidation.CategoryDocument), ), } - tenantService.VendorBusinessAssociateAgreements = &VendorBusinessAssociateAgreementService{svc: tenantService} - tenantService.VendorContacts = &VendorContactService{svc: tenantService} - tenantService.VendorDataPrivacyAgreements = &VendorDataPrivacyAgreementService{svc: tenantService} - tenantService.VendorServices = &VendorServiceService{svc: tenantService} + tenantService.ThirdPartyBusinessAssociateAgreements = &ThirdPartyBusinessAssociateAgreementService{svc: tenantService} + tenantService.ThirdPartyContacts = &ThirdPartyContactService{svc: tenantService} + tenantService.ThirdPartyDataPrivacyAgreements = &ThirdPartyDataPrivacyAgreementService{svc: tenantService} + tenantService.ThirdPartyServices = &ThirdPartyServiceService{svc: tenantService} tenantService.Connectors = &ConnectorService{svc: tenantService} tenantService.Assets = &AssetService{svc: tenantService} tenantService.Data = &DatumService{svc: tenantService} diff --git a/pkg/probo/templates/asset_list.json.tmpl b/pkg/probo/templates/asset_list.json.tmpl index 84f1d439b..235aebfc1 100644 --- a/pkg/probo/templates/asset_list.json.tmpl +++ b/pkg/probo/templates/asset_list.json.tmpl @@ -8,7 +8,7 @@ }, { "type": "paragraph", - "content": [{ "type": "text", "text": "This document provides a comprehensive inventory of assets managed by the organization. It serves as a record of all assets, their types, quantities, data stored, ownership, and associated vendors." }] + "content": [{ "type": "text", "text": "This document provides a comprehensive inventory of assets managed by the organization. It serves as a record of all assets, their types, quantities, data stored, ownership, and associated third parties." }] }, { "type": "horizontalRule" }, { @@ -27,7 +27,7 @@ { "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [80] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Amount", "marks": [{ "type": "bold" }] }] }] }, { "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [120] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Data Types Stored", "marks": [{ "type": "bold" }] }] }] }, { "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [150] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Owner", "marks": [{ "type": "bold" }] }] }] }, - { "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [200] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Vendors", "marks": [{ "type": "bold" }] }] }] } + { "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [200] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Third parties", "marks": [{ "type": "bold" }] }] }] } ] }{{range .Rows}}, { @@ -38,7 +38,7 @@ { "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [80] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json (printf "%d" .Amount)}} }] }] }, { "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [120] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .DataTypesStored}} }] }] }, { "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [150] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .Owner}} }] }] }, - { "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [200] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .Vendors}} }] }] } + { "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [200] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .ThirdParties}} }] }] } ] }{{end}} ] @@ -73,11 +73,11 @@ { "type": "heading", "attrs": { "level": 3 }, - "content": [{ "type": "text", "text": "Vendors" }] + "content": [{ "type": "text", "text": "Third parties" }] }, { "type": "paragraph", - "content": [{ "type": "text", "text": "Third-party vendors that provide or support the asset." }] + "content": [{ "type": "text", "text": "Third parties that provide or support the asset." }] } ] } diff --git a/pkg/probo/templates/data_list.json.tmpl b/pkg/probo/templates/data_list.json.tmpl index a6840f8a4..e22807b10 100644 --- a/pkg/probo/templates/data_list.json.tmpl +++ b/pkg/probo/templates/data_list.json.tmpl @@ -8,7 +8,7 @@ }, { "type": "paragraph", - "content": [{ "type": "text", "text": "This document provides a comprehensive inventory of data assets managed by the organization. It serves as a record of all data items, their classification levels, ownership, and associated vendors." }] + "content": [{ "type": "text", "text": "This document provides a comprehensive inventory of data assets managed by the organization. It serves as a record of all data items, their classification levels, ownership, and associated third parties." }] }, { "type": "horizontalRule" }, { @@ -25,7 +25,7 @@ { "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [250] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Name", "marks": [{ "type": "bold" }] }] }] }, { "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [130] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Classification", "marks": [{ "type": "bold" }] }] }] }, { "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [180] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Owner", "marks": [{ "type": "bold" }] }] }] }, - { "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [250] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Vendors", "marks": [{ "type": "bold" }] }] }] } + { "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [250] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Third parties", "marks": [{ "type": "bold" }] }] }] } ] }{{range .Rows}}, { @@ -34,7 +34,7 @@ { "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [250] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .Name}} }] }] }, { "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [130] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .Classification}} }] }] }, { "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [180] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .Owner}} }] }] }, - { "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [250] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .Vendors}} }] }] } + { "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [250] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .ThirdParties}} }] }] } ] }{{end}} ] @@ -71,11 +71,11 @@ { "type": "heading", "attrs": { "level": 3 }, - "content": [{ "type": "text", "text": "Vendors" }] + "content": [{ "type": "text", "text": "Third parties" }] }, { "type": "paragraph", - "content": [{ "type": "text", "text": "Third-party vendors that process or have access to the data asset." }] + "content": [{ "type": "text", "text": "Third parties that process or have access to the data asset." }] } ] } diff --git a/pkg/probo/templates/processing_activity_list.json.tmpl b/pkg/probo/templates/processing_activity_list.json.tmpl index 6901ed28f..82de22db9 100644 --- a/pkg/probo/templates/processing_activity_list.json.tmpl +++ b/pkg/probo/templates/processing_activity_list.json.tmpl @@ -186,8 +186,8 @@ { "type": "paragraph", "content": [ - { "type": "text", "text": "Vendors: ", "marks": [{ "type": "bold" }] }, - { "type": "text", "text": {{json $r.Vendors}} } + { "type": "text", "text": "Third parties: ", "marks": [{ "type": "bold" }] }, + { "type": "text", "text": {{json $r.ThirdParties}} } ] }{{end}}, { "type": "horizontalRule" }, diff --git a/pkg/probo/templates/vendor_list.json.tmpl b/pkg/probo/templates/third_party_list.json.tmpl similarity index 89% rename from pkg/probo/templates/vendor_list.json.tmpl rename to pkg/probo/templates/third_party_list.json.tmpl index 170072277..6c02f5ec8 100644 --- a/pkg/probo/templates/vendor_list.json.tmpl +++ b/pkg/probo/templates/third_party_list.json.tmpl @@ -8,13 +8,13 @@ }, { "type": "paragraph", - "content": [{ "type": "text", "text": "This document provides a comprehensive register of all vendors used by the organization. It captures vendor profile information, services consumed, contacts, risk assessments, compliance reports, and contractual agreements (BAA, DPA) for each vendor." }] + "content": [{ "type": "text", "text": "This document provides a comprehensive register of all thirdParties used by the organization. It captures thirdParty profile information, services consumed, contacts, risk assessments, compliance reports, and contractual agreements (BAA, DPA) for each thirdParty." }] }, { "type": "horizontalRule" }, { "type": "heading", "attrs": { "level": 1 }, - "content": [{ "type": "text", "text": "2. Vendors" }] + "content": [{ "type": "text", "text": "2. ThirdParties" }] }{{range $i, $r := .Rows}}, { "type": "heading", @@ -272,11 +272,11 @@ { "type": "bulletList", "content": [ - { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "None: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "No sensitive data is shared with the vendor." }] }] }, - { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Low: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "The vendor processes low sensitivity data such as public information." }] }] }, - { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Medium: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "The vendor processes medium sensitivity data such as internal business data." }] }] }, - { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "High: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "The vendor processes high sensitivity data such as personal data, financial data, or trade secrets." }] }] }, - { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Critical: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "The vendor processes the most sensitive categories of data, where unauthorized disclosure would cause severe harm." }] }] } + { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "None: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "No sensitive data is shared with the thirdParty." }] }] }, + { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Low: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "The thirdParty processes low sensitivity data such as public information." }] }] }, + { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Medium: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "The thirdParty processes medium sensitivity data such as internal business data." }] }] }, + { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "High: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "The thirdParty processes high sensitivity data such as personal data, financial data, or trade secrets." }] }] }, + { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Critical: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "The thirdParty processes the most sensitive categories of data, where unauthorized disclosure would cause severe harm." }] }] } ] }, { @@ -287,10 +287,10 @@ { "type": "bulletList", "content": [ - { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Low: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "Limited disruption to operations if the vendor service is unavailable." }] }] }, - { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Medium: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "Significant disruption to operations if the vendor service is unavailable." }] }] }, - { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "High: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "Severe disruption or outage if the vendor service is unavailable." }] }] }, - { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Critical: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "Operations cannot continue if the vendor service is unavailable; immediate business-wide impact." }] }] } + { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Low: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "Limited disruption to operations if the thirdParty service is unavailable." }] }] }, + { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Medium: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "Significant disruption to operations if the thirdParty service is unavailable." }] }] }, + { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "High: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "Severe disruption or outage if the thirdParty service is unavailable." }] }] }, + { "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Critical: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "Operations cannot continue if the thirdParty service is unavailable; immediate business-wide impact." }] }] } ] } ] diff --git a/pkg/probo/vendor_business_associate_agreement_service.go b/pkg/probo/third_party_business_associate_agreement_service.go similarity index 52% rename from pkg/probo/vendor_business_associate_agreement_service.go rename to pkg/probo/third_party_business_associate_agreement_service.go index 1a5280fb1..24a763a27 100644 --- a/pkg/probo/vendor_business_associate_agreement_service.go +++ b/pkg/probo/third_party_business_associate_agreement_service.go @@ -32,24 +32,24 @@ import ( ) type ( - VendorBusinessAssociateAgreementService struct { + ThirdPartyBusinessAssociateAgreementService struct { svc *TenantService } - VendorBusinessAssociateAgreementCreateRequest struct { + ThirdPartyBusinessAssociateAgreementCreateRequest struct { File io.Reader ValidFrom *time.Time ValidUntil *time.Time FileName string } - VendorBusinessAssociateAgreementUpdateRequest struct { + ThirdPartyBusinessAssociateAgreementUpdateRequest struct { ValidFrom **time.Time ValidUntil **time.Time } ) -func (vbaacr *VendorBusinessAssociateAgreementCreateRequest) Validate() error { +func (vbaacr *ThirdPartyBusinessAssociateAgreementCreateRequest) Validate() error { v := validator.New() v.Check(vbaacr.FileName, "file_name", validator.SafeTextNoNewLine(TitleMaxLength)) @@ -58,7 +58,7 @@ func (vbaacr *VendorBusinessAssociateAgreementCreateRequest) Validate() error { return v.Error() } -func (vbaaur *VendorBusinessAssociateAgreementUpdateRequest) Validate() error { +func (vbaaur *ThirdPartyBusinessAssociateAgreementUpdateRequest) Validate() error { v := validator.New() v.Check(vbaaur.ValidUntil, "valid_until", validator.After(vbaaur.ValidFrom)) @@ -66,23 +66,23 @@ func (vbaaur *VendorBusinessAssociateAgreementUpdateRequest) Validate() error { return v.Error() } -func (s VendorBusinessAssociateAgreementService) GetByVendorID( +func (s ThirdPartyBusinessAssociateAgreementService) GetByThirdPartyID( ctx context.Context, - vendorID gid.GID, -) (*coredata.VendorBusinessAssociateAgreement, *coredata.File, error) { - var vendorBusinessAssociateAgreement *coredata.VendorBusinessAssociateAgreement + thirdPartyID gid.GID, +) (*coredata.ThirdPartyBusinessAssociateAgreement, *coredata.File, error) { + var thirdPartyBusinessAssociateAgreement *coredata.ThirdPartyBusinessAssociateAgreement var file *coredata.File err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - vendorBusinessAssociateAgreement = &coredata.VendorBusinessAssociateAgreement{} - if err := vendorBusinessAssociateAgreement.LoadByVendorID(ctx, conn, s.svc.scope, vendorID); err != nil { - return fmt.Errorf("cannot load vendor business associate agreement: %w", err) + thirdPartyBusinessAssociateAgreement = &coredata.ThirdPartyBusinessAssociateAgreement{} + if err := thirdPartyBusinessAssociateAgreement.LoadByThirdPartyID(ctx, conn, s.svc.scope, thirdPartyID); err != nil { + return fmt.Errorf("cannot load thirdParty business associate agreement: %w", err) } file = &coredata.File{} - if err := file.LoadByID(ctx, conn, s.svc.scope, vendorBusinessAssociateAgreement.FileID); err != nil { + if err := file.LoadByID(ctx, conn, s.svc.scope, thirdPartyBusinessAssociateAgreement.FileID); err != nil { return fmt.Errorf("cannot load file: %w", err) } @@ -94,14 +94,14 @@ func (s VendorBusinessAssociateAgreementService) GetByVendorID( return nil, nil, err } - return vendorBusinessAssociateAgreement, file, nil + return thirdPartyBusinessAssociateAgreement, file, nil } -func (s VendorBusinessAssociateAgreementService) Upload( +func (s ThirdPartyBusinessAssociateAgreementService) Upload( ctx context.Context, - vendorID gid.GID, - req *VendorBusinessAssociateAgreementCreateRequest, -) (*coredata.VendorBusinessAssociateAgreement, *coredata.File, error) { + thirdPartyID gid.GID, + req *ThirdPartyBusinessAssociateAgreementCreateRequest, +) (*coredata.ThirdPartyBusinessAssociateAgreement, *coredata.File, error) { if err := req.Validate(); err != nil { return nil, nil, err } @@ -111,15 +111,15 @@ func (s VendorBusinessAssociateAgreementService) Upload( return nil, nil, fmt.Errorf("cannot generate object key: %w", err) } - var vendorBusinessAssociateAgreement *coredata.VendorBusinessAssociateAgreement + var thirdPartyBusinessAssociateAgreement *coredata.ThirdPartyBusinessAssociateAgreement var file *coredata.File err = s.svc.pg.WithTx( ctx, func(ctx context.Context, conn pg.Tx) error { - vendor := &coredata.Vendor{} - if err := vendor.LoadByID(ctx, conn, s.svc.scope, vendorID); err != nil { - return fmt.Errorf("cannot load vendor: %w", err) + thirdParty := &coredata.ThirdParty{} + if err := thirdParty.LoadByID(ctx, conn, s.svc.scope, thirdPartyID); err != nil { + return fmt.Errorf("cannot load thirdParty: %w", err) } mimeType := mime.TypeByExtension(filepath.Ext(req.FileName)) @@ -131,9 +131,9 @@ func (s VendorBusinessAssociateAgreementService) Upload( ContentType: &mimeType, CacheControl: new("private, max-age=3600"), Metadata: map[string]string{ - "type": "vendor-business-associate-agreement", - "vendor-id": vendorID.String(), - "organization-id": vendor.OrganizationID.String(), + "type": "thirdParty-business-associate-agreement", + "thirdParty-id": thirdPartyID.String(), + "organization-id": thirdParty.OrganizationID.String(), }, }) if err != nil { @@ -150,7 +150,7 @@ func (s VendorBusinessAssociateAgreementService) Upload( now := time.Now() fileID := gid.New(s.svc.scope.GetTenantID(), coredata.FileEntityType) - vendorBusinessAssociateAgreementID := gid.New(s.svc.scope.GetTenantID(), coredata.VendorBusinessAssociateAgreementEntityType) + thirdPartyBusinessAssociateAgreementID := gid.New(s.svc.scope.GetTenantID(), coredata.ThirdPartyBusinessAssociateAgreementEntityType) file = &coredata.File{ ID: fileID, @@ -164,10 +164,10 @@ func (s VendorBusinessAssociateAgreementService) Upload( UpdatedAt: now, } - vendorBusinessAssociateAgreement = &coredata.VendorBusinessAssociateAgreement{ - ID: vendorBusinessAssociateAgreementID, - OrganizationID: vendor.OrganizationID, - VendorID: vendorID, + thirdPartyBusinessAssociateAgreement = &coredata.ThirdPartyBusinessAssociateAgreement{ + ID: thirdPartyBusinessAssociateAgreementID, + OrganizationID: thirdParty.OrganizationID, + ThirdPartyID: thirdPartyID, ValidFrom: req.ValidFrom, ValidUntil: req.ValidUntil, FileID: fileID, @@ -179,8 +179,8 @@ func (s VendorBusinessAssociateAgreementService) Upload( return fmt.Errorf("cannot insert file: %w", err) } - if err := vendorBusinessAssociateAgreement.Upsert(ctx, conn, s.svc.scope); err != nil { - return fmt.Errorf("cannot insert vendor business associate agreement: %w", err) + if err := thirdPartyBusinessAssociateAgreement.Upsert(ctx, conn, s.svc.scope); err != nil { + return fmt.Errorf("cannot insert thirdParty business associate agreement: %w", err) } return nil @@ -191,26 +191,26 @@ func (s VendorBusinessAssociateAgreementService) Upload( return nil, nil, err } - return vendorBusinessAssociateAgreement, file, nil + return thirdPartyBusinessAssociateAgreement, file, nil } -func (s VendorBusinessAssociateAgreementService) Get( +func (s ThirdPartyBusinessAssociateAgreementService) Get( ctx context.Context, - vendorBusinessAssociateAgreementID gid.GID, -) (*coredata.VendorBusinessAssociateAgreement, *coredata.File, error) { - var vendorBusinessAssociateAgreement *coredata.VendorBusinessAssociateAgreement + thirdPartyBusinessAssociateAgreementID gid.GID, +) (*coredata.ThirdPartyBusinessAssociateAgreement, *coredata.File, error) { + var thirdPartyBusinessAssociateAgreement *coredata.ThirdPartyBusinessAssociateAgreement var file *coredata.File err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - vendorBusinessAssociateAgreement = &coredata.VendorBusinessAssociateAgreement{} - if err := vendorBusinessAssociateAgreement.LoadByID(ctx, conn, s.svc.scope, vendorBusinessAssociateAgreementID); err != nil { - return fmt.Errorf("cannot load vendor business associate agreement: %w", err) + thirdPartyBusinessAssociateAgreement = &coredata.ThirdPartyBusinessAssociateAgreement{} + if err := thirdPartyBusinessAssociateAgreement.LoadByID(ctx, conn, s.svc.scope, thirdPartyBusinessAssociateAgreementID); err != nil { + return fmt.Errorf("cannot load thirdParty business associate agreement: %w", err) } file = &coredata.File{} - if err := file.LoadByID(ctx, conn, s.svc.scope, vendorBusinessAssociateAgreement.FileID); err != nil { + if err := file.LoadByID(ctx, conn, s.svc.scope, thirdPartyBusinessAssociateAgreement.FileID); err != nil { return fmt.Errorf("cannot load file: %w", err) } @@ -219,15 +219,15 @@ func (s VendorBusinessAssociateAgreementService) Get( ) if err != nil { - return nil, nil, fmt.Errorf("cannot load vendor business associate agreement: %w", err) + return nil, nil, fmt.Errorf("cannot load thirdParty business associate agreement: %w", err) } - return vendorBusinessAssociateAgreement, file, nil + return thirdPartyBusinessAssociateAgreement, file, nil } -func (s VendorBusinessAssociateAgreementService) GenerateFileURL( +func (s ThirdPartyBusinessAssociateAgreementService) GenerateFileURL( ctx context.Context, - vendorBusinessAssociateAgreementID gid.GID, + thirdPartyBusinessAssociateAgreementID gid.GID, expiresIn time.Duration, ) (string, error) { var file *coredata.File @@ -235,13 +235,13 @@ func (s VendorBusinessAssociateAgreementService) GenerateFileURL( err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - vendorBusinessAssociateAgreement := &coredata.VendorBusinessAssociateAgreement{} - if err := vendorBusinessAssociateAgreement.LoadByID(ctx, conn, s.svc.scope, vendorBusinessAssociateAgreementID); err != nil { - return fmt.Errorf("cannot load vendor business associate agreement: %w", err) + thirdPartyBusinessAssociateAgreement := &coredata.ThirdPartyBusinessAssociateAgreement{} + if err := thirdPartyBusinessAssociateAgreement.LoadByID(ctx, conn, s.svc.scope, thirdPartyBusinessAssociateAgreementID); err != nil { + return fmt.Errorf("cannot load thirdParty business associate agreement: %w", err) } file = &coredata.File{} - if err := file.LoadByID(ctx, conn, s.svc.scope, vendorBusinessAssociateAgreement.FileID); err != nil { + if err := file.LoadByID(ctx, conn, s.svc.scope, thirdPartyBusinessAssociateAgreement.FileID); err != nil { return fmt.Errorf("cannot load file: %w", err) } @@ -273,23 +273,23 @@ func (s VendorBusinessAssociateAgreementService) GenerateFileURL( return presignedReq.URL, nil } -func (s VendorBusinessAssociateAgreementService) Update( +func (s ThirdPartyBusinessAssociateAgreementService) Update( ctx context.Context, - vendorID gid.GID, - req *VendorBusinessAssociateAgreementUpdateRequest, -) (*coredata.VendorBusinessAssociateAgreement, *coredata.File, error) { + thirdPartyID gid.GID, + req *ThirdPartyBusinessAssociateAgreementUpdateRequest, +) (*coredata.ThirdPartyBusinessAssociateAgreement, *coredata.File, error) { if err := req.Validate(); err != nil { return nil, nil, err } - existingAgreement := &coredata.VendorBusinessAssociateAgreement{} + existingAgreement := &coredata.ThirdPartyBusinessAssociateAgreement{} file := &coredata.File{} err := s.svc.pg.WithTx( ctx, func(ctx context.Context, conn pg.Tx) error { - if err := existingAgreement.LoadByVendorID(ctx, conn, s.svc.scope, vendorID); err != nil { - return fmt.Errorf("cannot load existing vendor business associate agreement: %w", err) + if err := existingAgreement.LoadByThirdPartyID(ctx, conn, s.svc.scope, thirdPartyID); err != nil { + return fmt.Errorf("cannot load existing thirdParty business associate agreement: %w", err) } now := time.Now() @@ -303,7 +303,7 @@ func (s VendorBusinessAssociateAgreementService) Update( existingAgreement.UpdatedAt = now if err := existingAgreement.Update(ctx, conn, s.svc.scope); err != nil { - return fmt.Errorf("cannot update vendor business associate agreement: %w", err) + return fmt.Errorf("cannot update thirdParty business associate agreement: %w", err) } if err := file.LoadByID(ctx, conn, s.svc.scope, existingAgreement.FileID); err != nil { @@ -321,20 +321,20 @@ func (s VendorBusinessAssociateAgreementService) Update( return existingAgreement, file, nil } -func (s VendorBusinessAssociateAgreementService) Delete( +func (s ThirdPartyBusinessAssociateAgreementService) Delete( ctx context.Context, - vendorBusinessAssociateAgreementID gid.GID, + thirdPartyBusinessAssociateAgreementID gid.GID, ) error { return s.svc.pg.WithTx( ctx, func(ctx context.Context, conn pg.Tx) error { - vendorBusinessAssociateAgreement := &coredata.VendorBusinessAssociateAgreement{} - if err := vendorBusinessAssociateAgreement.LoadByID(ctx, conn, s.svc.scope, vendorBusinessAssociateAgreementID); err != nil { - return fmt.Errorf("cannot load vendor business associate agreement: %w", err) + thirdPartyBusinessAssociateAgreement := &coredata.ThirdPartyBusinessAssociateAgreement{} + if err := thirdPartyBusinessAssociateAgreement.LoadByID(ctx, conn, s.svc.scope, thirdPartyBusinessAssociateAgreementID); err != nil { + return fmt.Errorf("cannot load thirdParty business associate agreement: %w", err) } - if err := vendorBusinessAssociateAgreement.Delete(ctx, conn, s.svc.scope); err != nil { - return fmt.Errorf("cannot delete vendor business associate agreement: %w", err) + if err := thirdPartyBusinessAssociateAgreement.Delete(ctx, conn, s.svc.scope); err != nil { + return fmt.Errorf("cannot delete thirdParty business associate agreement: %w", err) } return nil @@ -342,20 +342,20 @@ func (s VendorBusinessAssociateAgreementService) Delete( ) } -func (s VendorBusinessAssociateAgreementService) DeleteByVendorID( +func (s ThirdPartyBusinessAssociateAgreementService) DeleteByThirdPartyID( ctx context.Context, - vendorID gid.GID, + thirdPartyID gid.GID, ) error { return s.svc.pg.WithTx( ctx, func(ctx context.Context, conn pg.Tx) error { - vendorBusinessAssociateAgreement := &coredata.VendorBusinessAssociateAgreement{} - if err := vendorBusinessAssociateAgreement.LoadByVendorID(ctx, conn, s.svc.scope, vendorID); err != nil { - return fmt.Errorf("cannot load vendor business associate agreement: %w", err) + thirdPartyBusinessAssociateAgreement := &coredata.ThirdPartyBusinessAssociateAgreement{} + if err := thirdPartyBusinessAssociateAgreement.LoadByThirdPartyID(ctx, conn, s.svc.scope, thirdPartyID); err != nil { + return fmt.Errorf("cannot load thirdParty business associate agreement: %w", err) } - if err := vendorBusinessAssociateAgreement.DeleteByVendorID(ctx, conn, s.svc.scope, vendorID); err != nil { - return fmt.Errorf("cannot delete vendor business associate agreement: %w", err) + if err := thirdPartyBusinessAssociateAgreement.DeleteByThirdPartyID(ctx, conn, s.svc.scope, thirdPartyID); err != nil { + return fmt.Errorf("cannot delete thirdParty business associate agreement: %w", err) } return nil diff --git a/pkg/probo/vendor_compliance_report_service.go b/pkg/probo/third_party_compliance_report_service.go similarity index 51% rename from pkg/probo/vendor_compliance_report_service.go rename to pkg/probo/third_party_compliance_report_service.go index 14510d4be..8bc9ac38b 100644 --- a/pkg/probo/vendor_compliance_report_service.go +++ b/pkg/probo/third_party_compliance_report_service.go @@ -28,12 +28,12 @@ import ( ) type ( - VendorComplianceReportService struct { + ThirdPartyComplianceReportService struct { svc *TenantService fileValidator *filevalidation.FileValidator } - VendorComplianceReportCreateRequest struct { + ThirdPartyComplianceReportCreateRequest struct { File FileUpload ReportDate time.Time ValidUntil *time.Time @@ -41,7 +41,7 @@ type ( } ) -func (vcrcr *VendorComplianceReportCreateRequest) Validate() error { +func (vcrcr *ThirdPartyComplianceReportCreateRequest) Validate() error { v := validator.New() v.Check(vcrcr.ReportName, "report_name", validator.SafeTextNoNewLine(TitleMaxLength)) @@ -49,17 +49,17 @@ func (vcrcr *VendorComplianceReportCreateRequest) Validate() error { return v.Error() } -func (s VendorComplianceReportService) ListForVendorID( +func (s ThirdPartyComplianceReportService) ListForThirdPartyID( ctx context.Context, - vendorID gid.GID, - cursor *page.Cursor[coredata.VendorComplianceReportOrderField], -) (*page.Page[*coredata.VendorComplianceReport, coredata.VendorComplianceReportOrderField], error) { - var vendorComplianceReports coredata.VendorComplianceReports + thirdPartyID gid.GID, + cursor *page.Cursor[coredata.ThirdPartyComplianceReportOrderField], +) (*page.Page[*coredata.ThirdPartyComplianceReport, coredata.ThirdPartyComplianceReportOrderField], error) { + var thirdPartyComplianceReports coredata.ThirdPartyComplianceReports err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - return vendorComplianceReports.LoadForVendorID(ctx, conn, s.svc.scope, vendorID, cursor) + return thirdPartyComplianceReports.LoadForThirdPartyID(ctx, conn, s.svc.scope, thirdPartyID, cursor) }, ) @@ -67,30 +67,30 @@ func (s VendorComplianceReportService) ListForVendorID( return nil, err } - return page.NewPage(vendorComplianceReports, cursor), nil + return page.NewPage(thirdPartyComplianceReports, cursor), nil } -func (s VendorComplianceReportService) Upload( +func (s ThirdPartyComplianceReportService) Upload( ctx context.Context, - vendorID gid.GID, - req *VendorComplianceReportCreateRequest, -) (*coredata.VendorComplianceReport, error) { + thirdPartyID gid.GID, + req *ThirdPartyComplianceReportCreateRequest, +) (*coredata.ThirdPartyComplianceReport, error) { if err := req.Validate(); err != nil { return nil, err } - vendor, err := s.svc.Vendors.Get(ctx, vendorID) + thirdParty, err := s.svc.ThirdParties.Get(ctx, thirdPartyID) if err != nil { - return nil, fmt.Errorf("cannot get vendor: %w", err) + return nil, fmt.Errorf("cannot get thirdParty: %w", err) } f, err := s.svc.Files.UploadAndSaveFile( ctx, s.fileValidator, map[string]string{ - "type": "vendor-compliance-report", - "vendor-id": vendorID.String(), - "organization-id": vendor.OrganizationID.String(), + "type": "thirdParty-compliance-report", + "thirdParty-id": thirdPartyID.String(), + "organization-id": thirdParty.OrganizationID.String(), }, &req.File) @@ -100,12 +100,12 @@ func (s VendorComplianceReportService) Upload( now := time.Now() - vendorComplianceReportID := gid.New(s.svc.scope.GetTenantID(), coredata.VendorComplianceReportEntityType) + thirdPartyComplianceReportID := gid.New(s.svc.scope.GetTenantID(), coredata.ThirdPartyComplianceReportEntityType) - vendorComplianceReport := &coredata.VendorComplianceReport{ - ID: vendorComplianceReportID, - OrganizationID: vendor.OrganizationID, - VendorID: vendorID, + thirdPartyComplianceReport := &coredata.ThirdPartyComplianceReport{ + ID: thirdPartyComplianceReportID, + OrganizationID: thirdParty.OrganizationID, + ThirdPartyID: thirdPartyID, ReportDate: req.ReportDate, ValidUntil: req.ValidUntil, ReportName: req.ReportName, @@ -117,7 +117,7 @@ func (s VendorComplianceReportService) Upload( err = s.svc.pg.WithTx( ctx, func(ctx context.Context, tx pg.Tx) error { - return vendorComplianceReport.Insert(ctx, tx, s.svc.scope) + return thirdPartyComplianceReport.Insert(ctx, tx, s.svc.scope) }, ) @@ -125,39 +125,39 @@ func (s VendorComplianceReportService) Upload( return nil, err } - return vendorComplianceReport, nil + return thirdPartyComplianceReport, nil } -func (s VendorComplianceReportService) Get( +func (s ThirdPartyComplianceReportService) Get( ctx context.Context, - vendorComplianceReportID gid.GID, -) (*coredata.VendorComplianceReport, error) { - vendorComplianceReport := &coredata.VendorComplianceReport{} + thirdPartyComplianceReportID gid.GID, +) (*coredata.ThirdPartyComplianceReport, error) { + thirdPartyComplianceReport := &coredata.ThirdPartyComplianceReport{} err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - return vendorComplianceReport.LoadByID(ctx, conn, s.svc.scope, vendorComplianceReportID) + return thirdPartyComplianceReport.LoadByID(ctx, conn, s.svc.scope, thirdPartyComplianceReportID) }, ) if err != nil { - return nil, fmt.Errorf("cannot load vendor compliance report: %w", err) + return nil, fmt.Errorf("cannot load thirdParty compliance report: %w", err) } - return vendorComplianceReport, nil + return thirdPartyComplianceReport, nil } -func (s VendorComplianceReportService) Delete( +func (s ThirdPartyComplianceReportService) Delete( ctx context.Context, - vendorComplianceReportID gid.GID, + thirdPartyComplianceReportID gid.GID, ) error { - vendorComplianceReport := &coredata.VendorComplianceReport{ID: vendorComplianceReportID} + thirdPartyComplianceReport := &coredata.ThirdPartyComplianceReport{ID: thirdPartyComplianceReportID} err := s.svc.pg.WithTx( ctx, func(ctx context.Context, tx pg.Tx) error { - if err := vendorComplianceReport.Delete(ctx, tx, s.svc.scope); err != nil { + if err := thirdPartyComplianceReport.Delete(ctx, tx, s.svc.scope); err != nil { return err } @@ -166,7 +166,7 @@ func (s VendorComplianceReportService) Delete( ) if err != nil { - return fmt.Errorf("cannot delete vendor compliance report: %w", err) + return fmt.Errorf("cannot delete thirdParty compliance report: %w", err) } return nil diff --git a/pkg/probo/third_party_contact_service.go b/pkg/probo/third_party_contact_service.go new file mode 100644 index 000000000..8e625195a --- /dev/null +++ b/pkg/probo/third_party_contact_service.go @@ -0,0 +1,232 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package probo + +import ( + "context" + "fmt" + "time" + + "go.gearno.de/kit/pg" + "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/mail" + "go.probo.inc/probo/pkg/page" + "go.probo.inc/probo/pkg/validator" +) + +type ( + ThirdPartyContactService struct { + svc *TenantService + } + + CreateThirdPartyContactRequest struct { + ThirdPartyID gid.GID + FullName *string + Email *mail.Addr + Phone *string + Role *string + } + + UpdateThirdPartyContactRequest struct { + ID gid.GID + FullName **string + Email **mail.Addr + Phone **string + Role **string + } +) + +func (cvcr *CreateThirdPartyContactRequest) Validate() error { + v := validator.New() + + v.Check(cvcr.ThirdPartyID, "third_party_id", validator.Required(), validator.GID(coredata.ThirdPartyEntityType)) + v.Check(cvcr.FullName, "fullName", validator.SafeTextNoNewLine(TitleMaxLength)) + v.Check(cvcr.Phone, "phone", validator.SafeText(NameMaxLength)) + v.Check(cvcr.Role, "role", validator.SafeText(TitleMaxLength)) + + return v.Error() +} + +func (uvcr *UpdateThirdPartyContactRequest) Validate() error { + v := validator.New() + + v.Check(uvcr.ID, "id", validator.Required(), validator.GID(coredata.ThirdPartyContactEntityType)) + v.Check(uvcr.FullName, "fullName", validator.SafeTextNoNewLine(TitleMaxLength)) + v.Check(uvcr.Phone, "phone", validator.SafeText(NameMaxLength)) + v.Check(uvcr.Role, "role", validator.SafeText(TitleMaxLength)) + + return v.Error() +} + +func (s ThirdPartyContactService) Get( + ctx context.Context, + thirdPartyContactID gid.GID, +) (*coredata.ThirdPartyContact, error) { + thirdPartyContact := &coredata.ThirdPartyContact{} + + err := s.svc.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + err := thirdPartyContact.LoadByID(ctx, conn, s.svc.scope, thirdPartyContactID) + if err != nil { + return fmt.Errorf("cannot load thirdParty contact: %w", err) + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + return thirdPartyContact, nil +} + +func (s ThirdPartyContactService) List( + ctx context.Context, + thirdPartyID gid.GID, + cursor *page.Cursor[coredata.ThirdPartyContactOrderField], +) (*page.Page[*coredata.ThirdPartyContact, coredata.ThirdPartyContactOrderField], error) { + var thirdPartyContacts coredata.ThirdPartyContacts + + err := s.svc.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + err := thirdPartyContacts.LoadByThirdPartyID(ctx, conn, s.svc.scope, thirdPartyID, cursor) + if err != nil { + return fmt.Errorf("cannot load thirdParty contacts: %w", err) + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + return page.NewPage(thirdPartyContacts, cursor), nil +} + +func (s ThirdPartyContactService) Create( + ctx context.Context, + req CreateThirdPartyContactRequest, +) (*coredata.ThirdPartyContact, error) { + if err := req.Validate(); err != nil { + return nil, err + } + + now := time.Now() + thirdPartyContact := &coredata.ThirdPartyContact{ + ID: gid.New(s.svc.scope.GetTenantID(), coredata.ThirdPartyContactEntityType), + ThirdPartyID: req.ThirdPartyID, + FullName: req.FullName, + Email: req.Email, + Phone: req.Phone, + Role: req.Role, + CreatedAt: now, + UpdatedAt: now, + } + + err := s.svc.pg.WithTx( + ctx, + func(ctx context.Context, conn pg.Tx) error { + thirdParty := &coredata.ThirdParty{} + if err := thirdParty.LoadByID(ctx, conn, s.svc.scope, req.ThirdPartyID); err != nil { + return fmt.Errorf("cannot load thirdParty: %w", err) + } + + thirdPartyContact.OrganizationID = thirdParty.OrganizationID + + if err := thirdPartyContact.Insert(ctx, conn, s.svc.scope); err != nil { + return fmt.Errorf("cannot insert thirdParty contact: %w", err) + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + return thirdPartyContact, nil +} + +func (s ThirdPartyContactService) Update( + ctx context.Context, + req UpdateThirdPartyContactRequest, +) (*coredata.ThirdPartyContact, error) { + if err := req.Validate(); err != nil { + return nil, err + } + + thirdPartyContact := &coredata.ThirdPartyContact{} + + err := s.svc.pg.WithTx( + ctx, + func(ctx context.Context, conn pg.Tx) error { + err := thirdPartyContact.LoadByID(ctx, conn, s.svc.scope, req.ID) + if err != nil { + return fmt.Errorf("cannot load thirdParty contact: %w", err) + } + + if req.FullName != nil { + thirdPartyContact.FullName = *req.FullName + } + if req.Email != nil { + thirdPartyContact.Email = *req.Email + } + if req.Phone != nil { + thirdPartyContact.Phone = *req.Phone + } + if req.Role != nil { + thirdPartyContact.Role = *req.Role + } + thirdPartyContact.UpdatedAt = time.Now() + + return thirdPartyContact.Update(ctx, conn, s.svc.scope) + }, + ) + + if err != nil { + return nil, err + } + + return thirdPartyContact, nil +} + +func (s ThirdPartyContactService) Delete( + ctx context.Context, + thirdPartyContactID gid.GID, +) error { + thirdPartyContact := coredata.ThirdPartyContact{ID: thirdPartyContactID} + return s.svc.pg.WithTx( + ctx, + func(ctx context.Context, conn pg.Tx) error { + if err := thirdPartyContact.LoadByID(ctx, conn, s.svc.scope, thirdPartyContactID); err != nil { + return fmt.Errorf("cannot load thirdParty contact: %w", err) + } + + if err := thirdPartyContact.Delete(ctx, conn, s.svc.scope); err != nil { + return fmt.Errorf("cannot delete thirdParty contact: %w", err) + } + + return nil + }, + ) +} diff --git a/pkg/probo/vendor_data_privacy_agreement_service.go b/pkg/probo/third_party_data_privacy_agreement_service.go similarity index 53% rename from pkg/probo/vendor_data_privacy_agreement_service.go rename to pkg/probo/third_party_data_privacy_agreement_service.go index 64fa866d0..ec9c28095 100644 --- a/pkg/probo/vendor_data_privacy_agreement_service.go +++ b/pkg/probo/third_party_data_privacy_agreement_service.go @@ -32,24 +32,24 @@ import ( ) type ( - VendorDataPrivacyAgreementService struct { + ThirdPartyDataPrivacyAgreementService struct { svc *TenantService } - VendorDataPrivacyAgreementCreateRequest struct { + ThirdPartyDataPrivacyAgreementCreateRequest struct { File io.Reader ValidFrom *time.Time ValidUntil *time.Time FileName string } - VendorDataPrivacyAgreementUpdateRequest struct { + ThirdPartyDataPrivacyAgreementUpdateRequest struct { ValidFrom **time.Time ValidUntil **time.Time } ) -func (vdpacr *VendorDataPrivacyAgreementCreateRequest) Validate() error { +func (vdpacr *ThirdPartyDataPrivacyAgreementCreateRequest) Validate() error { v := validator.New() v.Check(vdpacr.FileName, "file_name", validator.SafeTextNoNewLine(TitleMaxLength)) @@ -58,7 +58,7 @@ func (vdpacr *VendorDataPrivacyAgreementCreateRequest) Validate() error { return v.Error() } -func (vdpaur *VendorDataPrivacyAgreementUpdateRequest) Validate() error { +func (vdpaur *ThirdPartyDataPrivacyAgreementUpdateRequest) Validate() error { v := validator.New() v.Check(vdpaur.ValidUntil, "valid_until", validator.After(vdpaur.ValidFrom)) @@ -66,23 +66,23 @@ func (vdpaur *VendorDataPrivacyAgreementUpdateRequest) Validate() error { return v.Error() } -func (s VendorDataPrivacyAgreementService) GetByVendorID( +func (s ThirdPartyDataPrivacyAgreementService) GetByThirdPartyID( ctx context.Context, - vendorID gid.GID, -) (*coredata.VendorDataPrivacyAgreement, *coredata.File, error) { - var vendorDataPrivacyAgreement *coredata.VendorDataPrivacyAgreement + thirdPartyID gid.GID, +) (*coredata.ThirdPartyDataPrivacyAgreement, *coredata.File, error) { + var thirdPartyDataPrivacyAgreement *coredata.ThirdPartyDataPrivacyAgreement var file *coredata.File err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - vendorDataPrivacyAgreement = &coredata.VendorDataPrivacyAgreement{} - if err := vendorDataPrivacyAgreement.LoadByVendorID(ctx, conn, s.svc.scope, vendorID); err != nil { - return fmt.Errorf("cannot load vendor data privacy agreement: %w", err) + thirdPartyDataPrivacyAgreement = &coredata.ThirdPartyDataPrivacyAgreement{} + if err := thirdPartyDataPrivacyAgreement.LoadByThirdPartyID(ctx, conn, s.svc.scope, thirdPartyID); err != nil { + return fmt.Errorf("cannot load thirdParty data privacy agreement: %w", err) } file = &coredata.File{} - if err := file.LoadByID(ctx, conn, s.svc.scope, vendorDataPrivacyAgreement.FileID); err != nil { + if err := file.LoadByID(ctx, conn, s.svc.scope, thirdPartyDataPrivacyAgreement.FileID); err != nil { return fmt.Errorf("cannot load file: %w", err) } @@ -94,14 +94,14 @@ func (s VendorDataPrivacyAgreementService) GetByVendorID( return nil, nil, err } - return vendorDataPrivacyAgreement, file, nil + return thirdPartyDataPrivacyAgreement, file, nil } -func (s VendorDataPrivacyAgreementService) Upload( +func (s ThirdPartyDataPrivacyAgreementService) Upload( ctx context.Context, - vendorID gid.GID, - req *VendorDataPrivacyAgreementCreateRequest, -) (*coredata.VendorDataPrivacyAgreement, *coredata.File, error) { + thirdPartyID gid.GID, + req *ThirdPartyDataPrivacyAgreementCreateRequest, +) (*coredata.ThirdPartyDataPrivacyAgreement, *coredata.File, error) { if err := req.Validate(); err != nil { return nil, nil, err } @@ -111,16 +111,16 @@ func (s VendorDataPrivacyAgreementService) Upload( return nil, nil, fmt.Errorf("cannot generate object key: %w", err) } - var vendorDataPrivacyAgreement *coredata.VendorDataPrivacyAgreement + var thirdPartyDataPrivacyAgreement *coredata.ThirdPartyDataPrivacyAgreement var file *coredata.File - var vendor *coredata.Vendor + var thirdParty *coredata.ThirdParty err = s.svc.pg.WithTx( ctx, func(ctx context.Context, conn pg.Tx) error { - vendor = &coredata.Vendor{} - if err := vendor.LoadByID(ctx, conn, s.svc.scope, vendorID); err != nil { - return fmt.Errorf("cannot load vendor: %w", err) + thirdParty = &coredata.ThirdParty{} + if err := thirdParty.LoadByID(ctx, conn, s.svc.scope, thirdPartyID); err != nil { + return fmt.Errorf("cannot load thirdParty: %w", err) } mimeType := mime.TypeByExtension(filepath.Ext(req.FileName)) @@ -131,9 +131,9 @@ func (s VendorDataPrivacyAgreementService) Upload( ContentType: &mimeType, CacheControl: new("private, max-age=3600"), Metadata: map[string]string{ - "type": "vendor-data-privacy-agreement", - "vendor-id": vendorID.String(), - "organization-id": vendor.OrganizationID.String(), + "type": "thirdParty-data-privacy-agreement", + "thirdParty-id": thirdPartyID.String(), + "organization-id": thirdParty.OrganizationID.String(), }, }) if err != nil { @@ -149,7 +149,7 @@ func (s VendorDataPrivacyAgreementService) Upload( now := time.Now() fileID := gid.New(s.svc.scope.GetTenantID(), coredata.FileEntityType) - vendorDataPrivacyAgreementID := gid.New(s.svc.scope.GetTenantID(), coredata.VendorDataPrivacyAgreementEntityType) + thirdPartyDataPrivacyAgreementID := gid.New(s.svc.scope.GetTenantID(), coredata.ThirdPartyDataPrivacyAgreementEntityType) file = &coredata.File{ ID: fileID, BucketName: s.svc.bucket, @@ -162,10 +162,10 @@ func (s VendorDataPrivacyAgreementService) Upload( UpdatedAt: now, } - vendorDataPrivacyAgreement = &coredata.VendorDataPrivacyAgreement{ - ID: vendorDataPrivacyAgreementID, - OrganizationID: vendor.OrganizationID, - VendorID: vendorID, + thirdPartyDataPrivacyAgreement = &coredata.ThirdPartyDataPrivacyAgreement{ + ID: thirdPartyDataPrivacyAgreementID, + OrganizationID: thirdParty.OrganizationID, + ThirdPartyID: thirdPartyID, ValidFrom: req.ValidFrom, ValidUntil: req.ValidUntil, FileID: fileID, @@ -177,8 +177,8 @@ func (s VendorDataPrivacyAgreementService) Upload( return fmt.Errorf("cannot insert file: %w", err) } - if err := vendorDataPrivacyAgreement.Upsert(ctx, conn, s.svc.scope); err != nil { - return fmt.Errorf("cannot insert vendor data privacy agreement: %w", err) + if err := thirdPartyDataPrivacyAgreement.Upsert(ctx, conn, s.svc.scope); err != nil { + return fmt.Errorf("cannot insert thirdParty data privacy agreement: %w", err) } return nil @@ -189,26 +189,26 @@ func (s VendorDataPrivacyAgreementService) Upload( return nil, nil, err } - return vendorDataPrivacyAgreement, file, nil + return thirdPartyDataPrivacyAgreement, file, nil } -func (s VendorDataPrivacyAgreementService) Get( +func (s ThirdPartyDataPrivacyAgreementService) Get( ctx context.Context, - vendorDataPrivacyAgreementID gid.GID, -) (*coredata.VendorDataPrivacyAgreement, *coredata.File, error) { - var vendorDataPrivacyAgreement *coredata.VendorDataPrivacyAgreement + thirdPartyDataPrivacyAgreementID gid.GID, +) (*coredata.ThirdPartyDataPrivacyAgreement, *coredata.File, error) { + var thirdPartyDataPrivacyAgreement *coredata.ThirdPartyDataPrivacyAgreement var file *coredata.File err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - vendorDataPrivacyAgreement = &coredata.VendorDataPrivacyAgreement{} - if err := vendorDataPrivacyAgreement.LoadByID(ctx, conn, s.svc.scope, vendorDataPrivacyAgreementID); err != nil { - return fmt.Errorf("cannot load vendor data privacy agreement: %w", err) + thirdPartyDataPrivacyAgreement = &coredata.ThirdPartyDataPrivacyAgreement{} + if err := thirdPartyDataPrivacyAgreement.LoadByID(ctx, conn, s.svc.scope, thirdPartyDataPrivacyAgreementID); err != nil { + return fmt.Errorf("cannot load thirdParty data privacy agreement: %w", err) } file = &coredata.File{} - if err := file.LoadByID(ctx, conn, s.svc.scope, vendorDataPrivacyAgreement.FileID); err != nil { + if err := file.LoadByID(ctx, conn, s.svc.scope, thirdPartyDataPrivacyAgreement.FileID); err != nil { return fmt.Errorf("cannot load file: %w", err) } @@ -217,15 +217,15 @@ func (s VendorDataPrivacyAgreementService) Get( ) if err != nil { - return nil, nil, fmt.Errorf("cannot load vendor data privacy agreement: %w", err) + return nil, nil, fmt.Errorf("cannot load thirdParty data privacy agreement: %w", err) } - return vendorDataPrivacyAgreement, file, nil + return thirdPartyDataPrivacyAgreement, file, nil } -func (s VendorDataPrivacyAgreementService) GenerateFileURL( +func (s ThirdPartyDataPrivacyAgreementService) GenerateFileURL( ctx context.Context, - vendorDataPrivacyAgreementID gid.GID, + thirdPartyDataPrivacyAgreementID gid.GID, expiresIn time.Duration, ) (string, error) { var file *coredata.File @@ -233,13 +233,13 @@ func (s VendorDataPrivacyAgreementService) GenerateFileURL( err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - vendorDataPrivacyAgreement := &coredata.VendorDataPrivacyAgreement{} - if err := vendorDataPrivacyAgreement.LoadByID(ctx, conn, s.svc.scope, vendorDataPrivacyAgreementID); err != nil { - return fmt.Errorf("cannot load vendor data privacy agreement: %w", err) + thirdPartyDataPrivacyAgreement := &coredata.ThirdPartyDataPrivacyAgreement{} + if err := thirdPartyDataPrivacyAgreement.LoadByID(ctx, conn, s.svc.scope, thirdPartyDataPrivacyAgreementID); err != nil { + return fmt.Errorf("cannot load thirdParty data privacy agreement: %w", err) } file = &coredata.File{} - if err := file.LoadByID(ctx, conn, s.svc.scope, vendorDataPrivacyAgreement.FileID); err != nil { + if err := file.LoadByID(ctx, conn, s.svc.scope, thirdPartyDataPrivacyAgreement.FileID); err != nil { return fmt.Errorf("cannot load file: %w", err) } @@ -271,23 +271,23 @@ func (s VendorDataPrivacyAgreementService) GenerateFileURL( return presignedReq.URL, nil } -func (s VendorDataPrivacyAgreementService) Update( +func (s ThirdPartyDataPrivacyAgreementService) Update( ctx context.Context, - vendorID gid.GID, - req *VendorDataPrivacyAgreementUpdateRequest, -) (*coredata.VendorDataPrivacyAgreement, *coredata.File, error) { + thirdPartyID gid.GID, + req *ThirdPartyDataPrivacyAgreementUpdateRequest, +) (*coredata.ThirdPartyDataPrivacyAgreement, *coredata.File, error) { if err := req.Validate(); err != nil { return nil, nil, err } - existingAgreement := &coredata.VendorDataPrivacyAgreement{} + existingAgreement := &coredata.ThirdPartyDataPrivacyAgreement{} file := &coredata.File{} err := s.svc.pg.WithTx( ctx, func(ctx context.Context, conn pg.Tx) error { - if err := existingAgreement.LoadByVendorID(ctx, conn, s.svc.scope, vendorID); err != nil { - return fmt.Errorf("cannot load existing vendor data privacy agreement: %w", err) + if err := existingAgreement.LoadByThirdPartyID(ctx, conn, s.svc.scope, thirdPartyID); err != nil { + return fmt.Errorf("cannot load existing thirdParty data privacy agreement: %w", err) } now := time.Now() @@ -301,7 +301,7 @@ func (s VendorDataPrivacyAgreementService) Update( existingAgreement.UpdatedAt = now if err := existingAgreement.Update(ctx, conn, s.svc.scope); err != nil { - return fmt.Errorf("cannot update vendor data privacy agreement: %w", err) + return fmt.Errorf("cannot update thirdParty data privacy agreement: %w", err) } if err := file.LoadByID(ctx, conn, s.svc.scope, existingAgreement.FileID); err != nil { @@ -319,20 +319,20 @@ func (s VendorDataPrivacyAgreementService) Update( return existingAgreement, file, nil } -func (s VendorDataPrivacyAgreementService) Delete( +func (s ThirdPartyDataPrivacyAgreementService) Delete( ctx context.Context, - vendorDataPrivacyAgreementID gid.GID, + thirdPartyDataPrivacyAgreementID gid.GID, ) error { return s.svc.pg.WithTx( ctx, func(ctx context.Context, conn pg.Tx) error { - vendorDataPrivacyAgreement := &coredata.VendorDataPrivacyAgreement{} - if err := vendorDataPrivacyAgreement.LoadByID(ctx, conn, s.svc.scope, vendorDataPrivacyAgreementID); err != nil { - return fmt.Errorf("cannot load vendor data privacy agreement: %w", err) + thirdPartyDataPrivacyAgreement := &coredata.ThirdPartyDataPrivacyAgreement{} + if err := thirdPartyDataPrivacyAgreement.LoadByID(ctx, conn, s.svc.scope, thirdPartyDataPrivacyAgreementID); err != nil { + return fmt.Errorf("cannot load thirdParty data privacy agreement: %w", err) } - if err := vendorDataPrivacyAgreement.Delete(ctx, conn, s.svc.scope); err != nil { - return fmt.Errorf("cannot delete vendor data privacy agreement: %w", err) + if err := thirdPartyDataPrivacyAgreement.Delete(ctx, conn, s.svc.scope); err != nil { + return fmt.Errorf("cannot delete thirdParty data privacy agreement: %w", err) } return nil @@ -340,20 +340,20 @@ func (s VendorDataPrivacyAgreementService) Delete( ) } -func (s VendorDataPrivacyAgreementService) DeleteByVendorID( +func (s ThirdPartyDataPrivacyAgreementService) DeleteByThirdPartyID( ctx context.Context, - vendorID gid.GID, + thirdPartyID gid.GID, ) error { return s.svc.pg.WithTx( ctx, func(ctx context.Context, conn pg.Tx) error { - vendorDataPrivacyAgreement := &coredata.VendorDataPrivacyAgreement{} - if err := vendorDataPrivacyAgreement.LoadByVendorID(ctx, conn, s.svc.scope, vendorID); err != nil { - return fmt.Errorf("cannot load vendor data privacy agreement: %w", err) + thirdPartyDataPrivacyAgreement := &coredata.ThirdPartyDataPrivacyAgreement{} + if err := thirdPartyDataPrivacyAgreement.LoadByThirdPartyID(ctx, conn, s.svc.scope, thirdPartyID); err != nil { + return fmt.Errorf("cannot load thirdParty data privacy agreement: %w", err) } - if err := vendorDataPrivacyAgreement.DeleteByVendorID(ctx, conn, s.svc.scope, vendorID); err != nil { - return fmt.Errorf("cannot delete vendor data privacy agreement: %w", err) + if err := thirdPartyDataPrivacyAgreement.DeleteByThirdPartyID(ctx, conn, s.svc.scope, thirdPartyID); err != nil { + return fmt.Errorf("cannot delete thirdParty data privacy agreement: %w", err) } return nil diff --git a/pkg/probo/vendor_service.go b/pkg/probo/third_party_service.go similarity index 59% rename from pkg/probo/vendor_service.go rename to pkg/probo/third_party_service.go index a79cfddc2..087a0c4d2 100644 --- a/pkg/probo/vendor_service.go +++ b/pkg/probo/third_party_service.go @@ -32,16 +32,16 @@ import ( webhooktypes "go.probo.inc/probo/pkg/webhook/types" ) -// ErrVendorAssessmentDisabled is returned by VendorAssessor.Assess when the -// deployment has not configured an LLM provider for vendor assessment. -var ErrVendorAssessmentDisabled = errors.New("vendor assessment is not configured on this deployment") +// ErrThirdPartyAssessmentDisabled is returned by ThirdPartyAssessor.Assess when the +// deployment has not configured an LLM provider for thirdParty assessment. +var ErrThirdPartyAssessmentDisabled = errors.New("thirdParty assessment is not configured on this deployment") -// VendorAssessor produces a vendor assessment report from a website URL and +// ThirdPartyAssessor produces a thirdParty assessment report from a website URL and // an optional procedure description. Implementations that cannot perform // assessment (missing LLM credentials, misconfigured provider) must return -// ErrVendorAssessmentDisabled from Assess so callers can surface a stable +// ErrThirdPartyAssessmentDisabled from Assess so callers can surface a stable // "feature unavailable" error instead of a generic internal error. -type VendorAssessor interface { +type ThirdPartyAssessor interface { Assess( ctx context.Context, websiteURL string, @@ -50,35 +50,35 @@ type VendorAssessor interface { ) (*vetting.Result, error) } -// DisabledVendorAssessor is the VendorAssessor implementation used when no -// LLM provider is configured for the vendor-assessor agent. Its Assess -// method always returns ErrVendorAssessmentDisabled. -type DisabledVendorAssessor struct{} +// DisabledThirdPartyAssessor is the ThirdPartyAssessor implementation used when no +// LLM provider is configured for the third-party-assessor agent. Its Assess +// method always returns ErrThirdPartyAssessmentDisabled. +type DisabledThirdPartyAssessor struct{} -var _ VendorAssessor = DisabledVendorAssessor{} +var _ ThirdPartyAssessor = DisabledThirdPartyAssessor{} -func (DisabledVendorAssessor) Assess( +func (DisabledThirdPartyAssessor) Assess( _ context.Context, _ string, _ string, _ agent.ProgressReporter, ) (*vetting.Result, error) { - return nil, ErrVendorAssessmentDisabled + return nil, ErrThirdPartyAssessmentDisabled } type ( - VendorService struct { + ThirdPartyService struct { svc *TenantService } - CreateVendorRequest struct { + CreateThirdPartyRequest struct { OrganizationID gid.GID Name string Description *string HeadquarterAddress *string LegalName *string WebsiteURL *string - Category *coredata.VendorCategory + Category *coredata.ThirdPartyCategory PrivacyPolicyURL *string ServiceLevelAgreementURL *string DataProcessingAgreementURL *string @@ -94,7 +94,7 @@ type ( SecurityOwnerID *gid.GID } - UpdateVendorRequest struct { + UpdateThirdPartyRequest struct { ID gid.GID Name *string Description **string @@ -102,7 +102,7 @@ type ( LegalName **string WebsiteURL **string TermsOfServiceURL **string - Category *coredata.VendorCategory + Category *coredata.ThirdPartyCategory PrivacyPolicyURL **string ServiceLevelAgreementURL **string DataProcessingAgreementURL **string @@ -118,14 +118,14 @@ type ( ShowOnTrustCenter *bool } - AssessVendorRequest struct { + AssessThirdPartyRequest struct { ID gid.GID WebsiteURL string Procedure *string } - AssessVendorResult struct { - Vendor *coredata.Vendor + AssessThirdPartyResult struct { + ThirdParty *coredata.ThirdParty Report string Subprocessors []Subprocessor } @@ -136,8 +136,8 @@ type ( Purpose string } - CreateVendorRiskAssessmentRequest struct { - VendorID gid.GID + CreateThirdPartyRiskAssessmentRequest struct { + ThirdPartyID gid.GID ExpiresAt time.Time DataSensitivity coredata.DataSensitivity BusinessImpact coredata.BusinessImpact @@ -145,7 +145,7 @@ type ( } ) -func (cvr *CreateVendorRequest) Validate() error { +func (cvr *CreateThirdPartyRequest) Validate() error { v := validator.New() v.Check(cvr.OrganizationID, "organization_id", validator.Required(), validator.GID(coredata.OrganizationEntityType)) @@ -154,7 +154,7 @@ func (cvr *CreateVendorRequest) Validate() error { v.Check(cvr.HeadquarterAddress, "headquarter_address", validator.SafeText(ContentMaxLength)) v.Check(cvr.LegalName, "cvr.LegalName", validator.SafeTextNoNewLine(TitleMaxLength)) v.Check(cvr.WebsiteURL, "website_url", validator.SafeText(2048)) - v.Check(cvr.Category, "category", validator.OneOfSlice(coredata.VendorCategories())) + v.Check(cvr.Category, "category", validator.OneOfSlice(coredata.ThirdPartyCategories())) v.Check(cvr.PrivacyPolicyURL, "privacy_policy_url", validator.SafeText(2048)) v.Check(cvr.ServiceLevelAgreementURL, "service_level_agreement_url", validator.SafeText(2048)) v.Check(cvr.DataProcessingAgreementURL, "data_processing_agreement_url", validator.SafeText(2048)) @@ -170,16 +170,16 @@ func (cvr *CreateVendorRequest) Validate() error { return v.Error() } -func (uvr *UpdateVendorRequest) Validate() error { +func (uvr *UpdateThirdPartyRequest) Validate() error { v := validator.New() - v.Check(uvr.ID, "id", validator.Required(), validator.GID(coredata.VendorEntityType)) + v.Check(uvr.ID, "id", validator.Required(), validator.GID(coredata.ThirdPartyEntityType)) v.Check(uvr.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength)) v.Check(uvr.Description, "description", validator.SafeText(ContentMaxLength)) v.Check(uvr.HeadquarterAddress, "headquarter_address", validator.SafeText(ContentMaxLength)) v.Check(uvr.LegalName, "uvr.LegalName", validator.SafeTextNoNewLine(TitleMaxLength)) v.Check(uvr.WebsiteURL, "website_url", validator.SafeText(2048)) - v.Check(uvr.Category, "category", validator.OneOfSlice(coredata.VendorCategories())) + v.Check(uvr.Category, "category", validator.OneOfSlice(coredata.ThirdPartyCategories())) v.Check(uvr.PrivacyPolicyURL, "privacy_policy_url", validator.SafeText(2048)) v.Check(uvr.ServiceLevelAgreementURL, "service_level_agreement_url", validator.SafeText(2048)) v.Check(uvr.DataProcessingAgreementURL, "data_processing_agreement_url", validator.SafeText(2048)) @@ -195,10 +195,10 @@ func (uvr *UpdateVendorRequest) Validate() error { return v.Error() } -func (cvrar *CreateVendorRiskAssessmentRequest) Validate() error { +func (cvrar *CreateThirdPartyRiskAssessmentRequest) Validate() error { v := validator.New() - v.Check(cvrar.VendorID, "vendor_id", validator.Required(), validator.GID(coredata.VendorEntityType)) + v.Check(cvrar.ThirdPartyID, "third_party_id", validator.Required(), validator.GID(coredata.ThirdPartyEntityType)) v.Check(cvrar.DataSensitivity, "data_sensitivity", validator.Required(), validator.OneOfSlice(coredata.DataSensitivities())) v.Check(cvrar.BusinessImpact, "business_impact", validator.Required(), validator.OneOfSlice(coredata.BusinessImpacts())) v.Check(cvrar.Notes, "notes", validator.SafeText(ContentMaxLength)) @@ -206,7 +206,7 @@ func (cvrar *CreateVendorRiskAssessmentRequest) Validate() error { return v.Error() } -func (s VendorService) CountForOrganizationID( +func (s ThirdPartyService) CountForOrganizationID( ctx context.Context, organizationID gid.GID, ) (int, error) { @@ -215,11 +215,11 @@ func (s VendorService) CountForOrganizationID( err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) (err error) { - vendors := coredata.Vendors{} - filter := &coredata.VendorFilter{} - count, err = vendors.CountByOrganizationID(ctx, conn, s.svc.scope, organizationID, filter) + thirdParties := coredata.ThirdParties{} + filter := &coredata.ThirdPartyFilter{} + count, err = thirdParties.CountByOrganizationID(ctx, conn, s.svc.scope, organizationID, filter) if err != nil { - return fmt.Errorf("cannot count vendors: %w", err) + return fmt.Errorf("cannot count thirdParties: %w", err) } return nil @@ -233,13 +233,13 @@ func (s VendorService) CountForOrganizationID( return count, nil } -func (s VendorService) ListForOrganizationID( +func (s ThirdPartyService) ListForOrganizationID( ctx context.Context, organizationID gid.GID, - cursor *page.Cursor[coredata.VendorOrderField], - filter *coredata.VendorFilter, -) (*page.Page[*coredata.Vendor, coredata.VendorOrderField], error) { - var vendors coredata.Vendors + cursor *page.Cursor[coredata.ThirdPartyOrderField], + filter *coredata.ThirdPartyFilter, +) (*page.Page[*coredata.ThirdParty, coredata.ThirdPartyOrderField], error) { + var thirdParties coredata.ThirdParties organization := &coredata.Organization{} err := s.svc.pg.WithConn( @@ -249,7 +249,7 @@ func (s VendorService) ListForOrganizationID( return fmt.Errorf("cannot load organization: %w", err) } - return vendors.LoadByOrganizationID( + return thirdParties.LoadByOrganizationID( ctx, conn, s.svc.scope, @@ -264,10 +264,10 @@ func (s VendorService) ListForOrganizationID( return nil, err } - return page.NewPage(vendors, cursor), nil + return page.NewPage(thirdParties, cursor), nil } -func (s VendorService) CountForDatumID( +func (s ThirdPartyService) CountForDatumID( ctx context.Context, datumID gid.GID, ) (int, error) { @@ -276,10 +276,10 @@ func (s VendorService) CountForDatumID( err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) (err error) { - vendors := coredata.Vendors{} - count, err = vendors.CountByDatumID(ctx, conn, s.svc.scope, datumID) + thirdParties := coredata.ThirdParties{} + count, err = thirdParties.CountByDatumID(ctx, conn, s.svc.scope, datumID) if err != nil { - return fmt.Errorf("cannot count vendors: %w", err) + return fmt.Errorf("cannot count thirdParties: %w", err) } return nil @@ -293,17 +293,17 @@ func (s VendorService) CountForDatumID( return count, nil } -func (s VendorService) ListForDatumID( +func (s ThirdPartyService) ListForDatumID( ctx context.Context, datumID gid.GID, - cursor *page.Cursor[coredata.VendorOrderField], -) (*page.Page[*coredata.Vendor, coredata.VendorOrderField], error) { - var vendors coredata.Vendors + cursor *page.Cursor[coredata.ThirdPartyOrderField], +) (*page.Page[*coredata.ThirdParty, coredata.ThirdPartyOrderField], error) { + var thirdParties coredata.ThirdParties err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - return vendors.LoadByDatumID( + return thirdParties.LoadByDatumID( ctx, conn, s.svc.scope, @@ -317,102 +317,102 @@ func (s VendorService) ListForDatumID( return nil, err } - return page.NewPage(vendors, cursor), nil + return page.NewPage(thirdParties, cursor), nil } -func (s VendorService) Update( +func (s ThirdPartyService) Update( ctx context.Context, - req UpdateVendorRequest, -) (*coredata.Vendor, error) { + req UpdateThirdPartyRequest, +) (*coredata.ThirdParty, error) { if err := req.Validate(); err != nil { return nil, err } - vendor := &coredata.Vendor{} + thirdParty := &coredata.ThirdParty{} err := s.svc.pg.WithTx( ctx, func(ctx context.Context, conn pg.Tx) error { - if err := vendor.LoadByID(ctx, conn, s.svc.scope, req.ID); err != nil { - return fmt.Errorf("cannot load vendor %q: %w", req.ID, err) + if err := thirdParty.LoadByID(ctx, conn, s.svc.scope, req.ID); err != nil { + return fmt.Errorf("cannot load thirdParty %q: %w", req.ID, err) } if req.Name != nil { - vendor.Name = *req.Name + thirdParty.Name = *req.Name } if req.Description != nil { - vendor.Description = *req.Description + thirdParty.Description = *req.Description } if req.StatusPageURL != nil { - vendor.StatusPageURL = *req.StatusPageURL + thirdParty.StatusPageURL = *req.StatusPageURL } if req.TermsOfServiceURL != nil { - vendor.TermsOfServiceURL = *req.TermsOfServiceURL + thirdParty.TermsOfServiceURL = *req.TermsOfServiceURL } if req.PrivacyPolicyURL != nil { - vendor.PrivacyPolicyURL = *req.PrivacyPolicyURL + thirdParty.PrivacyPolicyURL = *req.PrivacyPolicyURL } if req.ServiceLevelAgreementURL != nil { - vendor.ServiceLevelAgreementURL = *req.ServiceLevelAgreementURL + thirdParty.ServiceLevelAgreementURL = *req.ServiceLevelAgreementURL } if req.DataProcessingAgreementURL != nil { - vendor.DataProcessingAgreementURL = *req.DataProcessingAgreementURL + thirdParty.DataProcessingAgreementURL = *req.DataProcessingAgreementURL } if req.BusinessAssociateAgreementURL != nil { - vendor.BusinessAssociateAgreementURL = *req.BusinessAssociateAgreementURL + thirdParty.BusinessAssociateAgreementURL = *req.BusinessAssociateAgreementURL } if req.SubprocessorsListURL != nil { - vendor.SubprocessorsListURL = *req.SubprocessorsListURL + thirdParty.SubprocessorsListURL = *req.SubprocessorsListURL } if req.Category != nil { - vendor.Category = *req.Category + thirdParty.Category = *req.Category } else { - vendor.Category = coredata.VendorCategoryOther + thirdParty.Category = coredata.ThirdPartyCategoryOther } if req.SecurityPageURL != nil { - vendor.SecurityPageURL = *req.SecurityPageURL + thirdParty.SecurityPageURL = *req.SecurityPageURL } if req.ShowOnTrustCenter != nil { - vendor.ShowOnTrustCenter = *req.ShowOnTrustCenter + thirdParty.ShowOnTrustCenter = *req.ShowOnTrustCenter } if req.TrustPageURL != nil { - vendor.TrustPageURL = *req.TrustPageURL + thirdParty.TrustPageURL = *req.TrustPageURL } if req.HeadquarterAddress != nil { - vendor.HeadquarterAddress = *req.HeadquarterAddress + thirdParty.HeadquarterAddress = *req.HeadquarterAddress } if req.LegalName != nil { - vendor.LegalName = *req.LegalName + thirdParty.LegalName = *req.LegalName } if req.WebsiteURL != nil { - vendor.WebsiteURL = *req.WebsiteURL + thirdParty.WebsiteURL = *req.WebsiteURL } if req.TermsOfServiceURL != nil { - vendor.TermsOfServiceURL = *req.TermsOfServiceURL + thirdParty.TermsOfServiceURL = *req.TermsOfServiceURL } if req.Certifications != nil { - vendor.Certifications = req.Certifications + thirdParty.Certifications = req.Certifications } if req.Countries != nil { - vendor.Countries = req.Countries + thirdParty.Countries = req.Countries } if req.BusinessOwnerID != nil { @@ -421,9 +421,9 @@ func (s VendorService) Update( if err := businessOwner.LoadByID(ctx, conn, s.svc.scope, **req.BusinessOwnerID); err != nil { return fmt.Errorf("cannot load business owner profile: %w", err) } - vendor.BusinessOwnerID = &businessOwner.ID + thirdParty.BusinessOwnerID = &businessOwner.ID } else { - vendor.BusinessOwnerID = nil + thirdParty.BusinessOwnerID = nil } } @@ -433,25 +433,25 @@ func (s VendorService) Update( if err := securityOwner.LoadByID(ctx, conn, s.svc.scope, **req.SecurityOwnerID); err != nil { return fmt.Errorf("cannot load security owner profile: %w", err) } - vendor.SecurityOwnerID = &securityOwner.ID + thirdParty.SecurityOwnerID = &securityOwner.ID } else { - vendor.SecurityOwnerID = nil + thirdParty.SecurityOwnerID = nil } } - vendor.UpdatedAt = time.Now() + thirdParty.UpdatedAt = time.Now() - if err := vendor.Update(ctx, conn, s.svc.scope); err != nil { - return fmt.Errorf("cannot update vendor: %w", err) + if err := thirdParty.Update(ctx, conn, s.svc.scope); err != nil { + return fmt.Errorf("cannot update thirdParty: %w", err) } if err := webhook.InsertData( ctx, conn, s.svc.scope, - vendor.OrganizationID, - coredata.WebhookEventTypeVendorUpdated, - webhooktypes.NewVendor(vendor), + thirdParty.OrganizationID, + coredata.WebhookEventTypeThirdPartyUpdated, + webhooktypes.NewThirdParty(thirdParty), ); err != nil { return fmt.Errorf("cannot insert webhook event: %w", err) } @@ -464,19 +464,19 @@ func (s VendorService) Update( return nil, err } - return vendor, nil + return thirdParty, nil } -func (s VendorService) Get( +func (s ThirdPartyService) Get( ctx context.Context, - vendorID gid.GID, -) (*coredata.Vendor, error) { - vendor := &coredata.Vendor{} + thirdPartyID gid.GID, +) (*coredata.ThirdParty, error) { + thirdParty := &coredata.ThirdParty{} err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - return vendor.LoadByID(ctx, conn, s.svc.scope, vendorID) + return thirdParty.LoadByID(ctx, conn, s.svc.scope, thirdPartyID) }, ) @@ -484,25 +484,25 @@ func (s VendorService) Get( return nil, err } - return vendor, nil + return thirdParty, nil } -func (s VendorService) GetByIDs( +func (s ThirdPartyService) GetByIDs( ctx context.Context, - vendorIDs ...gid.GID, -) (coredata.Vendors, error) { - var vendors coredata.Vendors + thirdPartyIDs ...gid.GID, +) (coredata.ThirdParties, error) { + var thirdParties coredata.ThirdParties err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - if err := vendors.LoadByIDs( + if err := thirdParties.LoadByIDs( ctx, conn, s.svc.scope, - vendorIDs, + thirdPartyIDs, ); err != nil { - return fmt.Errorf("cannot load vendors by ids: %w", err) + return fmt.Errorf("cannot load thirdParties by ids: %w", err) } return nil @@ -512,49 +512,49 @@ func (s VendorService) GetByIDs( return nil, err } - return vendors, nil + return thirdParties, nil } -func (s VendorService) Delete( +func (s ThirdPartyService) Delete( ctx context.Context, - vendorID gid.GID, + thirdPartyID gid.GID, ) error { - vendor := &coredata.Vendor{} + thirdParty := &coredata.ThirdParty{} return s.svc.pg.WithTx( ctx, func(ctx context.Context, conn pg.Tx) error { - if err := vendor.LoadByID(ctx, conn, s.svc.scope, vendorID); err != nil { - return fmt.Errorf("cannot load vendor: %w", err) + if err := thirdParty.LoadByID(ctx, conn, s.svc.scope, thirdPartyID); err != nil { + return fmt.Errorf("cannot load thirdParty: %w", err) } if err := webhook.InsertData( ctx, conn, s.svc.scope, - vendor.OrganizationID, - coredata.WebhookEventTypeVendorDeleted, - webhooktypes.NewVendor(vendor), + thirdParty.OrganizationID, + coredata.WebhookEventTypeThirdPartyDeleted, + webhooktypes.NewThirdParty(thirdParty), ); err != nil { return fmt.Errorf("cannot insert webhook event: %w", err) } - return vendor.Delete(ctx, conn, s.svc.scope) + return thirdParty.Delete(ctx, conn, s.svc.scope) }, ) } -func (s VendorService) Create( +func (s ThirdPartyService) Create( ctx context.Context, - req CreateVendorRequest, -) (*coredata.Vendor, error) { + req CreateThirdPartyRequest, +) (*coredata.ThirdParty, error) { if err := req.Validate(); err != nil { return nil, err } now := time.Now() - vendor := &coredata.Vendor{ - ID: gid.New(s.svc.scope.GetTenantID(), coredata.VendorEntityType), + thirdParty := &coredata.ThirdParty{ + ID: gid.New(s.svc.scope.GetTenantID(), coredata.ThirdPartyEntityType), Name: req.Name, CreatedAt: now, UpdatedAt: now, @@ -584,14 +584,14 @@ func (s VendorService) Create( return fmt.Errorf("cannot load organization %q: %w", req.OrganizationID, err) } - vendor.OrganizationID = organization.ID + thirdParty.OrganizationID = organization.ID if req.BusinessOwnerID != nil { businessOwner := &coredata.MembershipProfile{} if err := businessOwner.LoadByID(ctx, conn, s.svc.scope, *req.BusinessOwnerID); err != nil { return fmt.Errorf("cannot load business owner profile: %w", err) } - vendor.BusinessOwnerID = &businessOwner.ID + thirdParty.BusinessOwnerID = &businessOwner.ID } if req.SecurityOwnerID != nil { @@ -599,17 +599,17 @@ func (s VendorService) Create( if err := securityOwner.LoadByID(ctx, conn, s.svc.scope, *req.SecurityOwnerID); err != nil { return fmt.Errorf("cannot load security owner profile: %w", err) } - vendor.SecurityOwnerID = &securityOwner.ID + thirdParty.SecurityOwnerID = &securityOwner.ID } if req.Category != nil { - vendor.Category = *req.Category + thirdParty.Category = *req.Category } else { - vendor.Category = coredata.VendorCategoryOther + thirdParty.Category = coredata.ThirdPartyCategoryOther } - if err := vendor.Insert(ctx, conn, s.svc.scope); err != nil { - return fmt.Errorf("cannot insert vendor: %w", err) + if err := thirdParty.Insert(ctx, conn, s.svc.scope); err != nil { + return fmt.Errorf("cannot insert thirdParty: %w", err) } if err := webhook.InsertData( @@ -617,8 +617,8 @@ func (s VendorService) Create( conn, s.svc.scope, organization.ID, - coredata.WebhookEventTypeVendorCreated, - webhooktypes.NewVendor(vendor), + coredata.WebhookEventTypeThirdPartyCreated, + webhooktypes.NewThirdParty(thirdParty), ); err != nil { return fmt.Errorf("cannot insert webhook event: %w", err) } @@ -631,10 +631,10 @@ func (s VendorService) Create( return nil, err } - return vendor, nil + return thirdParty, nil } -func (s VendorService) CountForAssetID( +func (s ThirdPartyService) CountForAssetID( ctx context.Context, assetID gid.GID, ) (int, error) { @@ -643,10 +643,10 @@ func (s VendorService) CountForAssetID( err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) (err error) { - vendors := coredata.Vendors{} - count, err = vendors.CountByAssetID(ctx, conn, s.svc.scope, assetID) + thirdParties := coredata.ThirdParties{} + count, err = thirdParties.CountByAssetID(ctx, conn, s.svc.scope, assetID) if err != nil { - return fmt.Errorf("cannot count vendors: %w", err) + return fmt.Errorf("cannot count thirdParties: %w", err) } return nil @@ -660,17 +660,17 @@ func (s VendorService) CountForAssetID( return count, nil } -func (s VendorService) ListForAssetID( +func (s ThirdPartyService) ListForAssetID( ctx context.Context, assetID gid.GID, - cursor *page.Cursor[coredata.VendorOrderField], -) (*page.Page[*coredata.Vendor, coredata.VendorOrderField], error) { - var vendors coredata.Vendors + cursor *page.Cursor[coredata.ThirdPartyOrderField], +) (*page.Page[*coredata.ThirdParty, coredata.ThirdPartyOrderField], error) { + var thirdParties coredata.ThirdParties err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - return vendors.LoadByAssetID(ctx, conn, s.svc.scope, assetID, cursor) + return thirdParties.LoadByAssetID(ctx, conn, s.svc.scope, assetID, cursor) }, ) @@ -678,22 +678,22 @@ func (s VendorService) ListForAssetID( return nil, err } - return page.NewPage(vendors, cursor), nil + return page.NewPage(thirdParties, cursor), nil } -func (s VendorService) ListForProcessingActivityID( +func (s ThirdPartyService) ListForProcessingActivityID( ctx context.Context, processingActivityID gid.GID, - cursor *page.Cursor[coredata.VendorOrderField], -) (*page.Page[*coredata.Vendor, coredata.VendorOrderField], error) { - var vendors coredata.Vendors + cursor *page.Cursor[coredata.ThirdPartyOrderField], +) (*page.Page[*coredata.ThirdParty, coredata.ThirdPartyOrderField], error) { + var thirdParties coredata.ThirdParties err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - err := vendors.LoadByProcessingActivityID(ctx, conn, s.svc.scope, processingActivityID, cursor) + err := thirdParties.LoadByProcessingActivityID(ctx, conn, s.svc.scope, processingActivityID, cursor) if err != nil { - return fmt.Errorf("cannot load vendors by processing activity: %w", err) + return fmt.Errorf("cannot load thirdParties by processing activity: %w", err) } return nil @@ -704,20 +704,20 @@ func (s VendorService) ListForProcessingActivityID( return nil, err } - return page.NewPage(vendors, cursor), nil + return page.NewPage(thirdParties, cursor), nil } -func (s VendorService) ListRiskAssessments( +func (s ThirdPartyService) ListRiskAssessments( ctx context.Context, - vendorID gid.GID, - cursor *page.Cursor[coredata.VendorRiskAssessmentOrderField], -) (*page.Page[*coredata.VendorRiskAssessment, coredata.VendorRiskAssessmentOrderField], error) { - var vendorRiskAssessments coredata.VendorRiskAssessments + thirdPartyID gid.GID, + cursor *page.Cursor[coredata.ThirdPartyRiskAssessmentOrderField], +) (*page.Page[*coredata.ThirdPartyRiskAssessment, coredata.ThirdPartyRiskAssessmentOrderField], error) { + var thirdPartyRiskAssessments coredata.ThirdPartyRiskAssessments err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - return vendorRiskAssessments.LoadByVendorID(ctx, conn, s.svc.scope, vendorID, cursor) + return thirdPartyRiskAssessments.LoadByThirdPartyID(ctx, conn, s.svc.scope, thirdPartyID, cursor) }, ) @@ -725,24 +725,24 @@ func (s VendorService) ListRiskAssessments( return nil, err } - return page.NewPage(vendorRiskAssessments, cursor), nil + return page.NewPage(thirdPartyRiskAssessments, cursor), nil } -func (s VendorService) CreateRiskAssessment( +func (s ThirdPartyService) CreateRiskAssessment( ctx context.Context, - req CreateVendorRiskAssessmentRequest, -) (*coredata.VendorRiskAssessment, error) { + req CreateThirdPartyRiskAssessmentRequest, +) (*coredata.ThirdPartyRiskAssessment, error) { if err := req.Validate(); err != nil { return nil, err } - vendorRiskAssessmentID := gid.New(s.svc.scope.GetTenantID(), coredata.VendorRiskAssessmentEntityType) + thirdPartyRiskAssessmentID := gid.New(s.svc.scope.GetTenantID(), coredata.ThirdPartyRiskAssessmentEntityType) now := time.Now() - vendorRiskAssessment := &coredata.VendorRiskAssessment{ - ID: vendorRiskAssessmentID, - VendorID: req.VendorID, + thirdPartyRiskAssessment := &coredata.ThirdPartyRiskAssessment{ + ID: thirdPartyRiskAssessmentID, + ThirdPartyID: req.ThirdPartyID, ExpiresAt: req.ExpiresAt, DataSensitivity: req.DataSensitivity, BusinessImpact: req.BusinessImpact, @@ -758,19 +758,19 @@ func (s VendorService) CreateRiskAssessment( err := s.svc.pg.WithTx( ctx, func(ctx context.Context, tx pg.Tx) error { - vendor := coredata.Vendor{} - if err := vendor.LoadByID(ctx, tx, s.svc.scope, req.VendorID); err != nil { - return fmt.Errorf("cannot load vendor: %w", err) + thirdParty := coredata.ThirdParty{} + if err := thirdParty.LoadByID(ctx, tx, s.svc.scope, req.ThirdPartyID); err != nil { + return fmt.Errorf("cannot load thirdParty: %w", err) } - vendorRiskAssessment.OrganizationID = vendor.OrganizationID + thirdPartyRiskAssessment.OrganizationID = thirdParty.OrganizationID - if err := vendor.ExpireNonExpiredRiskAssessments(ctx, tx, s.svc.scope); err != nil { - return fmt.Errorf("cannot expire vendor risk assessments: %w", err) + if err := thirdParty.ExpireNonExpiredRiskAssessments(ctx, tx, s.svc.scope); err != nil { + return fmt.Errorf("cannot expire thirdParty risk assessments: %w", err) } - if err := vendorRiskAssessment.Insert(ctx, tx, s.svc.scope); err != nil { - return fmt.Errorf("cannot insert vendor risk assessment: %w", err) + if err := thirdPartyRiskAssessment.Insert(ctx, tx, s.svc.scope); err != nil { + return fmt.Errorf("cannot insert thirdParty risk assessment: %w", err) } return nil }, @@ -780,19 +780,19 @@ func (s VendorService) CreateRiskAssessment( return nil, err } - return vendorRiskAssessment, nil + return thirdPartyRiskAssessment, nil } -func (s VendorService) GetRiskAssessment( +func (s ThirdPartyService) GetRiskAssessment( ctx context.Context, - vendorRiskAssessmentID gid.GID, -) (*coredata.VendorRiskAssessment, error) { - vendorRiskAssessment := &coredata.VendorRiskAssessment{} + thirdPartyRiskAssessmentID gid.GID, +) (*coredata.ThirdPartyRiskAssessment, error) { + thirdPartyRiskAssessment := &coredata.ThirdPartyRiskAssessment{} err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - return vendorRiskAssessment.LoadByID(ctx, conn, s.svc.scope, vendorRiskAssessmentID) + return thirdPartyRiskAssessment.LoadByID(ctx, conn, s.svc.scope, thirdPartyRiskAssessmentID) }, ) @@ -800,25 +800,25 @@ func (s VendorService) GetRiskAssessment( return nil, err } - return vendorRiskAssessment, nil + return thirdPartyRiskAssessment, nil } -func (s VendorService) GetByRiskAssessmentID( +func (s ThirdPartyService) GetByRiskAssessmentID( ctx context.Context, - vendorRiskAssessmentID gid.GID, -) (*coredata.Vendor, error) { - vendor := &coredata.Vendor{} + thirdPartyRiskAssessmentID gid.GID, +) (*coredata.ThirdParty, error) { + thirdParty := &coredata.ThirdParty{} err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - vendorRiskAssessment := &coredata.VendorRiskAssessment{} - if err := vendorRiskAssessment.LoadByID(ctx, conn, s.svc.scope, vendorRiskAssessmentID); err != nil { - return fmt.Errorf("cannot load vendor risk assessment: %w", err) + thirdPartyRiskAssessment := &coredata.ThirdPartyRiskAssessment{} + if err := thirdPartyRiskAssessment.LoadByID(ctx, conn, s.svc.scope, thirdPartyRiskAssessmentID); err != nil { + return fmt.Errorf("cannot load thirdParty risk assessment: %w", err) } - if err := vendor.LoadByID(ctx, conn, s.svc.scope, vendorRiskAssessment.VendorID); err != nil { - return fmt.Errorf("cannot load vendor: %w", err) + if err := thirdParty.LoadByID(ctx, conn, s.svc.scope, thirdPartyRiskAssessment.ThirdPartyID); err != nil { + return fmt.Errorf("cannot load thirdParty: %w", err) } return nil @@ -829,91 +829,91 @@ func (s VendorService) GetByRiskAssessmentID( return nil, err } - return vendor, nil + return thirdParty, nil } -func (s VendorService) Assess( +func (s ThirdPartyService) Assess( ctx context.Context, - req AssessVendorRequest, -) (*AssessVendorResult, error) { - result, err := s.svc.vendorAssessor.Assess(ctx, req.WebsiteURL, ref.UnrefOrZero(req.Procedure), nil) + req AssessThirdPartyRequest, +) (*AssessThirdPartyResult, error) { + result, err := s.svc.thirdPartyAssessor.Assess(ctx, req.WebsiteURL, ref.UnrefOrZero(req.Procedure), nil) if err != nil { - return nil, fmt.Errorf("cannot assess vendor: %w", err) + return nil, fmt.Errorf("cannot assess thirdParty: %w", err) } - vendor := &coredata.Vendor{} + thirdParty := &coredata.ThirdParty{} err = s.svc.pg.WithTx( ctx, func(ctx context.Context, conn pg.Tx) error { - if err := vendor.LoadByID(ctx, conn, s.svc.scope, req.ID); err != nil { - return fmt.Errorf("cannot load vendor %q: %w", req.ID, err) + if err := thirdParty.LoadByID(ctx, conn, s.svc.scope, req.ID); err != nil { + return fmt.Errorf("cannot load thirdParty %q: %w", req.ID, err) } info := result.Info if info.Name != "" { - vendor.Name = info.Name + thirdParty.Name = info.Name } - vendor.WebsiteURL = &req.WebsiteURL + thirdParty.WebsiteURL = &req.WebsiteURL if info.Category != "" { - vendor.Category = coredata.VendorCategory(info.Category) + thirdParty.Category = coredata.ThirdPartyCategory(info.Category) } - vendor.UpdatedAt = time.Now() + thirdParty.UpdatedAt = time.Now() if info.Description != "" { - vendor.Description = &info.Description + thirdParty.Description = &info.Description } if info.HeadquarterAddress != "" { - vendor.HeadquarterAddress = &info.HeadquarterAddress + thirdParty.HeadquarterAddress = &info.HeadquarterAddress } if info.LegalName != "" { - vendor.LegalName = &info.LegalName + thirdParty.LegalName = &info.LegalName } if info.PrivacyPolicyURL != "" { - vendor.PrivacyPolicyURL = &info.PrivacyPolicyURL + thirdParty.PrivacyPolicyURL = &info.PrivacyPolicyURL } if info.ServiceLevelAgreementURL != "" { - vendor.ServiceLevelAgreementURL = &info.ServiceLevelAgreementURL + thirdParty.ServiceLevelAgreementURL = &info.ServiceLevelAgreementURL } if info.DataProcessingAgreementURL != "" { - vendor.DataProcessingAgreementURL = &info.DataProcessingAgreementURL + thirdParty.DataProcessingAgreementURL = &info.DataProcessingAgreementURL } if info.BusinessAssociateAgreementURL != "" { - vendor.BusinessAssociateAgreementURL = &info.BusinessAssociateAgreementURL + thirdParty.BusinessAssociateAgreementURL = &info.BusinessAssociateAgreementURL } if info.SubprocessorsListURL != "" { - vendor.SubprocessorsListURL = &info.SubprocessorsListURL + thirdParty.SubprocessorsListURL = &info.SubprocessorsListURL } if info.SecurityPageURL != "" { - vendor.SecurityPageURL = &info.SecurityPageURL + thirdParty.SecurityPageURL = &info.SecurityPageURL } if info.TrustPageURL != "" { - vendor.TrustPageURL = &info.TrustPageURL + thirdParty.TrustPageURL = &info.TrustPageURL } if info.TermsOfServiceURL != "" { - vendor.TermsOfServiceURL = &info.TermsOfServiceURL + thirdParty.TermsOfServiceURL = &info.TermsOfServiceURL } if info.StatusPageURL != "" { - vendor.StatusPageURL = &info.StatusPageURL + thirdParty.StatusPageURL = &info.StatusPageURL } if len(info.Certifications) > 0 { - vendor.Certifications = info.Certifications + thirdParty.Certifications = info.Certifications } - if err := vendor.Update(ctx, conn, s.svc.scope); err != nil { - return fmt.Errorf("cannot update vendor: %w", err) + if err := thirdParty.Update(ctx, conn, s.svc.scope); err != nil { + return fmt.Errorf("cannot update thirdParty: %w", err) } if err := webhook.InsertData( ctx, conn, s.svc.scope, - vendor.OrganizationID, - coredata.WebhookEventTypeVendorUpdated, - webhooktypes.NewVendor(vendor), + thirdParty.OrganizationID, + coredata.WebhookEventTypeThirdPartyUpdated, + webhooktypes.NewThirdParty(thirdParty), ); err != nil { return fmt.Errorf("cannot insert webhook event: %w", err) } @@ -934,8 +934,8 @@ func (s VendorService) Assess( } } - return &AssessVendorResult{ - Vendor: vendor, + return &AssessThirdPartyResult{ + ThirdParty: thirdParty, Report: result.Document, Subprocessors: subprocessors, }, nil diff --git a/pkg/probo/third_party_service_service.go b/pkg/probo/third_party_service_service.go new file mode 100644 index 000000000..ebb6a8ff1 --- /dev/null +++ b/pkg/probo/third_party_service_service.go @@ -0,0 +1,221 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package probo + +import ( + "context" + "fmt" + "time" + + "go.gearno.de/kit/pg" + "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/page" + "go.probo.inc/probo/pkg/validator" +) + +type ( + ThirdPartyServiceService struct { + svc *TenantService + } + + CreateThirdPartyServiceRequest struct { + ThirdPartyID gid.GID + Name string + Description *string + } + + UpdateThirdPartyServiceRequest struct { + ID gid.GID + Name *string + Description **string + } +) + +func (cvsr *CreateThirdPartyServiceRequest) Validate() error { + v := validator.New() + + v.Check(cvsr.ThirdPartyID, "third_party_id", validator.Required(), validator.GID(coredata.ThirdPartyEntityType)) + v.Check(cvsr.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength)) + v.Check(cvsr.Description, "description", validator.SafeText(ContentMaxLength)) + + return v.Error() +} + +func (uvsr *UpdateThirdPartyServiceRequest) Validate() error { + v := validator.New() + + v.Check(uvsr.ID, "id", validator.Required(), validator.GID(coredata.ThirdPartyServiceEntityType)) + v.Check(uvsr.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength)) + v.Check(uvsr.Description, "description", validator.SafeText(ContentMaxLength)) + + return v.Error() +} + +func (s ThirdPartyServiceService) Get( + ctx context.Context, + thirdPartyServiceID gid.GID, +) (*coredata.ThirdPartyService, error) { + thirdPartyService := &coredata.ThirdPartyService{} + + err := s.svc.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + err := thirdPartyService.LoadByID(ctx, conn, s.svc.scope, thirdPartyServiceID) + if err != nil { + return fmt.Errorf("cannot load thirdParty service: %w", err) + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + return thirdPartyService, nil +} + +func (s ThirdPartyServiceService) List( + ctx context.Context, + thirdPartyID gid.GID, + cursor *page.Cursor[coredata.ThirdPartyServiceOrderField], +) (*page.Page[*coredata.ThirdPartyService, coredata.ThirdPartyServiceOrderField], error) { + var thirdPartyServices coredata.ThirdPartyServices + + err := s.svc.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + err := thirdPartyServices.LoadByThirdPartyID(ctx, conn, s.svc.scope, thirdPartyID, cursor) + if err != nil { + return fmt.Errorf("cannot load thirdParty services: %w", err) + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + return page.NewPage(thirdPartyServices, cursor), nil +} + +func (s ThirdPartyServiceService) Create( + ctx context.Context, + req CreateThirdPartyServiceRequest, +) (*coredata.ThirdPartyService, error) { + if err := req.Validate(); err != nil { + return nil, err + } + + now := time.Now() + thirdPartyService := &coredata.ThirdPartyService{ + ID: gid.New(s.svc.scope.GetTenantID(), coredata.ThirdPartyServiceEntityType), + ThirdPartyID: req.ThirdPartyID, + Name: req.Name, + Description: req.Description, + CreatedAt: now, + UpdatedAt: now, + } + + err := s.svc.pg.WithTx( + ctx, + func(ctx context.Context, conn pg.Tx) error { + thirdParty := &coredata.ThirdParty{} + if err := thirdParty.LoadByID(ctx, conn, s.svc.scope, req.ThirdPartyID); err != nil { + return fmt.Errorf("cannot load thirdParty: %w", err) + } + + thirdPartyService.OrganizationID = thirdParty.OrganizationID + + if err := thirdPartyService.Insert(ctx, conn, s.svc.scope); err != nil { + return fmt.Errorf("cannot insert thirdParty service: %w", err) + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + return thirdPartyService, nil +} + +func (s ThirdPartyServiceService) Update( + ctx context.Context, + req UpdateThirdPartyServiceRequest, +) (*coredata.ThirdPartyService, error) { + if err := req.Validate(); err != nil { + return nil, err + } + + thirdPartyService := &coredata.ThirdPartyService{} + + err := s.svc.pg.WithTx( + ctx, + func(ctx context.Context, conn pg.Tx) error { + err := thirdPartyService.LoadByID(ctx, conn, s.svc.scope, req.ID) + if err != nil { + return fmt.Errorf("cannot load thirdParty service: %w", err) + } + + if req.Name != nil { + thirdPartyService.Name = *req.Name + } + if req.Description != nil { + thirdPartyService.Description = *req.Description + } + thirdPartyService.UpdatedAt = time.Now() + + if err := thirdPartyService.Update(ctx, conn, s.svc.scope); err != nil { + return fmt.Errorf("cannot update thirdParty service: %w", err) + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + return thirdPartyService, nil +} + +func (s ThirdPartyServiceService) Delete( + ctx context.Context, + thirdPartyServiceID gid.GID, +) error { + thirdPartyService := coredata.ThirdPartyService{ID: thirdPartyServiceID} + return s.svc.pg.WithTx( + ctx, + func(ctx context.Context, conn pg.Tx) error { + if err := thirdPartyService.LoadByID(ctx, conn, s.svc.scope, thirdPartyServiceID); err != nil { + return fmt.Errorf("cannot load thirdParty service: %w", err) + } + + if err := thirdPartyService.Delete(ctx, conn, s.svc.scope); err != nil { + return fmt.Errorf("cannot delete thirdParty service: %w", err) + } + + return nil + }, + ) +} diff --git a/pkg/probo/vendor_contact_service.go b/pkg/probo/vendor_contact_service.go deleted file mode 100644 index e106e9d08..000000000 --- a/pkg/probo/vendor_contact_service.go +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright (c) 2025-2026 Probo Inc . -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -package probo - -import ( - "context" - "fmt" - "time" - - "go.gearno.de/kit/pg" - "go.probo.inc/probo/pkg/coredata" - "go.probo.inc/probo/pkg/gid" - "go.probo.inc/probo/pkg/mail" - "go.probo.inc/probo/pkg/page" - "go.probo.inc/probo/pkg/validator" -) - -type ( - VendorContactService struct { - svc *TenantService - } - - CreateVendorContactRequest struct { - VendorID gid.GID - FullName *string - Email *mail.Addr - Phone *string - Role *string - } - - UpdateVendorContactRequest struct { - ID gid.GID - FullName **string - Email **mail.Addr - Phone **string - Role **string - } -) - -func (cvcr *CreateVendorContactRequest) Validate() error { - v := validator.New() - - v.Check(cvcr.VendorID, "vendor_id", validator.Required(), validator.GID(coredata.VendorEntityType)) - v.Check(cvcr.FullName, "fullName", validator.SafeTextNoNewLine(TitleMaxLength)) - v.Check(cvcr.Phone, "phone", validator.SafeText(NameMaxLength)) - v.Check(cvcr.Role, "role", validator.SafeText(TitleMaxLength)) - - return v.Error() -} - -func (uvcr *UpdateVendorContactRequest) Validate() error { - v := validator.New() - - v.Check(uvcr.ID, "id", validator.Required(), validator.GID(coredata.VendorContactEntityType)) - v.Check(uvcr.FullName, "fullName", validator.SafeTextNoNewLine(TitleMaxLength)) - v.Check(uvcr.Phone, "phone", validator.SafeText(NameMaxLength)) - v.Check(uvcr.Role, "role", validator.SafeText(TitleMaxLength)) - - return v.Error() -} - -func (s VendorContactService) Get( - ctx context.Context, - vendorContactID gid.GID, -) (*coredata.VendorContact, error) { - vendorContact := &coredata.VendorContact{} - - err := s.svc.pg.WithConn( - ctx, - func(ctx context.Context, conn pg.Querier) error { - err := vendorContact.LoadByID(ctx, conn, s.svc.scope, vendorContactID) - if err != nil { - return fmt.Errorf("cannot load vendor contact: %w", err) - } - - return nil - }, - ) - - if err != nil { - return nil, err - } - - return vendorContact, nil -} - -func (s VendorContactService) List( - ctx context.Context, - vendorID gid.GID, - cursor *page.Cursor[coredata.VendorContactOrderField], -) (*page.Page[*coredata.VendorContact, coredata.VendorContactOrderField], error) { - var vendorContacts coredata.VendorContacts - - err := s.svc.pg.WithConn( - ctx, - func(ctx context.Context, conn pg.Querier) error { - err := vendorContacts.LoadByVendorID(ctx, conn, s.svc.scope, vendorID, cursor) - if err != nil { - return fmt.Errorf("cannot load vendor contacts: %w", err) - } - - return nil - }, - ) - - if err != nil { - return nil, err - } - - return page.NewPage(vendorContacts, cursor), nil -} - -func (s VendorContactService) Create( - ctx context.Context, - req CreateVendorContactRequest, -) (*coredata.VendorContact, error) { - if err := req.Validate(); err != nil { - return nil, err - } - - now := time.Now() - vendorContact := &coredata.VendorContact{ - ID: gid.New(s.svc.scope.GetTenantID(), coredata.VendorContactEntityType), - VendorID: req.VendorID, - FullName: req.FullName, - Email: req.Email, - Phone: req.Phone, - Role: req.Role, - CreatedAt: now, - UpdatedAt: now, - } - - err := s.svc.pg.WithTx( - ctx, - func(ctx context.Context, conn pg.Tx) error { - vendor := &coredata.Vendor{} - if err := vendor.LoadByID(ctx, conn, s.svc.scope, req.VendorID); err != nil { - return fmt.Errorf("cannot load vendor: %w", err) - } - - vendorContact.OrganizationID = vendor.OrganizationID - - if err := vendorContact.Insert(ctx, conn, s.svc.scope); err != nil { - return fmt.Errorf("cannot insert vendor contact: %w", err) - } - - return nil - }, - ) - - if err != nil { - return nil, err - } - - return vendorContact, nil -} - -func (s VendorContactService) Update( - ctx context.Context, - req UpdateVendorContactRequest, -) (*coredata.VendorContact, error) { - if err := req.Validate(); err != nil { - return nil, err - } - - vendorContact := &coredata.VendorContact{} - - err := s.svc.pg.WithTx( - ctx, - func(ctx context.Context, conn pg.Tx) error { - err := vendorContact.LoadByID(ctx, conn, s.svc.scope, req.ID) - if err != nil { - return fmt.Errorf("cannot load vendor contact: %w", err) - } - - if req.FullName != nil { - vendorContact.FullName = *req.FullName - } - if req.Email != nil { - vendorContact.Email = *req.Email - } - if req.Phone != nil { - vendorContact.Phone = *req.Phone - } - if req.Role != nil { - vendorContact.Role = *req.Role - } - vendorContact.UpdatedAt = time.Now() - - return vendorContact.Update(ctx, conn, s.svc.scope) - }, - ) - - if err != nil { - return nil, err - } - - return vendorContact, nil -} - -func (s VendorContactService) Delete( - ctx context.Context, - vendorContactID gid.GID, -) error { - vendorContact := coredata.VendorContact{ID: vendorContactID} - return s.svc.pg.WithTx( - ctx, - func(ctx context.Context, conn pg.Tx) error { - if err := vendorContact.LoadByID(ctx, conn, s.svc.scope, vendorContactID); err != nil { - return fmt.Errorf("cannot load vendor contact: %w", err) - } - - if err := vendorContact.Delete(ctx, conn, s.svc.scope); err != nil { - return fmt.Errorf("cannot delete vendor contact: %w", err) - } - - return nil - }, - ) -} diff --git a/pkg/probo/vendor_service_service.go b/pkg/probo/vendor_service_service.go deleted file mode 100644 index e5ce6c794..000000000 --- a/pkg/probo/vendor_service_service.go +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright (c) 2025-2026 Probo Inc . -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -// PERFORMANCE OF THIS SOFTWARE. - -package probo - -import ( - "context" - "fmt" - "time" - - "go.gearno.de/kit/pg" - "go.probo.inc/probo/pkg/coredata" - "go.probo.inc/probo/pkg/gid" - "go.probo.inc/probo/pkg/page" - "go.probo.inc/probo/pkg/validator" -) - -type ( - VendorServiceService struct { - svc *TenantService - } - - CreateVendorServiceRequest struct { - VendorID gid.GID - Name string - Description *string - } - - UpdateVendorServiceRequest struct { - ID gid.GID - Name *string - Description **string - } -) - -func (cvsr *CreateVendorServiceRequest) Validate() error { - v := validator.New() - - v.Check(cvsr.VendorID, "vendor_id", validator.Required(), validator.GID(coredata.VendorEntityType)) - v.Check(cvsr.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength)) - v.Check(cvsr.Description, "description", validator.SafeText(ContentMaxLength)) - - return v.Error() -} - -func (uvsr *UpdateVendorServiceRequest) Validate() error { - v := validator.New() - - v.Check(uvsr.ID, "id", validator.Required(), validator.GID(coredata.VendorServiceEntityType)) - v.Check(uvsr.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength)) - v.Check(uvsr.Description, "description", validator.SafeText(ContentMaxLength)) - - return v.Error() -} - -func (s VendorServiceService) Get( - ctx context.Context, - vendorServiceID gid.GID, -) (*coredata.VendorService, error) { - vendorService := &coredata.VendorService{} - - err := s.svc.pg.WithConn( - ctx, - func(ctx context.Context, conn pg.Querier) error { - err := vendorService.LoadByID(ctx, conn, s.svc.scope, vendorServiceID) - if err != nil { - return fmt.Errorf("cannot load vendor service: %w", err) - } - - return nil - }, - ) - - if err != nil { - return nil, err - } - - return vendorService, nil -} - -func (s VendorServiceService) List( - ctx context.Context, - vendorID gid.GID, - cursor *page.Cursor[coredata.VendorServiceOrderField], -) (*page.Page[*coredata.VendorService, coredata.VendorServiceOrderField], error) { - var vendorServices coredata.VendorServices - - err := s.svc.pg.WithConn( - ctx, - func(ctx context.Context, conn pg.Querier) error { - err := vendorServices.LoadByVendorID(ctx, conn, s.svc.scope, vendorID, cursor) - if err != nil { - return fmt.Errorf("cannot load vendor services: %w", err) - } - - return nil - }, - ) - - if err != nil { - return nil, err - } - - return page.NewPage(vendorServices, cursor), nil -} - -func (s VendorServiceService) Create( - ctx context.Context, - req CreateVendorServiceRequest, -) (*coredata.VendorService, error) { - if err := req.Validate(); err != nil { - return nil, err - } - - now := time.Now() - vendorService := &coredata.VendorService{ - ID: gid.New(s.svc.scope.GetTenantID(), coredata.VendorServiceEntityType), - VendorID: req.VendorID, - Name: req.Name, - Description: req.Description, - CreatedAt: now, - UpdatedAt: now, - } - - err := s.svc.pg.WithTx( - ctx, - func(ctx context.Context, conn pg.Tx) error { - vendor := &coredata.Vendor{} - if err := vendor.LoadByID(ctx, conn, s.svc.scope, req.VendorID); err != nil { - return fmt.Errorf("cannot load vendor: %w", err) - } - - vendorService.OrganizationID = vendor.OrganizationID - - if err := vendorService.Insert(ctx, conn, s.svc.scope); err != nil { - return fmt.Errorf("cannot insert vendor service: %w", err) - } - - return nil - }, - ) - - if err != nil { - return nil, err - } - - return vendorService, nil -} - -func (s VendorServiceService) Update( - ctx context.Context, - req UpdateVendorServiceRequest, -) (*coredata.VendorService, error) { - if err := req.Validate(); err != nil { - return nil, err - } - - vendorService := &coredata.VendorService{} - - err := s.svc.pg.WithTx( - ctx, - func(ctx context.Context, conn pg.Tx) error { - err := vendorService.LoadByID(ctx, conn, s.svc.scope, req.ID) - if err != nil { - return fmt.Errorf("cannot load vendor service: %w", err) - } - - if req.Name != nil { - vendorService.Name = *req.Name - } - if req.Description != nil { - vendorService.Description = *req.Description - } - vendorService.UpdatedAt = time.Now() - - if err := vendorService.Update(ctx, conn, s.svc.scope); err != nil { - return fmt.Errorf("cannot update vendor service: %w", err) - } - - return nil - }, - ) - - if err != nil { - return nil, err - } - - return vendorService, nil -} - -func (s VendorServiceService) Delete( - ctx context.Context, - vendorServiceID gid.GID, -) error { - vendorService := coredata.VendorService{ID: vendorServiceID} - return s.svc.pg.WithTx( - ctx, - func(ctx context.Context, conn pg.Tx) error { - if err := vendorService.LoadByID(ctx, conn, s.svc.scope, vendorServiceID); err != nil { - return fmt.Errorf("cannot load vendor service: %w", err) - } - - if err := vendorService.Delete(ctx, conn, s.svc.scope); err != nil { - return fmt.Errorf("cannot delete vendor service: %w", err) - } - - return nil - }, - ) -} diff --git a/pkg/probod/probod.go b/pkg/probod/probod.go index e5f3a60c8..5cca7357b 100644 --- a/pkg/probod/probod.go +++ b/pkg/probod/probod.go @@ -299,7 +299,7 @@ func (impl *Implm) Run( return err } - vendorAssessor, err := impl.buildVendorAssessor(l, tp, r) + thirdPartyAssessor, err := impl.buildThirdPartyAssessor(l, tp, r) if err != nil { return err } @@ -491,7 +491,7 @@ func (impl *Implm) Run( esignService, defaultConnectorRegistry, time.Duration(impl.cfg.Auth.InvitationConfirmationTokenValidity)*time.Second, - vendorAssessor, + thirdPartyAssessor, ) if err != nil { return fmt.Errorf("cannot create probo service: %w", err) diff --git a/pkg/probod/vendor_assessor.go b/pkg/probod/third_party_assessor.go similarity index 69% rename from pkg/probod/vendor_assessor.go rename to pkg/probod/third_party_assessor.go index 7b1ff9861..fa814b1d7 100644 --- a/pkg/probod/vendor_assessor.go +++ b/pkg/probod/third_party_assessor.go @@ -22,22 +22,22 @@ import ( "go.probo.inc/probo/pkg/vetting" ) -// buildVendorAssessor wires the vendor assessment agent. It is an opt-in -// feature: deployments that do not set `llm.vendor-assessor.provider` get a -// DisabledVendorAssessor that reports the feature as unavailable. The -// vendor-assessor does not inherit the default provider because its +// buildThirdPartyAssessor wires the thirdParty assessment agent. It is an opt-in +// feature: deployments that do not set `llm.third-party-assessor.provider` get a +// DisabledThirdPartyAssessor that reports the feature as unavailable. The +// third-party-assessor does not inherit the default provider because its // pipeline (LLM + browser + search) is expensive and should not be enabled // implicitly. -func (impl *Implm) buildVendorAssessor( +func (impl *Implm) buildThirdPartyAssessor( l *log.Logger, tp trace.TracerProvider, r prometheus.Registerer, -) (probo.VendorAssessor, error) { - if impl.cfg.Agents.VendorAssessor.Provider == "" { - return probo.DisabledVendorAssessor{}, nil +) (probo.ThirdPartyAssessor, error) { + if impl.cfg.Agents.ThirdPartyAssessor.Provider == "" { + return probo.DisabledThirdPartyAssessor{}, nil } - agentCfg, llmClient, err := impl.resolveAgentClient("vendor-assessor", impl.cfg.Agents.VendorAssessor, l, tp, r) + agentCfg, llmClient, err := impl.resolveAgentClient("third-party-assessor", impl.cfg.Agents.ThirdPartyAssessor, l, tp, r) if err != nil { return nil, err } @@ -53,6 +53,6 @@ func (impl *Implm) buildVendorAssessor( MaxTokens: maxTokens, ChromeAddr: impl.cfg.ChromeDPAddr, SearchEndpoint: impl.cfg.SearchEndpoint, - Logger: l.Named("vendor-assessor"), + Logger: l.Named("third-party-assessor"), }), nil } diff --git a/pkg/probodconfig/llm_config.go b/pkg/probodconfig/llm_config.go index 5334f8b87..5612b114d 100644 --- a/pkg/probodconfig/llm_config.go +++ b/pkg/probodconfig/llm_config.go @@ -44,11 +44,11 @@ type ( // settings. Default is used as a fallback when an agent-specific field // is zero-valued. AgentsConfig struct { - Providers map[string]LLMProviderConfig `json:"providers"` - Default LLMAgentConfig `json:"defaults"` - Probo LLMAgentConfig `json:"probo"` - EvidenceDescriber LLMAgentConfig `json:"evidence-describer"` - VendorAssessor LLMAgentConfig `json:"vendor-assessor"` + Providers map[string]LLMProviderConfig `json:"providers"` + Default LLMAgentConfig `json:"defaults"` + Probo LLMAgentConfig `json:"probo"` + EvidenceDescriber LLMAgentConfig `json:"evidence-describer"` + ThirdPartyAssessor LLMAgentConfig `json:"third-party-assessor"` } ) diff --git a/pkg/server/api/console/v1/asset_resolvers.go b/pkg/server/api/console/v1/asset_resolvers.go index 7ced29f7a..9db646d93 100644 --- a/pkg/server/api/console/v1/asset_resolvers.go +++ b/pkg/server/api/console/v1/asset_resolvers.go @@ -44,20 +44,20 @@ func (r *assetResolver) Owner(ctx context.Context, obj *types.Asset) (*types.Pro return types.NewProfile(owner), nil } -// Vendors is the resolver for the vendors field. -func (r *assetResolver) Vendors(ctx context.Context, obj *types.Asset, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorOrderBy) (*types.VendorConnection, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionVendorList); err != nil { +// ThirdParties is the resolver for the thirdParties field. +func (r *assetResolver) ThirdParties(ctx context.Context, obj *types.Asset, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ThirdPartyOrderBy) (*types.ThirdPartyConnection, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionThirdPartyList); err != nil { return nil, err } prb := r.ProboService(ctx, obj.ID.TenantID()) - pageOrderBy := page.OrderBy[coredata.VendorOrderField]{ - Field: coredata.VendorOrderFieldCreatedAt, + pageOrderBy := page.OrderBy[coredata.ThirdPartyOrderField]{ + Field: coredata.ThirdPartyOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, } if orderBy != nil { - pageOrderBy = page.OrderBy[coredata.VendorOrderField]{ + pageOrderBy = page.OrderBy[coredata.ThirdPartyOrderField]{ Field: orderBy.Field, Direction: orderBy.Direction, } @@ -65,13 +65,13 @@ func (r *assetResolver) Vendors(ctx context.Context, obj *types.Asset, first *in cursor := types.NewCursor(first, after, last, before, pageOrderBy) - page, err := prb.Vendors.ListForAssetID(ctx, obj.ID, cursor) + page, err := prb.ThirdParties.ListForAssetID(ctx, obj.ID, cursor) if err != nil { - r.logger.ErrorCtx(ctx, "cannot list asset vendors", log.Error(err)) + r.logger.ErrorCtx(ctx, "cannot list asset thirdParties", log.Error(err)) return nil, gqlutils.Internal(ctx) } - return types.NewVendorConnection(page, r, obj.ID), nil + return types.NewThirdPartyConnection(page, r, obj.ID), nil } // Organization is the resolver for the organization field. @@ -149,20 +149,20 @@ func (r *datumResolver) Owner(ctx context.Context, obj *types.Datum) (*types.Pro return types.NewProfile(owner), nil } -// Vendors is the resolver for the vendors field. -func (r *datumResolver) Vendors(ctx context.Context, obj *types.Datum, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorOrderBy) (*types.VendorConnection, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionVendorList); err != nil { +// ThirdParties is the resolver for the thirdParties field. +func (r *datumResolver) ThirdParties(ctx context.Context, obj *types.Datum, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ThirdPartyOrderBy) (*types.ThirdPartyConnection, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionThirdPartyList); err != nil { return nil, err } prb := r.ProboService(ctx, obj.ID.TenantID()) - pageOrderBy := page.OrderBy[coredata.VendorOrderField]{ - Field: coredata.VendorOrderFieldCreatedAt, + pageOrderBy := page.OrderBy[coredata.ThirdPartyOrderField]{ + Field: coredata.ThirdPartyOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, } if orderBy != nil { - pageOrderBy = page.OrderBy[coredata.VendorOrderField]{ + pageOrderBy = page.OrderBy[coredata.ThirdPartyOrderField]{ Field: orderBy.Field, Direction: orderBy.Direction, } @@ -170,13 +170,13 @@ func (r *datumResolver) Vendors(ctx context.Context, obj *types.Datum, first *in cursor := types.NewCursor(first, after, last, before, pageOrderBy) - page, err := prb.Data.ListVendors(ctx, obj.ID, cursor) + page, err := prb.Data.ListThirdParties(ctx, obj.ID, cursor) if err != nil { - r.logger.ErrorCtx(ctx, "cannot list data vendors", log.Error(err)) + r.logger.ErrorCtx(ctx, "cannot list data thirdParties", log.Error(err)) return nil, gqlutils.Internal(ctx) } - return types.NewVendorConnection(page, r, obj.ID), nil + return types.NewThirdPartyConnection(page, r, obj.ID), nil } // Organization is the resolver for the organization field. @@ -244,7 +244,7 @@ func (r *mutationResolver) CreateAsset(ctx context.Context, input types.CreateAs OwnerID: input.OwnerID, AssetType: input.AssetType, DataTypesStored: input.DataTypesStored, - VendorIDs: input.VendorIds, + ThirdPartyIDs: input.ThirdPartyIds, }, ) @@ -278,7 +278,7 @@ func (r *mutationResolver) UpdateAsset(ctx context.Context, input types.UpdateAs OwnerID: input.OwnerID, AssetType: input.AssetType, DataTypesStored: input.DataTypesStored, - VendorIDs: input.VendorIds, + ThirdPartyIDs: input.ThirdPartyIds, }, ) if err != nil { @@ -328,7 +328,7 @@ func (r *mutationResolver) CreateDatum(ctx context.Context, input types.CreateDa Name: input.Name, DataClassification: input.DataClassification, OwnerID: input.OwnerID, - VendorIDs: input.VendorIds, + ThirdPartyIDs: input.ThirdPartyIds, }, ) @@ -360,7 +360,7 @@ func (r *mutationResolver) UpdateDatum(ctx context.Context, input types.UpdateDa Name: input.Name, DataClassification: input.DataClassification, OwnerID: input.OwnerID, - VendorIDs: input.VendorIds, + ThirdPartyIDs: input.ThirdPartyIds, }, ) diff --git a/pkg/server/api/console/v1/base_resolvers.go b/pkg/server/api/console/v1/base_resolvers.go index 68ee26d17..37d0d971e 100644 --- a/pkg/server/api/console/v1/base_resolvers.go +++ b/pkg/server/api/console/v1/base_resolvers.go @@ -39,14 +39,14 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error } return types.NewOrganization(organization), nil } - case coredata.VendorEntityType: - action = probo.ActionVendorGet + case coredata.ThirdPartyEntityType: + action = probo.ActionThirdPartyGet loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) { - vendor, err := prb.Vendors.Get(ctx, id) + thirdParty, err := prb.ThirdParties.Get(ctx, id) if err != nil { return nil, err } - return types.NewVendor(vendor), nil + return types.NewThirdParty(thirdParty), nil } case coredata.FrameworkEntityType: action = probo.ActionFrameworkGet @@ -111,32 +111,32 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error } return types.NewRisk(risk), nil } - case coredata.VendorComplianceReportEntityType: - action = probo.ActionVendorComplianceReportGet + case coredata.ThirdPartyComplianceReportEntityType: + action = probo.ActionThirdPartyComplianceReportGet loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) { - vendorComplianceReport, err := prb.VendorComplianceReports.Get(ctx, id) + thirdPartyComplianceReport, err := prb.ThirdPartyComplianceReports.Get(ctx, id) if err != nil { return nil, err } - return types.NewVendorComplianceReport(vendorComplianceReport), nil + return types.NewThirdPartyComplianceReport(thirdPartyComplianceReport), nil } - case coredata.VendorContactEntityType: - action = probo.ActionVendorContactGet + case coredata.ThirdPartyContactEntityType: + action = probo.ActionThirdPartyContactGet loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) { - vendorContact, err := prb.VendorContacts.Get(ctx, id) + thirdPartyContact, err := prb.ThirdPartyContacts.Get(ctx, id) if err != nil { return nil, err } - return types.NewVendorContact(vendorContact), nil + return types.NewThirdPartyContact(thirdPartyContact), nil } - case coredata.VendorServiceEntityType: - action = probo.ActionVendorServiceGet + case coredata.ThirdPartyServiceEntityType: + action = probo.ActionThirdPartyServiceGet loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) { - vendorService, err := prb.VendorServices.Get(ctx, id) + thirdPartyService, err := prb.ThirdPartyServices.Get(ctx, id) if err != nil { return nil, err } - return types.NewVendorService(vendorService), nil + return types.NewThirdPartyService(thirdPartyService), nil } case coredata.DocumentVersionEntityType: action = probo.ActionDocumentVersionList diff --git a/pkg/server/api/console/v1/dataloader/dataloader.go b/pkg/server/api/console/v1/dataloader/dataloader.go index 670c2d586..1a74b3d2f 100644 --- a/pkg/server/api/console/v1/dataloader/dataloader.go +++ b/pkg/server/api/console/v1/dataloader/dataloader.go @@ -34,7 +34,7 @@ type ( Organization *dataloadgen.Loader[gid.GID, *coredata.Organization] Framework *dataloadgen.Loader[gid.GID, *coredata.Framework] Control *dataloadgen.Loader[gid.GID, *coredata.Control] - Vendor *dataloadgen.Loader[gid.GID, *coredata.Vendor] + ThirdParty *dataloadgen.Loader[gid.GID, *coredata.ThirdParty] Document *dataloadgen.Loader[gid.GID, *coredata.Document] Profile *dataloadgen.Loader[gid.GID, *coredata.MembershipProfile] Risk *dataloadgen.Loader[gid.GID, *coredata.Risk] @@ -77,7 +77,7 @@ func (f *batchFetcher) newLoaders() *Loaders { Organization: dataloadgen.NewMappedLoader(f.fetchOrganizations), Framework: dataloadgen.NewMappedLoader(f.fetchFrameworks), Control: dataloadgen.NewMappedLoader(f.fetchControls), - Vendor: dataloadgen.NewMappedLoader(f.fetchVendors), + ThirdParty: dataloadgen.NewMappedLoader(f.fetchThirdParties), Document: dataloadgen.NewMappedLoader(f.fetchDocuments), Profile: dataloadgen.NewMappedLoader(f.fetchProfiles), Risk: dataloadgen.NewMappedLoader(f.fetchRisks), @@ -135,16 +135,16 @@ func (f *batchFetcher) fetchControls(ctx context.Context, keys []gid.GID) (map[g return result, nil } -func (f *batchFetcher) fetchVendors(ctx context.Context, keys []gid.GID) (map[gid.GID]*coredata.Vendor, error) { +func (f *batchFetcher) fetchThirdParties(ctx context.Context, keys []gid.GID) (map[gid.GID]*coredata.ThirdParty, error) { tenantSvc := f.probo.WithTenant(keys[0].TenantID()) - vendors, err := tenantSvc.Vendors.GetByIDs(ctx, keys...) + thirdParties, err := tenantSvc.ThirdParties.GetByIDs(ctx, keys...) if err != nil { - return nil, fmt.Errorf("cannot batch load vendors: %w", err) + return nil, fmt.Errorf("cannot batch load thirdParties: %w", err) } - result := make(map[gid.GID]*coredata.Vendor, len(vendors)) - for _, v := range vendors { + result := make(map[gid.GID]*coredata.ThirdParty, len(thirdParties)) + for _, v := range thirdParties { result[v.ID] = v } return result, nil diff --git a/pkg/server/api/console/v1/graphql/asset.graphql b/pkg/server/api/console/v1/graphql/asset.graphql index 71687e5c6..9971cf211 100644 --- a/pkg/server/api/console/v1/graphql/asset.graphql +++ b/pkg/server/api/console/v1/graphql/asset.graphql @@ -67,13 +67,13 @@ type Asset implements Node { name: String! amount: Int! owner: Profile! @goField(forceResolver: true) - vendors( + thirdParties( first: Int after: CursorKey last: Int before: CursorKey - orderBy: VendorOrder - ): VendorConnection! @goField(forceResolver: true) + orderBy: ThirdPartyOrder + ): ThirdPartyConnection! @goField(forceResolver: true) assetType: AssetType! dataTypesStored: String! organization: Organization! @goField(forceResolver: true) @@ -91,13 +91,13 @@ type Datum implements Node name: String! dataClassification: DataClassification! owner: Profile! @goField(forceResolver: true) - vendors( + thirdParties( first: Int after: CursorKey last: Int before: CursorKey - orderBy: VendorOrder - ): VendorConnection! @goField(forceResolver: true) + orderBy: ThirdPartyOrder + ): ThirdPartyConnection! @goField(forceResolver: true) organization: Organization! @goField(forceResolver: true) createdAt: Datetime! updatedAt: Datetime! @@ -155,7 +155,7 @@ input CreateAssetInput { ownerId: ID! assetType: AssetType! dataTypesStored: String! - vendorIds: [ID!] + thirdPartyIds: [ID!] } input UpdateAssetInput { @@ -165,7 +165,7 @@ input UpdateAssetInput { ownerId: ID assetType: AssetType dataTypesStored: String - vendorIds: [ID!] + thirdPartyIds: [ID!] } input DeleteAssetInput { @@ -177,7 +177,7 @@ input CreateDatumInput { name: String! dataClassification: DataClassification! ownerId: ID! - vendorIds: [ID!] + thirdPartyIds: [ID!] } input UpdateDatumInput { @@ -185,7 +185,7 @@ input UpdateDatumInput { name: String dataClassification: DataClassification ownerId: ID - vendorIds: [ID!] + thirdPartyIds: [ID!] } input DeleteDatumInput { diff --git a/pkg/server/api/console/v1/graphql/common_third_party.graphql b/pkg/server/api/console/v1/graphql/common_third_party.graphql index 142e28376..dec50fb6c 100644 --- a/pkg/server/api/console/v1/graphql/common_third_party.graphql +++ b/pkg/server/api/console/v1/graphql/common_third_party.graphql @@ -18,7 +18,7 @@ type CommonThirdParty ) { id: ID! name: String! - category: VendorCategory! + category: ThirdPartyCategory! websiteUrl: String headquarterAddress: String legalName: String diff --git a/pkg/server/api/console/v1/graphql/organization.graphql b/pkg/server/api/console/v1/graphql/organization.graphql index ccaf740b7..d0e7e4be0 100644 --- a/pkg/server/api/console/v1/graphql/organization.graphql +++ b/pkg/server/api/console/v1/graphql/organization.graphql @@ -317,15 +317,15 @@ type Organization implements Node { orderBy: CookieBannerOrder ): CookieBannerConnection @goField(forceResolver: true) - vendors( + thirdParties( first: Int after: CursorKey last: Int before: CursorKey - orderBy: VendorOrder - ): VendorConnection! @goField(forceResolver: true) + orderBy: ThirdPartyOrder + ): ThirdPartyConnection! @goField(forceResolver: true) - vendorsDocument: Document @goField(forceResolver: true) + thirdPartiesDocument: Document @goField(forceResolver: true) webhookSubscriptions( first: Int diff --git a/pkg/server/api/console/v1/graphql/processing_activity.graphql b/pkg/server/api/console/v1/graphql/processing_activity.graphql index 7ebfa0b95..fa2301a42 100644 --- a/pkg/server/api/console/v1/graphql/processing_activity.graphql +++ b/pkg/server/api/console/v1/graphql/processing_activity.graphql @@ -160,13 +160,13 @@ type ProcessingActivity implements Node { nextReviewDate: Datetime role: ProcessingActivityRole! dataProtectionOfficer: Profile @goField(forceResolver: true) - vendors( + thirdParties( first: Int after: CursorKey last: Int before: CursorKey - orderBy: VendorOrder - ): VendorConnection! @goField(forceResolver: true) + orderBy: ThirdPartyOrder + ): ThirdPartyConnection! @goField(forceResolver: true) dataProtectionImpactAssessment: DataProtectionImpactAssessment @goField(forceResolver: true) transferImpactAssessment: TransferImpactAssessment @@ -227,7 +227,7 @@ input CreateProcessingActivityInput { nextReviewDate: Datetime role: ProcessingActivityRole! dataProtectionOfficerId: ID - vendorIds: [ID!] + thirdPartyIds: [ID!] } input UpdateProcessingActivityInput { @@ -252,7 +252,7 @@ input UpdateProcessingActivityInput { nextReviewDate: Datetime @goField(omittable: true) role: ProcessingActivityRole dataProtectionOfficerId: ID @goField(omittable: true) - vendorIds: [ID!] + thirdPartyIds: [ID!] } input DeleteProcessingActivityInput { diff --git a/pkg/server/api/console/v1/graphql/third_party.graphql b/pkg/server/api/console/v1/graphql/third_party.graphql new file mode 100644 index 000000000..512b50bab --- /dev/null +++ b/pkg/server/api/console/v1/graphql/third_party.graphql @@ -0,0 +1,711 @@ +enum ThirdPartyCategory + @goModel(model: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategory") { + ANALYTICS + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryAnalytics" + ) + CLOUD_MONITORING + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryCloudMonitoring" + ) + CLOUD_PROVIDER + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryCloudProvider" + ) + COLLABORATION + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryCollaboration" + ) + CUSTOMER_SUPPORT + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryCustomerSupport" + ) + DATA_STORAGE_AND_PROCESSING + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryDataStorageAndProcessing" + ) + DOCUMENT_MANAGEMENT + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryDocumentManagement" + ) + EMPLOYEE_MANAGEMENT + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryEmployeeManagement" + ) + ENGINEERING + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryEngineering" + ) + FINANCE + @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryFinance") + IDENTITY_PROVIDER + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryIdentityProvider" + ) + IT @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryIT") + MARKETING + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryMarketing" + ) + OFFICE_OPERATIONS + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryOfficeOperations" + ) + OTHER @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryOther") + PASSWORD_MANAGEMENT + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryPasswordManagement" + ) + PRODUCT_AND_DESIGN + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryProductAndDesign" + ) + PROFESSIONAL_SERVICES + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryProfessionalServices" + ) + RECRUITING + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryRecruiting" + ) + SALES @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategorySales") + SECURITY + @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategorySecurity") + VERSION_CONTROL + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryVersionControl" + ) +} + +enum DataSensitivity + @goModel(model: "go.probo.inc/probo/pkg/coredata.DataSensitivity") { + NONE @goEnum(value: "go.probo.inc/probo/pkg/coredata.DataSensitivityNone") + LOW @goEnum(value: "go.probo.inc/probo/pkg/coredata.DataSensitivityLow") + MEDIUM + @goEnum(value: "go.probo.inc/probo/pkg/coredata.DataSensitivityMedium") + HIGH @goEnum(value: "go.probo.inc/probo/pkg/coredata.DataSensitivityHigh") + CRITICAL + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.DataSensitivityCritical" + ) +} + +enum BusinessImpact + @goModel(model: "go.probo.inc/probo/pkg/coredata.BusinessImpact") { + LOW @goEnum(value: "go.probo.inc/probo/pkg/coredata.BusinessImpactLow") + MEDIUM + @goEnum(value: "go.probo.inc/probo/pkg/coredata.BusinessImpactMedium") + HIGH @goEnum(value: "go.probo.inc/probo/pkg/coredata.BusinessImpactHigh") + CRITICAL + @goEnum(value: "go.probo.inc/probo/pkg/coredata.BusinessImpactCritical") +} + +enum ThirdPartyOrderField + @goModel(model: "go.probo.inc/probo/pkg/coredata.ThirdPartyOrderField") { + NAME @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyOrderFieldName") + CREATED_AT + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyOrderFieldCreatedAt" + ) + UPDATED_AT + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyOrderFieldUpdatedAt" + ) +} + +enum ThirdPartyComplianceReportOrderField + @goModel( + model: "go.probo.inc/probo/pkg/coredata.ThirdPartyComplianceReportOrderField" + ) { + REPORT_DATE + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyComplianceReportOrderFieldReportDate" + ) + CREATED_AT + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyComplianceReportOrderFieldCreatedAt" + ) +} + +enum ThirdPartyContactOrderField + @goModel(model: "go.probo.inc/probo/pkg/coredata.ThirdPartyContactOrderField") { + CREATED_AT + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyContactOrderFieldCreatedAt" + ) + FULL_NAME + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyContactOrderFieldFullName" + ) + EMAIL + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyContactOrderFieldEmail" + ) +} + +enum ThirdPartyServiceOrderField + @goModel(model: "go.probo.inc/probo/pkg/coredata.ThirdPartyServiceOrderField") { + CREATED_AT + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyServiceOrderFieldCreatedAt" + ) + NAME + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyServiceOrderFieldName" + ) +} + +enum ThirdPartyRiskAssessmentOrderField + @goModel( + model: "go.probo.inc/probo/pkg/coredata.ThirdPartyRiskAssessmentOrderField" + ) { + CREATED_AT + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyRiskAssessmentOrderFieldCreatedAt" + ) + EXPIRES_AT + @goEnum( + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyRiskAssessmentOrderFieldExpiresAt" + ) +} + +input ThirdPartyOrder + @goModel( + model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ThirdPartyOrderBy" + ) { + direction: OrderDirection! + field: ThirdPartyOrderField! +} + +input ThirdPartyComplianceReportOrder + @goModel( + model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ThirdPartyComplianceReportOrderBy" + ) { + direction: OrderDirection! + field: ThirdPartyComplianceReportOrderField! +} + +input ThirdPartyContactOrder + @goModel( + model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ThirdPartyContactOrderBy" + ) { + direction: OrderDirection! + field: ThirdPartyContactOrderField! +} + +input ThirdPartyServiceOrder + @goModel( + model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ThirdPartyServiceOrderBy" + ) { + direction: OrderDirection! + field: ThirdPartyServiceOrderField! +} + +input ThirdPartyRiskAssessmentOrder { + field: ThirdPartyRiskAssessmentOrderField! + direction: OrderDirection! +} + +type ThirdParty implements Node { + id: ID! + name: String! + category: ThirdPartyCategory! + description: String + + organization: Organization! @goField(forceResolver: true) + + complianceReports( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: ThirdPartyComplianceReportOrder + ): ThirdPartyComplianceReportConnection! @goField(forceResolver: true) + + businessAssociateAgreement: ThirdPartyBusinessAssociateAgreement + @goField(forceResolver: true) + dataPrivacyAgreement: ThirdPartyDataPrivacyAgreement + @goField(forceResolver: true) + + contacts( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: ThirdPartyContactOrder + ): ThirdPartyContactConnection! @goField(forceResolver: true) + + services( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: ThirdPartyServiceOrder + ): ThirdPartyServiceConnection! @goField(forceResolver: true) + + riskAssessments( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: ThirdPartyRiskAssessmentOrder + ): ThirdPartyRiskAssessmentConnection! @goField(forceResolver: true) + + businessOwner: Profile @goField(forceResolver: true) + securityOwner: Profile @goField(forceResolver: true) + + statusPageUrl: String + termsOfServiceUrl: String + privacyPolicyUrl: String + serviceLevelAgreementUrl: String + dataProcessingAgreementUrl: String + businessAssociateAgreementUrl: String + subprocessorsListUrl: String + certifications: [String!]! + countries: [CountryCode!]! + securityPageUrl: String + trustPageUrl: String + headquarterAddress: String + legalName: String + websiteUrl: String + showOnTrustCenter: Boolean! + createdAt: Datetime! + updatedAt: Datetime! + + permission(action: String!): Boolean! @goField(forceResolver: true) +} + +type ThirdPartyComplianceReport implements Node { + id: ID! + thirdParty: ThirdParty! @goField(forceResolver: true) + reportDate: Datetime! + validUntil: Datetime + reportName: String! + file: File @goField(forceResolver: true) + createdAt: Datetime! + updatedAt: Datetime! + + permission(action: String!): Boolean! @goField(forceResolver: true) +} + +type ThirdPartyBusinessAssociateAgreement implements Node { + id: ID! + thirdParty: ThirdParty! @goField(forceResolver: true) + validFrom: Datetime + validUntil: Datetime + fileName: String! + fileUrl: String! @goField(forceResolver: true) + fileSize: BigInt! + createdAt: Datetime! + updatedAt: Datetime! + + permission(action: String!): Boolean! @goField(forceResolver: true) +} + +type ThirdPartyContact implements Node { + id: ID! + thirdParty: ThirdParty! @goField(forceResolver: true) + fullName: String + email: EmailAddr + phone: String + role: String + createdAt: Datetime! + updatedAt: Datetime! + + permission(action: String!): Boolean! @goField(forceResolver: true) +} + +type ThirdPartyService implements Node { + id: ID! + thirdParty: ThirdParty! @goField(forceResolver: true) + name: String! + description: String + createdAt: Datetime! + updatedAt: Datetime! + + permission(action: String!): Boolean! @goField(forceResolver: true) +} + +type ThirdPartyDataPrivacyAgreement implements Node { + id: ID! + thirdParty: ThirdParty! @goField(forceResolver: true) + validFrom: Datetime + validUntil: Datetime + fileName: String! + fileUrl: String! @goField(forceResolver: true) + fileSize: BigInt! + createdAt: Datetime! + updatedAt: Datetime! + + permission(action: String!): Boolean! @goField(forceResolver: true) +} + +type ThirdPartyRiskAssessment implements Node { + id: ID! + thirdParty: ThirdParty! @goField(forceResolver: true) + expiresAt: Datetime! + dataSensitivity: DataSensitivity! + businessImpact: BusinessImpact! + notes: String + createdAt: Datetime! + updatedAt: Datetime! + + permission(action: String!): Boolean! @goField(forceResolver: true) +} + +type ThirdPartyConnection + @goModel( + model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ThirdPartyConnection" + ) { + totalCount: Int! @goField(forceResolver: true) + edges: [ThirdPartyEdge!]! + pageInfo: PageInfo! +} + +type ThirdPartyEdge { + cursor: CursorKey! + node: ThirdParty! +} + +type ThirdPartyComplianceReportConnection { + edges: [ThirdPartyComplianceReportEdge!]! + pageInfo: PageInfo! +} + +type ThirdPartyComplianceReportEdge { + cursor: CursorKey! + node: ThirdPartyComplianceReport! +} + +type ThirdPartyContactConnection { + edges: [ThirdPartyContactEdge!]! + pageInfo: PageInfo! +} + +type ThirdPartyContactEdge { + cursor: CursorKey! + node: ThirdPartyContact! +} + +type ThirdPartyServiceConnection { + edges: [ThirdPartyServiceEdge!]! + pageInfo: PageInfo! +} + +type ThirdPartyServiceEdge { + cursor: CursorKey! + node: ThirdPartyService! +} + +type ThirdPartyRiskAssessmentConnection { + edges: [ThirdPartyRiskAssessmentEdge!]! + pageInfo: PageInfo! +} + +type ThirdPartyRiskAssessmentEdge { + cursor: CursorKey! + node: ThirdPartyRiskAssessment! +} + +extend type Mutation { + createThirdParty(input: CreateThirdPartyInput!): CreateThirdPartyPayload! + updateThirdParty(input: UpdateThirdPartyInput!): UpdateThirdPartyPayload! + deleteThirdParty(input: DeleteThirdPartyInput!): DeleteThirdPartyPayload! + createThirdPartyContact( + input: CreateThirdPartyContactInput! + ): CreateThirdPartyContactPayload! + updateThirdPartyContact( + input: UpdateThirdPartyContactInput! + ): UpdateThirdPartyContactPayload! + deleteThirdPartyContact( + input: DeleteThirdPartyContactInput! + ): DeleteThirdPartyContactPayload! + createThirdPartyService( + input: CreateThirdPartyServiceInput! + ): CreateThirdPartyServicePayload! + updateThirdPartyService( + input: UpdateThirdPartyServiceInput! + ): UpdateThirdPartyServicePayload! + deleteThirdPartyService( + input: DeleteThirdPartyServiceInput! + ): DeleteThirdPartyServicePayload! + uploadThirdPartyComplianceReport( + input: UploadThirdPartyComplianceReportInput! + ): UploadThirdPartyComplianceReportPayload! + deleteThirdPartyComplianceReport( + input: DeleteThirdPartyComplianceReportInput! + ): DeleteThirdPartyComplianceReportPayload! + uploadThirdPartyBusinessAssociateAgreement( + input: UploadThirdPartyBusinessAssociateAgreementInput! + ): UploadThirdPartyBusinessAssociateAgreementPayload! + updateThirdPartyBusinessAssociateAgreement( + input: UpdateThirdPartyBusinessAssociateAgreementInput! + ): UpdateThirdPartyBusinessAssociateAgreementPayload! + deleteThirdPartyBusinessAssociateAgreement( + input: DeleteThirdPartyBusinessAssociateAgreementInput! + ): DeleteThirdPartyBusinessAssociateAgreementPayload! + uploadThirdPartyDataPrivacyAgreement( + input: UploadThirdPartyDataPrivacyAgreementInput! + ): UploadThirdPartyDataPrivacyAgreementPayload! + updateThirdPartyDataPrivacyAgreement( + input: UpdateThirdPartyDataPrivacyAgreementInput! + ): UpdateThirdPartyDataPrivacyAgreementPayload! + deleteThirdPartyDataPrivacyAgreement( + input: DeleteThirdPartyDataPrivacyAgreementInput! + ): DeleteThirdPartyDataPrivacyAgreementPayload! + createThirdPartyRiskAssessment( + input: CreateThirdPartyRiskAssessmentInput! + ): CreateThirdPartyRiskAssessmentPayload! + assessThirdParty(input: AssessThirdPartyInput!): AssessThirdPartyPayload! + publishThirdPartyList( + input: PublishThirdPartyListInput! + ): PublishThirdPartyListPayload! +} + +input PublishThirdPartyListInput { + organizationId: ID! + approverIds: [ID!] + minor: Boolean! +} + +type PublishThirdPartyListPayload { + documentEdge: DocumentEdge! + documentVersionEdge: DocumentVersionEdge! +} + +input CreateThirdPartyInput { + organizationId: ID! + name: String! + description: String + headquarterAddress: String + legalName: String + websiteUrl: String + privacyPolicyUrl: String + category: ThirdPartyCategory + serviceLevelAgreementUrl: String + dataProcessingAgreementUrl: String + businessAssociateAgreementUrl: String + subprocessorsListUrl: String + certifications: [String!] + countries: [CountryCode!] + securityPageUrl: String + trustPageUrl: String + statusPageUrl: String + termsOfServiceUrl: String + businessOwnerId: ID + securityOwnerId: ID +} + +input UpdateThirdPartyInput { + id: ID! + name: String + description: String @goField(omittable: true) + statusPageUrl: String @goField(omittable: true) + termsOfServiceUrl: String @goField(omittable: true) + privacyPolicyUrl: String @goField(omittable: true) + serviceLevelAgreementUrl: String @goField(omittable: true) + dataProcessingAgreementUrl: String @goField(omittable: true) + businessAssociateAgreementUrl: String @goField(omittable: true) + subprocessorsListUrl: String @goField(omittable: true) + websiteUrl: String @goField(omittable: true) + legalName: String @goField(omittable: true) + headquarterAddress: String @goField(omittable: true) + category: ThirdPartyCategory + certifications: [String!] + countries: [CountryCode!] + securityPageUrl: String @goField(omittable: true) + trustPageUrl: String @goField(omittable: true) + businessOwnerId: ID @goField(omittable: true) + securityOwnerId: ID @goField(omittable: true) + showOnTrustCenter: Boolean +} + +input DeleteThirdPartyInput { + thirdPartyId: ID! +} + +input CreateThirdPartyContactInput { + thirdPartyId: ID! + fullName: String + email: EmailAddr + phone: String + role: String +} + +input UpdateThirdPartyContactInput { + id: ID! + fullName: String @goField(omittable: true) + email: EmailAddr @goField(omittable: true) + phone: String @goField(omittable: true) + role: String @goField(omittable: true) +} + +input DeleteThirdPartyContactInput { + thirdPartyContactId: ID! +} + +input CreateThirdPartyServiceInput { + thirdPartyId: ID! + name: String! + description: String + url: String + type: String +} + +input UpdateThirdPartyServiceInput { + id: ID! + name: String + description: String @goField(omittable: true) + url: String + type: String +} + +input DeleteThirdPartyServiceInput { + thirdPartyServiceId: ID! +} + +input UploadThirdPartyComplianceReportInput { + thirdPartyId: ID! + reportDate: Datetime! + validUntil: Datetime + reportName: String! + file: Upload! +} + +input DeleteThirdPartyComplianceReportInput { + reportId: ID! +} + +input UploadThirdPartyBusinessAssociateAgreementInput { + thirdPartyId: ID! + validFrom: Datetime + validUntil: Datetime + fileName: String! + file: Upload! +} + +input UpdateThirdPartyBusinessAssociateAgreementInput { + thirdPartyId: ID! + validFrom: Datetime @goField(omittable: true) + validUntil: Datetime @goField(omittable: true) +} + +input DeleteThirdPartyBusinessAssociateAgreementInput { + thirdPartyId: ID! +} + +input UploadThirdPartyDataPrivacyAgreementInput { + thirdPartyId: ID! + validFrom: Datetime + validUntil: Datetime + fileName: String! + file: Upload! +} + +input UpdateThirdPartyDataPrivacyAgreementInput { + thirdPartyId: ID! + validFrom: Datetime @goField(omittable: true) + validUntil: Datetime @goField(omittable: true) +} + +input DeleteThirdPartyDataPrivacyAgreementInput { + thirdPartyId: ID! +} + +input CreateThirdPartyRiskAssessmentInput { + thirdPartyId: ID! + expiresAt: Datetime! + dataSensitivity: DataSensitivity! + businessImpact: BusinessImpact! + notes: String +} + +input AssessThirdPartyInput { + id: ID! + websiteUrl: String! + procedure: String +} + +type ThirdPartySubprocessor { + name: String! + country: String! + purpose: String! +} + +type CreateThirdPartyPayload { + thirdPartyEdge: ThirdPartyEdge! +} + +type UpdateThirdPartyPayload { + thirdParty: ThirdParty! +} + +type DeleteThirdPartyPayload { + deletedThirdPartyId: ID! +} + +type CreateThirdPartyContactPayload { + thirdPartyContactEdge: ThirdPartyContactEdge! +} + +type UpdateThirdPartyContactPayload { + thirdPartyContact: ThirdPartyContact! +} + +type DeleteThirdPartyContactPayload { + deletedThirdPartyContactId: ID! +} + +type CreateThirdPartyServicePayload { + thirdPartyServiceEdge: ThirdPartyServiceEdge! +} + +type UpdateThirdPartyServicePayload { + thirdPartyService: ThirdPartyService! +} + +type DeleteThirdPartyServicePayload { + deletedThirdPartyServiceId: ID! +} + +type UploadThirdPartyComplianceReportPayload { + thirdPartyComplianceReportEdge: ThirdPartyComplianceReportEdge! +} + +type DeleteThirdPartyComplianceReportPayload { + deletedThirdPartyComplianceReportId: ID! +} + +type UploadThirdPartyBusinessAssociateAgreementPayload { + thirdPartyBusinessAssociateAgreement: ThirdPartyBusinessAssociateAgreement! +} + +type UpdateThirdPartyBusinessAssociateAgreementPayload { + thirdPartyBusinessAssociateAgreement: ThirdPartyBusinessAssociateAgreement! +} + +type DeleteThirdPartyBusinessAssociateAgreementPayload { + deletedThirdPartyId: ID! +} + +type UploadThirdPartyDataPrivacyAgreementPayload { + thirdPartyDataPrivacyAgreement: ThirdPartyDataPrivacyAgreement! +} + +type UpdateThirdPartyDataPrivacyAgreementPayload { + thirdPartyDataPrivacyAgreement: ThirdPartyDataPrivacyAgreement! +} + +type DeleteThirdPartyDataPrivacyAgreementPayload { + deletedThirdPartyId: ID! +} + +type CreateThirdPartyRiskAssessmentPayload { + thirdPartyRiskAssessmentEdge: ThirdPartyRiskAssessmentEdge! +} + +type AssessThirdPartyPayload { + thirdParty: ThirdParty! + report: String! + subprocessors: [ThirdPartySubprocessor!]! +} diff --git a/pkg/server/api/console/v1/graphql/vendor.graphql b/pkg/server/api/console/v1/graphql/vendor.graphql deleted file mode 100644 index c57f17934..000000000 --- a/pkg/server/api/console/v1/graphql/vendor.graphql +++ /dev/null @@ -1,711 +0,0 @@ -enum VendorCategory - @goModel(model: "go.probo.inc/probo/pkg/coredata.VendorCategory") { - ANALYTICS - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryAnalytics" - ) - CLOUD_MONITORING - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCloudMonitoring" - ) - CLOUD_PROVIDER - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCloudProvider" - ) - COLLABORATION - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCollaboration" - ) - CUSTOMER_SUPPORT - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCustomerSupport" - ) - DATA_STORAGE_AND_PROCESSING - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryDataStorageAndProcessing" - ) - DOCUMENT_MANAGEMENT - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryDocumentManagement" - ) - EMPLOYEE_MANAGEMENT - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryEmployeeManagement" - ) - ENGINEERING - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryEngineering" - ) - FINANCE - @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryFinance") - IDENTITY_PROVIDER - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryIdentityProvider" - ) - IT @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryIT") - MARKETING - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryMarketing" - ) - OFFICE_OPERATIONS - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryOfficeOperations" - ) - OTHER @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryOther") - PASSWORD_MANAGEMENT - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryPasswordManagement" - ) - PRODUCT_AND_DESIGN - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryProductAndDesign" - ) - PROFESSIONAL_SERVICES - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryProfessionalServices" - ) - RECRUITING - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryRecruiting" - ) - SALES @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategorySales") - SECURITY - @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategorySecurity") - VERSION_CONTROL - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryVersionControl" - ) -} - -enum DataSensitivity - @goModel(model: "go.probo.inc/probo/pkg/coredata.DataSensitivity") { - NONE @goEnum(value: "go.probo.inc/probo/pkg/coredata.DataSensitivityNone") - LOW @goEnum(value: "go.probo.inc/probo/pkg/coredata.DataSensitivityLow") - MEDIUM - @goEnum(value: "go.probo.inc/probo/pkg/coredata.DataSensitivityMedium") - HIGH @goEnum(value: "go.probo.inc/probo/pkg/coredata.DataSensitivityHigh") - CRITICAL - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.DataSensitivityCritical" - ) -} - -enum BusinessImpact - @goModel(model: "go.probo.inc/probo/pkg/coredata.BusinessImpact") { - LOW @goEnum(value: "go.probo.inc/probo/pkg/coredata.BusinessImpactLow") - MEDIUM - @goEnum(value: "go.probo.inc/probo/pkg/coredata.BusinessImpactMedium") - HIGH @goEnum(value: "go.probo.inc/probo/pkg/coredata.BusinessImpactHigh") - CRITICAL - @goEnum(value: "go.probo.inc/probo/pkg/coredata.BusinessImpactCritical") -} - -enum VendorOrderField - @goModel(model: "go.probo.inc/probo/pkg/coredata.VendorOrderField") { - NAME @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorOrderFieldName") - CREATED_AT - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorOrderFieldCreatedAt" - ) - UPDATED_AT - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorOrderFieldUpdatedAt" - ) -} - -enum VendorComplianceReportOrderField - @goModel( - model: "go.probo.inc/probo/pkg/coredata.VendorComplianceReportOrderField" - ) { - REPORT_DATE - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorComplianceReportOrderFieldReportDate" - ) - CREATED_AT - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorComplianceReportOrderFieldCreatedAt" - ) -} - -enum VendorContactOrderField - @goModel(model: "go.probo.inc/probo/pkg/coredata.VendorContactOrderField") { - CREATED_AT - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorContactOrderFieldCreatedAt" - ) - FULL_NAME - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorContactOrderFieldFullName" - ) - EMAIL - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorContactOrderFieldEmail" - ) -} - -enum VendorServiceOrderField - @goModel(model: "go.probo.inc/probo/pkg/coredata.VendorServiceOrderField") { - CREATED_AT - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorServiceOrderFieldCreatedAt" - ) - NAME - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorServiceOrderFieldName" - ) -} - -enum VendorRiskAssessmentOrderField - @goModel( - model: "go.probo.inc/probo/pkg/coredata.VendorRiskAssessmentOrderField" - ) { - CREATED_AT - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorRiskAssessmentOrderFieldCreatedAt" - ) - EXPIRES_AT - @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorRiskAssessmentOrderFieldExpiresAt" - ) -} - -input VendorOrder - @goModel( - model: "go.probo.inc/probo/pkg/server/api/console/v1/types.VendorOrderBy" - ) { - direction: OrderDirection! - field: VendorOrderField! -} - -input VendorComplianceReportOrder - @goModel( - model: "go.probo.inc/probo/pkg/server/api/console/v1/types.VendorComplianceReportOrderBy" - ) { - direction: OrderDirection! - field: VendorComplianceReportOrderField! -} - -input VendorContactOrder - @goModel( - model: "go.probo.inc/probo/pkg/server/api/console/v1/types.VendorContactOrderBy" - ) { - direction: OrderDirection! - field: VendorContactOrderField! -} - -input VendorServiceOrder - @goModel( - model: "go.probo.inc/probo/pkg/server/api/console/v1/types.VendorServiceOrderBy" - ) { - direction: OrderDirection! - field: VendorServiceOrderField! -} - -input VendorRiskAssessmentOrder { - field: VendorRiskAssessmentOrderField! - direction: OrderDirection! -} - -type Vendor implements Node { - id: ID! - name: String! - category: VendorCategory! - description: String - - organization: Organization! @goField(forceResolver: true) - - complianceReports( - first: Int - after: CursorKey - last: Int - before: CursorKey - orderBy: VendorComplianceReportOrder - ): VendorComplianceReportConnection! @goField(forceResolver: true) - - businessAssociateAgreement: VendorBusinessAssociateAgreement - @goField(forceResolver: true) - dataPrivacyAgreement: VendorDataPrivacyAgreement - @goField(forceResolver: true) - - contacts( - first: Int - after: CursorKey - last: Int - before: CursorKey - orderBy: VendorContactOrder - ): VendorContactConnection! @goField(forceResolver: true) - - services( - first: Int - after: CursorKey - last: Int - before: CursorKey - orderBy: VendorServiceOrder - ): VendorServiceConnection! @goField(forceResolver: true) - - riskAssessments( - first: Int - after: CursorKey - last: Int - before: CursorKey - orderBy: VendorRiskAssessmentOrder - ): VendorRiskAssessmentConnection! @goField(forceResolver: true) - - businessOwner: Profile @goField(forceResolver: true) - securityOwner: Profile @goField(forceResolver: true) - - statusPageUrl: String - termsOfServiceUrl: String - privacyPolicyUrl: String - serviceLevelAgreementUrl: String - dataProcessingAgreementUrl: String - businessAssociateAgreementUrl: String - subprocessorsListUrl: String - certifications: [String!]! - countries: [CountryCode!]! - securityPageUrl: String - trustPageUrl: String - headquarterAddress: String - legalName: String - websiteUrl: String - showOnTrustCenter: Boolean! - createdAt: Datetime! - updatedAt: Datetime! - - permission(action: String!): Boolean! @goField(forceResolver: true) -} - -type VendorComplianceReport implements Node { - id: ID! - vendor: Vendor! @goField(forceResolver: true) - reportDate: Datetime! - validUntil: Datetime - reportName: String! - file: File @goField(forceResolver: true) - createdAt: Datetime! - updatedAt: Datetime! - - permission(action: String!): Boolean! @goField(forceResolver: true) -} - -type VendorBusinessAssociateAgreement implements Node { - id: ID! - vendor: Vendor! @goField(forceResolver: true) - validFrom: Datetime - validUntil: Datetime - fileName: String! - fileUrl: String! @goField(forceResolver: true) - fileSize: BigInt! - createdAt: Datetime! - updatedAt: Datetime! - - permission(action: String!): Boolean! @goField(forceResolver: true) -} - -type VendorContact implements Node { - id: ID! - vendor: Vendor! @goField(forceResolver: true) - fullName: String - email: EmailAddr - phone: String - role: String - createdAt: Datetime! - updatedAt: Datetime! - - permission(action: String!): Boolean! @goField(forceResolver: true) -} - -type VendorService implements Node { - id: ID! - vendor: Vendor! @goField(forceResolver: true) - name: String! - description: String - createdAt: Datetime! - updatedAt: Datetime! - - permission(action: String!): Boolean! @goField(forceResolver: true) -} - -type VendorDataPrivacyAgreement implements Node { - id: ID! - vendor: Vendor! @goField(forceResolver: true) - validFrom: Datetime - validUntil: Datetime - fileName: String! - fileUrl: String! @goField(forceResolver: true) - fileSize: BigInt! - createdAt: Datetime! - updatedAt: Datetime! - - permission(action: String!): Boolean! @goField(forceResolver: true) -} - -type VendorRiskAssessment implements Node { - id: ID! - vendor: Vendor! @goField(forceResolver: true) - expiresAt: Datetime! - dataSensitivity: DataSensitivity! - businessImpact: BusinessImpact! - notes: String - createdAt: Datetime! - updatedAt: Datetime! - - permission(action: String!): Boolean! @goField(forceResolver: true) -} - -type VendorConnection - @goModel( - model: "go.probo.inc/probo/pkg/server/api/console/v1/types.VendorConnection" - ) { - totalCount: Int! @goField(forceResolver: true) - edges: [VendorEdge!]! - pageInfo: PageInfo! -} - -type VendorEdge { - cursor: CursorKey! - node: Vendor! -} - -type VendorComplianceReportConnection { - edges: [VendorComplianceReportEdge!]! - pageInfo: PageInfo! -} - -type VendorComplianceReportEdge { - cursor: CursorKey! - node: VendorComplianceReport! -} - -type VendorContactConnection { - edges: [VendorContactEdge!]! - pageInfo: PageInfo! -} - -type VendorContactEdge { - cursor: CursorKey! - node: VendorContact! -} - -type VendorServiceConnection { - edges: [VendorServiceEdge!]! - pageInfo: PageInfo! -} - -type VendorServiceEdge { - cursor: CursorKey! - node: VendorService! -} - -type VendorRiskAssessmentConnection { - edges: [VendorRiskAssessmentEdge!]! - pageInfo: PageInfo! -} - -type VendorRiskAssessmentEdge { - cursor: CursorKey! - node: VendorRiskAssessment! -} - -extend type Mutation { - createVendor(input: CreateVendorInput!): CreateVendorPayload! - updateVendor(input: UpdateVendorInput!): UpdateVendorPayload! - deleteVendor(input: DeleteVendorInput!): DeleteVendorPayload! - createVendorContact( - input: CreateVendorContactInput! - ): CreateVendorContactPayload! - updateVendorContact( - input: UpdateVendorContactInput! - ): UpdateVendorContactPayload! - deleteVendorContact( - input: DeleteVendorContactInput! - ): DeleteVendorContactPayload! - createVendorService( - input: CreateVendorServiceInput! - ): CreateVendorServicePayload! - updateVendorService( - input: UpdateVendorServiceInput! - ): UpdateVendorServicePayload! - deleteVendorService( - input: DeleteVendorServiceInput! - ): DeleteVendorServicePayload! - uploadVendorComplianceReport( - input: UploadVendorComplianceReportInput! - ): UploadVendorComplianceReportPayload! - deleteVendorComplianceReport( - input: DeleteVendorComplianceReportInput! - ): DeleteVendorComplianceReportPayload! - uploadVendorBusinessAssociateAgreement( - input: UploadVendorBusinessAssociateAgreementInput! - ): UploadVendorBusinessAssociateAgreementPayload! - updateVendorBusinessAssociateAgreement( - input: UpdateVendorBusinessAssociateAgreementInput! - ): UpdateVendorBusinessAssociateAgreementPayload! - deleteVendorBusinessAssociateAgreement( - input: DeleteVendorBusinessAssociateAgreementInput! - ): DeleteVendorBusinessAssociateAgreementPayload! - uploadVendorDataPrivacyAgreement( - input: UploadVendorDataPrivacyAgreementInput! - ): UploadVendorDataPrivacyAgreementPayload! - updateVendorDataPrivacyAgreement( - input: UpdateVendorDataPrivacyAgreementInput! - ): UpdateVendorDataPrivacyAgreementPayload! - deleteVendorDataPrivacyAgreement( - input: DeleteVendorDataPrivacyAgreementInput! - ): DeleteVendorDataPrivacyAgreementPayload! - createVendorRiskAssessment( - input: CreateVendorRiskAssessmentInput! - ): CreateVendorRiskAssessmentPayload! - assessVendor(input: AssessVendorInput!): AssessVendorPayload! - publishVendorList( - input: PublishVendorListInput! - ): PublishVendorListPayload! -} - -input PublishVendorListInput { - organizationId: ID! - approverIds: [ID!] - minor: Boolean! -} - -type PublishVendorListPayload { - documentEdge: DocumentEdge! - documentVersionEdge: DocumentVersionEdge! -} - -input CreateVendorInput { - organizationId: ID! - name: String! - description: String - headquarterAddress: String - legalName: String - websiteUrl: String - privacyPolicyUrl: String - category: VendorCategory - serviceLevelAgreementUrl: String - dataProcessingAgreementUrl: String - businessAssociateAgreementUrl: String - subprocessorsListUrl: String - certifications: [String!] - countries: [CountryCode!] - securityPageUrl: String - trustPageUrl: String - statusPageUrl: String - termsOfServiceUrl: String - businessOwnerId: ID - securityOwnerId: ID -} - -input UpdateVendorInput { - id: ID! - name: String - description: String @goField(omittable: true) - statusPageUrl: String @goField(omittable: true) - termsOfServiceUrl: String @goField(omittable: true) - privacyPolicyUrl: String @goField(omittable: true) - serviceLevelAgreementUrl: String @goField(omittable: true) - dataProcessingAgreementUrl: String @goField(omittable: true) - businessAssociateAgreementUrl: String @goField(omittable: true) - subprocessorsListUrl: String @goField(omittable: true) - websiteUrl: String @goField(omittable: true) - legalName: String @goField(omittable: true) - headquarterAddress: String @goField(omittable: true) - category: VendorCategory - certifications: [String!] - countries: [CountryCode!] - securityPageUrl: String @goField(omittable: true) - trustPageUrl: String @goField(omittable: true) - businessOwnerId: ID @goField(omittable: true) - securityOwnerId: ID @goField(omittable: true) - showOnTrustCenter: Boolean -} - -input DeleteVendorInput { - vendorId: ID! -} - -input CreateVendorContactInput { - vendorId: ID! - fullName: String - email: EmailAddr - phone: String - role: String -} - -input UpdateVendorContactInput { - id: ID! - fullName: String @goField(omittable: true) - email: EmailAddr @goField(omittable: true) - phone: String @goField(omittable: true) - role: String @goField(omittable: true) -} - -input DeleteVendorContactInput { - vendorContactId: ID! -} - -input CreateVendorServiceInput { - vendorId: ID! - name: String! - description: String - url: String - type: String -} - -input UpdateVendorServiceInput { - id: ID! - name: String - description: String @goField(omittable: true) - url: String - type: String -} - -input DeleteVendorServiceInput { - vendorServiceId: ID! -} - -input UploadVendorComplianceReportInput { - vendorId: ID! - reportDate: Datetime! - validUntil: Datetime - reportName: String! - file: Upload! -} - -input DeleteVendorComplianceReportInput { - reportId: ID! -} - -input UploadVendorBusinessAssociateAgreementInput { - vendorId: ID! - validFrom: Datetime - validUntil: Datetime - fileName: String! - file: Upload! -} - -input UpdateVendorBusinessAssociateAgreementInput { - vendorId: ID! - validFrom: Datetime @goField(omittable: true) - validUntil: Datetime @goField(omittable: true) -} - -input DeleteVendorBusinessAssociateAgreementInput { - vendorId: ID! -} - -input UploadVendorDataPrivacyAgreementInput { - vendorId: ID! - validFrom: Datetime - validUntil: Datetime - fileName: String! - file: Upload! -} - -input UpdateVendorDataPrivacyAgreementInput { - vendorId: ID! - validFrom: Datetime @goField(omittable: true) - validUntil: Datetime @goField(omittable: true) -} - -input DeleteVendorDataPrivacyAgreementInput { - vendorId: ID! -} - -input CreateVendorRiskAssessmentInput { - vendorId: ID! - expiresAt: Datetime! - dataSensitivity: DataSensitivity! - businessImpact: BusinessImpact! - notes: String -} - -input AssessVendorInput { - id: ID! - websiteUrl: String! - procedure: String -} - -type VendorSubprocessor { - name: String! - country: String! - purpose: String! -} - -type CreateVendorPayload { - vendorEdge: VendorEdge! -} - -type UpdateVendorPayload { - vendor: Vendor! -} - -type DeleteVendorPayload { - deletedVendorId: ID! -} - -type CreateVendorContactPayload { - vendorContactEdge: VendorContactEdge! -} - -type UpdateVendorContactPayload { - vendorContact: VendorContact! -} - -type DeleteVendorContactPayload { - deletedVendorContactId: ID! -} - -type CreateVendorServicePayload { - vendorServiceEdge: VendorServiceEdge! -} - -type UpdateVendorServicePayload { - vendorService: VendorService! -} - -type DeleteVendorServicePayload { - deletedVendorServiceId: ID! -} - -type UploadVendorComplianceReportPayload { - vendorComplianceReportEdge: VendorComplianceReportEdge! -} - -type DeleteVendorComplianceReportPayload { - deletedVendorComplianceReportId: ID! -} - -type UploadVendorBusinessAssociateAgreementPayload { - vendorBusinessAssociateAgreement: VendorBusinessAssociateAgreement! -} - -type UpdateVendorBusinessAssociateAgreementPayload { - vendorBusinessAssociateAgreement: VendorBusinessAssociateAgreement! -} - -type DeleteVendorBusinessAssociateAgreementPayload { - deletedVendorId: ID! -} - -type UploadVendorDataPrivacyAgreementPayload { - vendorDataPrivacyAgreement: VendorDataPrivacyAgreement! -} - -type UpdateVendorDataPrivacyAgreementPayload { - vendorDataPrivacyAgreement: VendorDataPrivacyAgreement! -} - -type DeleteVendorDataPrivacyAgreementPayload { - deletedVendorId: ID! -} - -type CreateVendorRiskAssessmentPayload { - vendorRiskAssessmentEdge: VendorRiskAssessmentEdge! -} - -type AssessVendorPayload { - vendor: Vendor! - report: String! - subprocessors: [VendorSubprocessor!]! -} diff --git a/pkg/server/api/console/v1/graphql/webhook.graphql b/pkg/server/api/console/v1/graphql/webhook.graphql index 3af9d7bb0..c012f8898 100644 --- a/pkg/server/api/console/v1/graphql/webhook.graphql +++ b/pkg/server/api/console/v1/graphql/webhook.graphql @@ -1,11 +1,11 @@ enum WebhookEventType @goModel(model: "go.probo.inc/probo/pkg/coredata.WebhookEventType") { - VENDOR_CREATED - @goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeVendorCreated") - VENDOR_UPDATED - @goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeVendorUpdated") - VENDOR_DELETED - @goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeVendorDeleted") + THIRD_PARTY_CREATED + @goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeThirdPartyCreated") + THIRD_PARTY_UPDATED + @goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeThirdPartyUpdated") + THIRD_PARTY_DELETED + @goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeThirdPartyDeleted") USER_CREATED @goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeUserCreated") USER_UPDATED diff --git a/pkg/server/api/console/v1/organization_resolvers.go b/pkg/server/api/console/v1/organization_resolvers.go index b00b1bf0c..6687e6c88 100644 --- a/pkg/server/api/console/v1/organization_resolvers.go +++ b/pkg/server/api/console/v1/organization_resolvers.go @@ -1208,20 +1208,20 @@ func (r *organizationResolver) CookieBanners(ctx context.Context, obj *types.Org return types.NewCookieBannerConnection(p, r, obj.ID), nil } -// Vendors is the resolver for the vendors field. -func (r *organizationResolver) Vendors(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorOrderBy) (*types.VendorConnection, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionVendorList); err != nil { +// ThirdParties is the resolver for the thirdParties field. +func (r *organizationResolver) ThirdParties(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ThirdPartyOrderBy) (*types.ThirdPartyConnection, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionThirdPartyList); err != nil { return nil, err } prb := r.ProboService(ctx, obj.ID.TenantID()) - pageOrderBy := page.OrderBy[coredata.VendorOrderField]{ - Field: coredata.VendorOrderFieldCreatedAt, + pageOrderBy := page.OrderBy[coredata.ThirdPartyOrderField]{ + Field: coredata.ThirdPartyOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, } if orderBy != nil { - pageOrderBy = page.OrderBy[coredata.VendorOrderField]{ + pageOrderBy = page.OrderBy[coredata.ThirdPartyOrderField]{ Field: orderBy.Field, Direction: orderBy.Direction, } @@ -1229,28 +1229,28 @@ func (r *organizationResolver) Vendors(ctx context.Context, obj *types.Organizat cursor := types.NewCursor(first, after, last, before, pageOrderBy) - vendorFilter := coredata.NewVendorFilter(nil) + thirdPartyFilter := coredata.NewThirdPartyFilter(nil) - page, err := prb.Vendors.ListForOrganizationID(ctx, obj.ID, cursor, vendorFilter) + page, err := prb.ThirdParties.ListForOrganizationID(ctx, obj.ID, cursor, thirdPartyFilter) if err != nil { - r.logger.ErrorCtx(ctx, "cannot list organization vendors", log.Error(err)) + r.logger.ErrorCtx(ctx, "cannot list organization thirdParties", log.Error(err)) return nil, gqlutils.Internal(ctx) } - return types.NewVendorConnection(page, r, obj.ID), nil + return types.NewThirdPartyConnection(page, r, obj.ID), nil } -// VendorsDocument is the resolver for the vendorsDocument field. -func (r *organizationResolver) VendorsDocument(ctx context.Context, obj *types.Organization) (*types.Document, error) { +// ThirdPartiesDocument is the resolver for the thirdPartiesDocument field. +func (r *organizationResolver) ThirdPartiesDocument(ctx context.Context, obj *types.Organization) (*types.Document, error) { if err := r.authorize(ctx, obj.ID, probo.ActionDocumentGet); err != nil { return nil, err } prb := r.ProboService(ctx, obj.ID.TenantID()) - documentID, err := prb.GeneratedDocuments.GetVendorsDocumentID(ctx, obj.ID) + documentID, err := prb.GeneratedDocuments.GetThirdPartiesDocumentID(ctx, obj.ID) if err != nil { - r.logger.ErrorCtx(ctx, "cannot get vendors document ID", log.Error(err)) + r.logger.ErrorCtx(ctx, "cannot get thirdParties document ID", log.Error(err)) return nil, gqlutils.Internal(ctx) } if documentID == nil { @@ -1262,7 +1262,7 @@ func (r *organizationResolver) VendorsDocument(ctx context.Context, obj *types.O if errors.Is(err, coredata.ErrResourceNotFound) { return nil, nil } - r.logger.ErrorCtx(ctx, "cannot load vendors document", log.Error(err)) + r.logger.ErrorCtx(ctx, "cannot load thirdParties document", log.Error(err)) return nil, gqlutils.Internal(ctx) } diff --git a/pkg/server/api/console/v1/processing_activity_resolvers.go b/pkg/server/api/console/v1/processing_activity_resolvers.go index e7d6a167c..ea1024f98 100644 --- a/pkg/server/api/console/v1/processing_activity_resolvers.go +++ b/pkg/server/api/console/v1/processing_activity_resolvers.go @@ -49,7 +49,7 @@ func (r *mutationResolver) CreateProcessingActivity(ctx context.Context, input t NextReviewDate: input.NextReviewDate, Role: input.Role, DataProtectionOfficerID: input.DataProtectionOfficerID, - VendorIDs: input.VendorIds, + ThirdPartyIDs: input.ThirdPartyIds, } activity, err := prb.ProcessingActivities.Create(ctx, &req) @@ -91,7 +91,7 @@ func (r *mutationResolver) UpdateProcessingActivity(ctx context.Context, input t NextReviewDate: gqlutils.UnwrapOmittable(input.NextReviewDate), Role: input.Role, DataProtectionOfficerID: gqlutils.UnwrapOmittable(input.DataProtectionOfficerID), - VendorIDs: &input.VendorIds, + ThirdPartyIDs: &input.ThirdPartyIds, } activity, err := prb.ProcessingActivities.Update(ctx, &req) @@ -196,20 +196,20 @@ func (r *processingActivityResolver) DataProtectionOfficer(ctx context.Context, return types.NewProfile(dpo), nil } -// Vendors is the resolver for the vendors field. -func (r *processingActivityResolver) Vendors(ctx context.Context, obj *types.ProcessingActivity, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorOrderBy) (*types.VendorConnection, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionVendorList); err != nil { +// ThirdParties is the resolver for the thirdParties field. +func (r *processingActivityResolver) ThirdParties(ctx context.Context, obj *types.ProcessingActivity, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ThirdPartyOrderBy) (*types.ThirdPartyConnection, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionThirdPartyList); err != nil { return nil, err } prb := r.ProboService(ctx, obj.ID.TenantID()) - pageOrderBy := page.OrderBy[coredata.VendorOrderField]{ - Field: coredata.VendorOrderFieldCreatedAt, + pageOrderBy := page.OrderBy[coredata.ThirdPartyOrderField]{ + Field: coredata.ThirdPartyOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, } if orderBy != nil { - pageOrderBy = page.OrderBy[coredata.VendorOrderField]{ + pageOrderBy = page.OrderBy[coredata.ThirdPartyOrderField]{ Field: orderBy.Field, Direction: orderBy.Direction, } @@ -217,13 +217,13 @@ func (r *processingActivityResolver) Vendors(ctx context.Context, obj *types.Pro cursor := types.NewCursor(first, after, last, before, pageOrderBy) - page, err := prb.Vendors.ListForProcessingActivityID(ctx, obj.ID, cursor) + page, err := prb.ThirdParties.ListForProcessingActivityID(ctx, obj.ID, cursor) if err != nil { - r.logger.ErrorCtx(ctx, "cannot list processing activity vendors", log.Error(err)) + r.logger.ErrorCtx(ctx, "cannot list processing activity thirdParties", log.Error(err)) return nil, gqlutils.Internal(ctx) } - return types.NewVendorConnection(page, r, obj.ID), nil + return types.NewThirdPartyConnection(page, r, obj.ID), nil } // DataProtectionImpactAssessment is the resolver for the dataProtectionImpactAssessment field. diff --git a/pkg/server/api/console/v1/third_party_resolvers.go b/pkg/server/api/console/v1/third_party_resolvers.go new file mode 100644 index 000000000..fcaa1ddb3 --- /dev/null +++ b/pkg/server/api/console/v1/third_party_resolvers.go @@ -0,0 +1,1140 @@ +package console_v1 + +// This file will be automatically regenerated based on the schema, any resolver +// implementations +// will be copied through when generating and any unknown code will be moved to the end. +// Code generated by github.com/99designs/gqlgen version v0.17.90 + +import ( + "context" + "errors" + "fmt" + "time" + + pgx "github.com/jackc/pgx/v5" + "github.com/vikstrous/dataloadgen" + "go.gearno.de/kit/log" + "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/iam" + "go.probo.inc/probo/pkg/page" + "go.probo.inc/probo/pkg/probo" + "go.probo.inc/probo/pkg/server/api/console/v1/dataloader" + "go.probo.inc/probo/pkg/server/api/console/v1/schema" + "go.probo.inc/probo/pkg/server/api/console/v1/types" + "go.probo.inc/probo/pkg/server/gqlutils" + "go.probo.inc/probo/pkg/validator" +) + +// CreateThirdParty is the resolver for the createThirdParty field. +func (r *mutationResolver) CreateThirdParty(ctx context.Context, input types.CreateThirdPartyInput) (*types.CreateThirdPartyPayload, error) { + if err := r.authorize(ctx, input.OrganizationID, probo.ActionThirdPartyCreate); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.OrganizationID.TenantID()) + + thirdParty, err := prb.ThirdParties.Create( + ctx, + probo.CreateThirdPartyRequest{ + OrganizationID: input.OrganizationID, + Name: input.Name, + Description: input.Description, + StatusPageURL: input.StatusPageURL, + TermsOfServiceURL: input.TermsOfServiceURL, + PrivacyPolicyURL: input.PrivacyPolicyURL, + ServiceLevelAgreementURL: input.ServiceLevelAgreementURL, + LegalName: input.LegalName, + HeadquarterAddress: input.HeadquarterAddress, + WebsiteURL: input.WebsiteURL, + Category: input.Category, + DataProcessingAgreementURL: input.DataProcessingAgreementURL, + BusinessAssociateAgreementURL: input.BusinessAssociateAgreementURL, + SubprocessorsListURL: input.SubprocessorsListURL, + Certifications: input.Certifications, + SecurityPageURL: input.SecurityPageURL, + TrustPageURL: input.TrustPageURL, + BusinessOwnerID: input.BusinessOwnerID, + SecurityOwnerID: input.SecurityOwnerID, + Countries: input.Countries, + }, + ) + if err != nil { + if errors.Is(err, coredata.ErrResourceAlreadyExists) { + return nil, gqlutils.Conflict(ctx, err) + } + + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + r.logger.ErrorCtx(ctx, "cannot create thirdParty", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + return &types.CreateThirdPartyPayload{ + ThirdPartyEdge: types.NewThirdPartyEdge(thirdParty, coredata.ThirdPartyOrderFieldName), + }, nil +} + +// UpdateThirdParty is the resolver for the updateThirdParty field. +func (r *mutationResolver) UpdateThirdParty(ctx context.Context, input types.UpdateThirdPartyInput) (*types.UpdateThirdPartyPayload, error) { + if err := r.authorize(ctx, input.ID, probo.ActionThirdPartyUpdate); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.ID.TenantID()) + + thirdParty, err := prb.ThirdParties.Update( + ctx, + probo.UpdateThirdPartyRequest{ + ID: input.ID, + Name: input.Name, + Description: gqlutils.UnwrapOmittable(input.Description), + StatusPageURL: gqlutils.UnwrapOmittable(input.StatusPageURL), + TermsOfServiceURL: gqlutils.UnwrapOmittable(input.TermsOfServiceURL), + PrivacyPolicyURL: gqlutils.UnwrapOmittable(input.PrivacyPolicyURL), + ServiceLevelAgreementURL: gqlutils.UnwrapOmittable(input.ServiceLevelAgreementURL), + DataProcessingAgreementURL: gqlutils.UnwrapOmittable(input.DataProcessingAgreementURL), + BusinessAssociateAgreementURL: gqlutils.UnwrapOmittable(input.BusinessAssociateAgreementURL), + SubprocessorsListURL: gqlutils.UnwrapOmittable(input.SubprocessorsListURL), + SecurityPageURL: gqlutils.UnwrapOmittable(input.SecurityPageURL), + TrustPageURL: gqlutils.UnwrapOmittable(input.TrustPageURL), + HeadquarterAddress: gqlutils.UnwrapOmittable(input.HeadquarterAddress), + LegalName: gqlutils.UnwrapOmittable(input.LegalName), + WebsiteURL: gqlutils.UnwrapOmittable(input.WebsiteURL), + Category: input.Category, + Certifications: input.Certifications, + BusinessOwnerID: gqlutils.UnwrapOmittable(input.BusinessOwnerID), + SecurityOwnerID: gqlutils.UnwrapOmittable(input.SecurityOwnerID), + ShowOnTrustCenter: input.ShowOnTrustCenter, + Countries: input.Countries, + }, + ) + if err != nil { + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + r.logger.ErrorCtx(ctx, "cannot update thirdParty", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.UpdateThirdPartyPayload{ + ThirdParty: types.NewThirdParty(thirdParty), + }, nil +} + +// DeleteThirdParty is the resolver for the deleteThirdParty field. +func (r *mutationResolver) DeleteThirdParty(ctx context.Context, input types.DeleteThirdPartyInput) (*types.DeleteThirdPartyPayload, error) { + if err := r.authorize(ctx, input.ThirdPartyID, probo.ActionThirdPartyDelete); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.ThirdPartyID.TenantID()) + + err := prb.ThirdParties.Delete(ctx, input.ThirdPartyID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot delete thirdParty", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.DeleteThirdPartyPayload{ + DeletedThirdPartyID: input.ThirdPartyID, + }, nil +} + +// CreateThirdPartyContact is the resolver for the createThirdPartyContact field. +func (r *mutationResolver) CreateThirdPartyContact(ctx context.Context, input types.CreateThirdPartyContactInput) (*types.CreateThirdPartyContactPayload, error) { + if err := r.authorize(ctx, input.ThirdPartyID, probo.ActionThirdPartyContactCreate); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.ThirdPartyID.TenantID()) + + req := probo.CreateThirdPartyContactRequest{ + ThirdPartyID: input.ThirdPartyID, + FullName: input.FullName, + Email: input.Email, + Phone: input.Phone, + Role: input.Role, + } + + thirdPartyContact, err := prb.ThirdPartyContacts.Create(ctx, req) + if err != nil { + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + r.logger.ErrorCtx(ctx, "cannot create thirdParty contact", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.CreateThirdPartyContactPayload{ + ThirdPartyContactEdge: types.NewThirdPartyContactEdge(thirdPartyContact, coredata.ThirdPartyContactOrderFieldCreatedAt), + }, nil +} + +// UpdateThirdPartyContact is the resolver for the updateThirdPartyContact field. +func (r *mutationResolver) UpdateThirdPartyContact(ctx context.Context, input types.UpdateThirdPartyContactInput) (*types.UpdateThirdPartyContactPayload, error) { + if err := r.authorize(ctx, input.ID, probo.ActionThirdPartyContactUpdate); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.ID.TenantID()) + + req := probo.UpdateThirdPartyContactRequest{ + ID: input.ID, + FullName: gqlutils.UnwrapOmittable(input.FullName), + Email: gqlutils.UnwrapOmittable(input.Email), + Phone: gqlutils.UnwrapOmittable(input.Phone), + Role: gqlutils.UnwrapOmittable(input.Role), + } + + thirdPartyContact, err := prb.ThirdPartyContacts.Update(ctx, req) + if err != nil { + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + r.logger.ErrorCtx(ctx, "cannot update thirdParty contact", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.UpdateThirdPartyContactPayload{ + ThirdPartyContact: types.NewThirdPartyContact(thirdPartyContact), + }, nil +} + +// DeleteThirdPartyContact is the resolver for the deleteThirdPartyContact field. +func (r *mutationResolver) DeleteThirdPartyContact(ctx context.Context, input types.DeleteThirdPartyContactInput) (*types.DeleteThirdPartyContactPayload, error) { + if err := r.authorize(ctx, input.ThirdPartyContactID, probo.ActionThirdPartyContactDelete); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.ThirdPartyContactID.TenantID()) + + err := prb.ThirdPartyContacts.Delete(ctx, input.ThirdPartyContactID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot delete thirdParty contact", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.DeleteThirdPartyContactPayload{ + DeletedThirdPartyContactID: input.ThirdPartyContactID, + }, nil +} + +// CreateThirdPartyService is the resolver for the createThirdPartyService field. +func (r *mutationResolver) CreateThirdPartyService(ctx context.Context, input types.CreateThirdPartyServiceInput) (*types.CreateThirdPartyServicePayload, error) { + if err := r.authorize(ctx, input.ThirdPartyID, probo.ActionThirdPartyServiceCreate); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.ThirdPartyID.TenantID()) + + req := probo.CreateThirdPartyServiceRequest{ + ThirdPartyID: input.ThirdPartyID, + Name: input.Name, + Description: input.Description, + } + + thirdPartyService, err := prb.ThirdPartyServices.Create(ctx, req) + if err != nil { + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + r.logger.ErrorCtx(ctx, "cannot create thirdParty service", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.CreateThirdPartyServicePayload{ + ThirdPartyServiceEdge: types.NewThirdPartyServiceEdge(thirdPartyService, coredata.ThirdPartyServiceOrderFieldCreatedAt), + }, nil +} + +// UpdateThirdPartyService is the resolver for the updateThirdPartyService field. +func (r *mutationResolver) UpdateThirdPartyService(ctx context.Context, input types.UpdateThirdPartyServiceInput) (*types.UpdateThirdPartyServicePayload, error) { + if err := r.authorize(ctx, input.ID, probo.ActionThirdPartyServiceUpdate); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.ID.TenantID()) + + req := probo.UpdateThirdPartyServiceRequest{ + ID: input.ID, + Name: input.Name, + Description: gqlutils.UnwrapOmittable(input.Description), + } + + thirdPartyService, err := prb.ThirdPartyServices.Update(ctx, req) + if err != nil { + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + r.logger.ErrorCtx(ctx, "cannot update thirdParty service", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.UpdateThirdPartyServicePayload{ + ThirdPartyService: types.NewThirdPartyService(thirdPartyService), + }, nil +} + +// DeleteThirdPartyService is the resolver for the deleteThirdPartyService field. +func (r *mutationResolver) DeleteThirdPartyService(ctx context.Context, input types.DeleteThirdPartyServiceInput) (*types.DeleteThirdPartyServicePayload, error) { + if err := r.authorize(ctx, input.ThirdPartyServiceID, probo.ActionThirdPartyServiceDelete); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.ThirdPartyServiceID.TenantID()) + + err := prb.ThirdPartyServices.Delete(ctx, input.ThirdPartyServiceID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot delete thirdParty service", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.DeleteThirdPartyServicePayload{ + DeletedThirdPartyServiceID: input.ThirdPartyServiceID, + }, nil +} + +// UploadThirdPartyComplianceReport is the resolver for the uploadThirdPartyComplianceReport field. +func (r *mutationResolver) UploadThirdPartyComplianceReport(ctx context.Context, input types.UploadThirdPartyComplianceReportInput) (*types.UploadThirdPartyComplianceReportPayload, error) { + if err := r.authorize(ctx, input.ThirdPartyID, probo.ActionThirdPartyComplianceReportUpload); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.ThirdPartyID.TenantID()) + + thirdPartyComplianceReport, err := prb.ThirdPartyComplianceReports.Upload( + ctx, + input.ThirdPartyID, + &probo.ThirdPartyComplianceReportCreateRequest{ + File: probo.FileUpload{Filename: input.File.Filename, Size: input.File.Size, Content: input.File.File, ContentType: input.File.ContentType}, + ReportDate: input.ReportDate, + ValidUntil: input.ValidUntil, + ReportName: input.ReportName, + }, + ) + if err != nil { + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + r.logger.ErrorCtx(ctx, "cannot upload thirdParty compliance report", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.UploadThirdPartyComplianceReportPayload{ + ThirdPartyComplianceReportEdge: types.NewThirdPartyComplianceReportEdge(thirdPartyComplianceReport, coredata.ThirdPartyComplianceReportOrderFieldCreatedAt), + }, nil +} + +// DeleteThirdPartyComplianceReport is the resolver for the deleteThirdPartyComplianceReport field. +func (r *mutationResolver) DeleteThirdPartyComplianceReport(ctx context.Context, input types.DeleteThirdPartyComplianceReportInput) (*types.DeleteThirdPartyComplianceReportPayload, error) { + if err := r.authorize(ctx, input.ReportID, probo.ActionThirdPartyComplianceReportDelete); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.ReportID.TenantID()) + + err := prb.ThirdPartyComplianceReports.Delete(ctx, input.ReportID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot delete thirdParty compliance report", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.DeleteThirdPartyComplianceReportPayload{ + DeletedThirdPartyComplianceReportID: input.ReportID, + }, nil +} + +// UploadThirdPartyBusinessAssociateAgreement is the resolver for the uploadThirdPartyBusinessAssociateAgreement field. +func (r *mutationResolver) UploadThirdPartyBusinessAssociateAgreement(ctx context.Context, input types.UploadThirdPartyBusinessAssociateAgreementInput) (*types.UploadThirdPartyBusinessAssociateAgreementPayload, error) { + if err := r.authorize(ctx, input.ThirdPartyID, probo.ActionThirdPartyBusinessAssociateAgreementUpload); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.ThirdPartyID.TenantID()) + + thirdPartyBusinessAssociateAgreement, file, err := prb.ThirdPartyBusinessAssociateAgreements.Upload( + ctx, + input.ThirdPartyID, + &probo.ThirdPartyBusinessAssociateAgreementCreateRequest{ + File: input.File.File, + ValidFrom: input.ValidFrom, + ValidUntil: input.ValidUntil, + FileName: input.FileName, + }, + ) + if err != nil { + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + r.logger.ErrorCtx(ctx, "cannot upload thirdParty business associate agreement", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.UploadThirdPartyBusinessAssociateAgreementPayload{ + ThirdPartyBusinessAssociateAgreement: types.NewThirdPartyBusinessAssociateAgreement(thirdPartyBusinessAssociateAgreement, file), + }, nil +} + +// UpdateThirdPartyBusinessAssociateAgreement is the resolver for the updateThirdPartyBusinessAssociateAgreement field. +func (r *mutationResolver) UpdateThirdPartyBusinessAssociateAgreement(ctx context.Context, input types.UpdateThirdPartyBusinessAssociateAgreementInput) (*types.UpdateThirdPartyBusinessAssociateAgreementPayload, error) { + if err := r.authorize(ctx, input.ThirdPartyID, probo.ActionThirdPartyBusinessAssociateAgreementUpdate); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.ThirdPartyID.TenantID()) + + thirdPartyBusinessAssociateAgreement, file, err := prb.ThirdPartyBusinessAssociateAgreements.Update( + ctx, + input.ThirdPartyID, + &probo.ThirdPartyBusinessAssociateAgreementUpdateRequest{ + ValidFrom: gqlutils.UnwrapOmittable(input.ValidFrom), + ValidUntil: gqlutils.UnwrapOmittable(input.ValidUntil), + }, + ) + if err != nil { + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + r.logger.ErrorCtx(ctx, "cannot update thirdParty business associate agreement", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.UpdateThirdPartyBusinessAssociateAgreementPayload{ + ThirdPartyBusinessAssociateAgreement: types.NewThirdPartyBusinessAssociateAgreement(thirdPartyBusinessAssociateAgreement, file), + }, nil +} + +// DeleteThirdPartyBusinessAssociateAgreement is the resolver for the deleteThirdPartyBusinessAssociateAgreement field. +func (r *mutationResolver) DeleteThirdPartyBusinessAssociateAgreement(ctx context.Context, input types.DeleteThirdPartyBusinessAssociateAgreementInput) (*types.DeleteThirdPartyBusinessAssociateAgreementPayload, error) { + if err := r.authorize(ctx, input.ThirdPartyID, probo.ActionThirdPartyBusinessAssociateAgreementDelete); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.ThirdPartyID.TenantID()) + + err := prb.ThirdPartyBusinessAssociateAgreements.DeleteByThirdPartyID(ctx, input.ThirdPartyID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot delete thirdParty business associate agreement", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.DeleteThirdPartyBusinessAssociateAgreementPayload{ + DeletedThirdPartyID: input.ThirdPartyID, + }, nil +} + +// UploadThirdPartyDataPrivacyAgreement is the resolver for the uploadThirdPartyDataPrivacyAgreement field. +func (r *mutationResolver) UploadThirdPartyDataPrivacyAgreement(ctx context.Context, input types.UploadThirdPartyDataPrivacyAgreementInput) (*types.UploadThirdPartyDataPrivacyAgreementPayload, error) { + if err := r.authorize(ctx, input.ThirdPartyID, probo.ActionThirdPartyDataPrivacyAgreementUpload); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.ThirdPartyID.TenantID()) + + thirdPartyDataPrivacyAgreement, file, err := prb.ThirdPartyDataPrivacyAgreements.Upload( + ctx, + input.ThirdPartyID, + &probo.ThirdPartyDataPrivacyAgreementCreateRequest{ + File: input.File.File, + ValidFrom: input.ValidFrom, + ValidUntil: input.ValidUntil, + FileName: input.FileName, + }, + ) + if err != nil { + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + r.logger.ErrorCtx(ctx, "cannot upload thirdParty data privacy agreement", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.UploadThirdPartyDataPrivacyAgreementPayload{ + ThirdPartyDataPrivacyAgreement: types.NewThirdPartyDataPrivacyAgreement(thirdPartyDataPrivacyAgreement, file), + }, nil +} + +// UpdateThirdPartyDataPrivacyAgreement is the resolver for the updateThirdPartyDataPrivacyAgreement field. +func (r *mutationResolver) UpdateThirdPartyDataPrivacyAgreement(ctx context.Context, input types.UpdateThirdPartyDataPrivacyAgreementInput) (*types.UpdateThirdPartyDataPrivacyAgreementPayload, error) { + if err := r.authorize(ctx, input.ThirdPartyID, probo.ActionThirdPartyDataPrivacyAgreementUpdate); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.ThirdPartyID.TenantID()) + + thirdPartyDataPrivacyAgreement, file, err := prb.ThirdPartyDataPrivacyAgreements.Update( + ctx, + input.ThirdPartyID, + &probo.ThirdPartyDataPrivacyAgreementUpdateRequest{ + ValidFrom: gqlutils.UnwrapOmittable(input.ValidFrom), + ValidUntil: gqlutils.UnwrapOmittable(input.ValidUntil), + }, + ) + if err != nil { + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + r.logger.ErrorCtx(ctx, "cannot update thirdParty data privacy agreement", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.UpdateThirdPartyDataPrivacyAgreementPayload{ + ThirdPartyDataPrivacyAgreement: types.NewThirdPartyDataPrivacyAgreement(thirdPartyDataPrivacyAgreement, file), + }, nil +} + +// DeleteThirdPartyDataPrivacyAgreement is the resolver for the deleteThirdPartyDataPrivacyAgreement field. +func (r *mutationResolver) DeleteThirdPartyDataPrivacyAgreement(ctx context.Context, input types.DeleteThirdPartyDataPrivacyAgreementInput) (*types.DeleteThirdPartyDataPrivacyAgreementPayload, error) { + if err := r.authorize(ctx, input.ThirdPartyID, probo.ActionThirdPartyDataPrivacyAgreementDelete); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.ThirdPartyID.TenantID()) + + err := prb.ThirdPartyDataPrivacyAgreements.DeleteByThirdPartyID(ctx, input.ThirdPartyID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot delete thirdParty data privacy agreement", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.DeleteThirdPartyDataPrivacyAgreementPayload{ + DeletedThirdPartyID: input.ThirdPartyID, + }, nil +} + +// CreateThirdPartyRiskAssessment is the resolver for the createThirdPartyRiskAssessment field. +func (r *mutationResolver) CreateThirdPartyRiskAssessment(ctx context.Context, input types.CreateThirdPartyRiskAssessmentInput) (*types.CreateThirdPartyRiskAssessmentPayload, error) { + if err := r.authorize(ctx, input.ThirdPartyID, probo.ActionThirdPartyRiskAssessmentCreate); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.ThirdPartyID.TenantID()) + + thirdPartyRiskAssessment, err := prb.ThirdParties.CreateRiskAssessment( + ctx, + probo.CreateThirdPartyRiskAssessmentRequest{ + ThirdPartyID: input.ThirdPartyID, + ExpiresAt: input.ExpiresAt, + DataSensitivity: input.DataSensitivity, + BusinessImpact: input.BusinessImpact, + Notes: input.Notes, + }, + ) + if err != nil { + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + r.logger.ErrorCtx(ctx, "cannot create thirdParty risk assessment", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.CreateThirdPartyRiskAssessmentPayload{ + ThirdPartyRiskAssessmentEdge: types.NewThirdPartyRiskAssessmentEdge(thirdPartyRiskAssessment, coredata.ThirdPartyRiskAssessmentOrderFieldCreatedAt), + }, nil +} + +// AssessThirdParty is the resolver for the assessThirdParty field. +func (r *mutationResolver) AssessThirdParty(ctx context.Context, input types.AssessThirdPartyInput) (*types.AssessThirdPartyPayload, error) { + if err := r.authorize(ctx, input.ID, probo.ActionThirdPartyAssess); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.ID.TenantID()) + + result, err := prb.ThirdParties.Assess( + ctx, + probo.AssessThirdPartyRequest{ + ID: input.ID, + WebsiteURL: input.WebsiteURL, + Procedure: input.Procedure, + }, + ) + if err != nil { + if errors.Is(err, probo.ErrThirdPartyAssessmentDisabled) { + return nil, gqlutils.Unavailable(ctx, probo.ErrThirdPartyAssessmentDisabled) + } + + r.logger.ErrorCtx(ctx, "cannot assess thirdParty", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.AssessThirdPartyPayload{ + ThirdParty: types.NewThirdParty(result.ThirdParty), + Report: result.Report, + Subprocessors: types.NewThirdPartySubprocessors(result.Subprocessors), + }, nil +} + +// PublishThirdPartyList is the resolver for the publishThirdPartyList field. +func (r *mutationResolver) PublishThirdPartyList(ctx context.Context, input types.PublishThirdPartyListInput) (*types.PublishThirdPartyListPayload, error) { + if err := r.authorize(ctx, input.OrganizationID, probo.ActionThirdPartyPublish); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.OrganizationID.TenantID()) + + document, documentVersion, err := prb.GeneratedDocuments.PublishThirdPartyList(ctx, input.OrganizationID, input.ApproverIds, input.Minor) + if err != nil { + if errors.Is(err, coredata.ErrResourceAlreadyExists) { + return nil, gqlutils.Conflict(ctx, err) + } + if errMinor, ok := errors.AsType[*probo.ErrCannotPublishMinorWithoutMajor](err); ok { + return nil, gqlutils.Invalid(ctx, errMinor) + } + r.logger.ErrorCtx(ctx, "cannot publish thirdParty list", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.PublishThirdPartyListPayload{ + DocumentEdge: types.NewDocumentEdge(document, coredata.DocumentOrderFieldCreatedAt), + DocumentVersionEdge: types.NewDocumentVersionEdge(documentVersion, coredata.DocumentVersionOrderFieldCreatedAt), + }, nil +} + +// Organization is the resolver for the organization field. +func (r *thirdPartyResolver) Organization(ctx context.Context, obj *types.ThirdParty) (*types.Organization, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil { + return nil, err + } + + loaders := dataloader.FromContext(ctx) + + organization, err := loaders.Organization.Load(ctx, obj.Organization.ID) + if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) { + return nil, gqlutils.NotFound(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewOrganization(organization), nil +} + +// ComplianceReports is the resolver for the complianceReports field. +func (r *thirdPartyResolver) ComplianceReports(ctx context.Context, obj *types.ThirdParty, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ThirdPartyComplianceReportOrderBy) (*types.ThirdPartyComplianceReportConnection, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionThirdPartyComplianceReportList); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, obj.ID.TenantID()) + + pageOrderBy := page.OrderBy[coredata.ThirdPartyComplianceReportOrderField]{ + Field: coredata.ThirdPartyComplianceReportOrderFieldReportDate, + Direction: page.OrderDirectionDesc, + } + if orderBy != nil { + pageOrderBy = page.OrderBy[coredata.ThirdPartyComplianceReportOrderField]{ + Field: orderBy.Field, + Direction: orderBy.Direction, + } + } + + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + + page, err := prb.ThirdPartyComplianceReports.ListForThirdPartyID(ctx, obj.ID, cursor) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot list thirdParty compliance reports", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewThirdPartyComplianceReportConnection(page), nil +} + +// BusinessAssociateAgreement is the resolver for the businessAssociateAgreement field. +func (r *thirdPartyResolver) BusinessAssociateAgreement(ctx context.Context, obj *types.ThirdParty) (*types.ThirdPartyBusinessAssociateAgreement, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionThirdPartyBusinessAssociateAgreementGet); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, obj.ID.TenantID()) + + thirdPartyBusinessAssociateAgreement, file, err := prb.ThirdPartyBusinessAssociateAgreements.GetByThirdPartyID(ctx, obj.ID) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return nil, nil + } + + r.logger.ErrorCtx(ctx, "cannot get thirdParty business associate agreement", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewThirdPartyBusinessAssociateAgreement(thirdPartyBusinessAssociateAgreement, file), nil +} + +// DataPrivacyAgreement is the resolver for the dataPrivacyAgreement field. +func (r *thirdPartyResolver) DataPrivacyAgreement(ctx context.Context, obj *types.ThirdParty) (*types.ThirdPartyDataPrivacyAgreement, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionThirdPartyDataPrivacyAgreementGet); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, obj.ID.TenantID()) + + thirdPartyDataPrivacyAgreement, file, err := prb.ThirdPartyDataPrivacyAgreements.GetByThirdPartyID(ctx, obj.ID) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return nil, nil + } + + r.logger.ErrorCtx(ctx, "cannot get thirdParty data privacy agreement", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewThirdPartyDataPrivacyAgreement(thirdPartyDataPrivacyAgreement, file), nil +} + +// Contacts is the resolver for the contacts field. +func (r *thirdPartyResolver) Contacts(ctx context.Context, obj *types.ThirdParty, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ThirdPartyContactOrderBy) (*types.ThirdPartyContactConnection, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionThirdPartyContactList); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, obj.ID.TenantID()) + + pageOrderBy := page.OrderBy[coredata.ThirdPartyContactOrderField]{ + Field: coredata.ThirdPartyContactOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if orderBy != nil { + pageOrderBy = page.OrderBy[coredata.ThirdPartyContactOrderField]{ + Field: orderBy.Field, + Direction: orderBy.Direction, + } + } + + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + + page, err := prb.ThirdPartyContacts.List(ctx, obj.ID, cursor) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot list thirdParty contacts", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewThirdPartyContactConnection(page), nil +} + +// Services is the resolver for the services field. +func (r *thirdPartyResolver) Services(ctx context.Context, obj *types.ThirdParty, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ThirdPartyServiceOrderBy) (*types.ThirdPartyServiceConnection, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionThirdPartyServiceList); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, obj.ID.TenantID()) + + pageOrderBy := page.OrderBy[coredata.ThirdPartyServiceOrderField]{ + Field: coredata.ThirdPartyServiceOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if orderBy != nil { + pageOrderBy = page.OrderBy[coredata.ThirdPartyServiceOrderField]{ + Field: orderBy.Field, + Direction: orderBy.Direction, + } + } + + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + + page, err := prb.ThirdPartyServices.List(ctx, obj.ID, cursor) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot list thirdParty services", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewThirdPartyServiceConnection(page), nil +} + +// RiskAssessments is the resolver for the riskAssessments field. +func (r *thirdPartyResolver) RiskAssessments(ctx context.Context, obj *types.ThirdParty, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ThirdPartyRiskAssessmentOrder) (*types.ThirdPartyRiskAssessmentConnection, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionThirdPartyRiskAssessmentList); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, obj.ID.TenantID()) + + pageOrderBy := page.OrderBy[coredata.ThirdPartyRiskAssessmentOrderField]{ + Field: coredata.ThirdPartyRiskAssessmentOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if orderBy != nil { + pageOrderBy = page.OrderBy[coredata.ThirdPartyRiskAssessmentOrderField]{ + Field: orderBy.Field, + Direction: orderBy.Direction, + } + } + + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + + page, err := prb.ThirdParties.ListRiskAssessments(ctx, obj.ID, cursor) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot list thirdParty risk assessments", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewThirdPartyRiskAssessmentConnection(page), nil +} + +// BusinessOwner is the resolver for the businessOwner field. +func (r *thirdPartyResolver) BusinessOwner(ctx context.Context, obj *types.ThirdParty) (*types.Profile, error) { + if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet); err != nil { + return nil, err + } + + if obj.BusinessOwner == nil { + return nil, nil + } + + loaders := dataloader.FromContext(ctx) + + businessOwner, err := loaders.Profile.Load(ctx, obj.BusinessOwner.ID) + if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) { + return nil, gqlutils.NotFound(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot get business owner", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewProfile(businessOwner), nil +} + +// SecurityOwner is the resolver for the securityOwner field. +func (r *thirdPartyResolver) SecurityOwner(ctx context.Context, obj *types.ThirdParty) (*types.Profile, error) { + if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet); err != nil { + return nil, err + } + + if obj.SecurityOwner == nil { + return nil, nil + } + + loaders := dataloader.FromContext(ctx) + + securityOwner, err := loaders.Profile.Load(ctx, obj.SecurityOwner.ID) + if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) { + return nil, gqlutils.NotFound(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot get security owner", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewProfile(securityOwner), nil +} + +// Permission is the resolver for the permission field. +func (r *thirdPartyResolver) Permission(ctx context.Context, obj *types.ThirdParty, action string) (bool, error) { + return r.Resolver.Permission(ctx, obj, action) +} + +// ThirdParty is the resolver for the thirdParty field. +func (r *thirdPartyBusinessAssociateAgreementResolver) ThirdParty(ctx context.Context, obj *types.ThirdPartyBusinessAssociateAgreement) (*types.ThirdParty, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionThirdPartyGet); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, obj.ID.TenantID()) + + thirdParty, err := prb.ThirdParties.Get(ctx, obj.ID) + if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return nil, gqlutils.NotFound(ctx, err) + } + + return nil, fmt.Errorf("cannot get thirdParty: %w", err) + } + + return types.NewThirdParty(thirdParty), nil +} + +// FileURL is the resolver for the fileUrl field. +func (r *thirdPartyBusinessAssociateAgreementResolver) FileURL(ctx context.Context, obj *types.ThirdPartyBusinessAssociateAgreement) (string, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionFileDownloadUrl); err != nil { + return "", err + } + + prb := r.ProboService(ctx, obj.ID.TenantID()) + + fileURL, err := prb.ThirdPartyBusinessAssociateAgreements.GenerateFileURL(ctx, obj.ID, 1*time.Hour) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot generate file URL", log.Error(err)) + return "", gqlutils.Internal(ctx) + } + + return fileURL, nil +} + +// Permission is the resolver for the permission field. +func (r *thirdPartyBusinessAssociateAgreementResolver) Permission(ctx context.Context, obj *types.ThirdPartyBusinessAssociateAgreement, action string) (bool, error) { + return r.Resolver.Permission(ctx, obj, action) +} + +// ThirdParty is the resolver for the thirdParty field. +func (r *thirdPartyComplianceReportResolver) ThirdParty(ctx context.Context, obj *types.ThirdPartyComplianceReport) (*types.ThirdParty, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionThirdPartyGet); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, obj.ID.TenantID()) + + thirdParty, err := prb.ThirdParties.Get(ctx, obj.ID) + if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return nil, gqlutils.NotFound(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot get thirdParty", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewThirdParty(thirdParty), nil +} + +// File is the resolver for the file field. +func (r *thirdPartyComplianceReportResolver) File(ctx context.Context, obj *types.ThirdPartyComplianceReport) (*types.File, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionFileGet); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, obj.ID.TenantID()) + + evidence, err := prb.ThirdPartyComplianceReports.Get(ctx, obj.ID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot load evidence", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + if evidence.ReportFileId == nil { + return nil, nil + } + + file, err := prb.Files.Get(ctx, *evidence.ReportFileId) + if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return nil, gqlutils.NotFound(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot load evidence file", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewFile(file), nil +} + +// Permission is the resolver for the permission field. +func (r *thirdPartyComplianceReportResolver) Permission(ctx context.Context, obj *types.ThirdPartyComplianceReport, action string) (bool, error) { + return r.Resolver.Permission(ctx, obj, action) +} + +// TotalCount is the resolver for the totalCount field. +func (r *thirdPartyConnectionResolver) TotalCount(ctx context.Context, obj *types.ThirdPartyConnection) (int, error) { + if err := r.authorize(ctx, obj.ParentID, probo.ActionThirdPartyList); err != nil { + return 0, err + } + + prb := r.ProboService(ctx, obj.ParentID.TenantID()) + + switch obj.Resolver.(type) { + case *organizationResolver: + count, err := prb.ThirdParties.CountForOrganizationID(ctx, obj.ParentID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot count thirdParties", log.Error(err)) + return 0, gqlutils.Internal(ctx) + } + return count, nil + case *assetResolver: + count, err := prb.ThirdParties.CountForAssetID(ctx, obj.ParentID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot count thirdParties", log.Error(err)) + return 0, gqlutils.Internal(ctx) + } + return count, nil + case *datumResolver: + count, err := prb.ThirdParties.CountForDatumID(ctx, obj.ParentID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot count thirdParties", log.Error(err)) + return 0, gqlutils.Internal(ctx) + } + return count, nil + } + + r.logger.ErrorCtx(ctx, "unsupported resolver") + return 0, gqlutils.Internal(ctx) +} + +// ThirdParty is the resolver for the thirdParty field. +func (r *thirdPartyContactResolver) ThirdParty(ctx context.Context, obj *types.ThirdPartyContact) (*types.ThirdParty, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionThirdPartyGet); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, obj.ID.TenantID()) + + // Get the thirdParty contact to access the ThirdPartyID + thirdPartyContact, err := prb.ThirdPartyContacts.Get(ctx, obj.ID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot get thirdParty contact", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + thirdParty, err := prb.ThirdParties.Get(ctx, thirdPartyContact.ThirdPartyID) + if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return nil, gqlutils.NotFound(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot get thirdParty", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewThirdParty(thirdParty), nil +} + +// Permission is the resolver for the permission field. +func (r *thirdPartyContactResolver) Permission(ctx context.Context, obj *types.ThirdPartyContact, action string) (bool, error) { + return r.Resolver.Permission(ctx, obj, action) +} + +// ThirdParty is the resolver for the thirdParty field. +func (r *thirdPartyDataPrivacyAgreementResolver) ThirdParty(ctx context.Context, obj *types.ThirdPartyDataPrivacyAgreement) (*types.ThirdParty, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionThirdPartyGet); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, obj.ID.TenantID()) + + thirdParty, err := prb.ThirdParties.Get(ctx, obj.ID) + if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return nil, gqlutils.NotFound(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot get thirdParty", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewThirdParty(thirdParty), nil +} + +// FileURL is the resolver for the fileUrl field. +func (r *thirdPartyDataPrivacyAgreementResolver) FileURL(ctx context.Context, obj *types.ThirdPartyDataPrivacyAgreement) (string, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionFileDownloadUrl); err != nil { + return "", err + } + + prb := r.ProboService(ctx, obj.ID.TenantID()) + + fileURL, err := prb.ThirdPartyDataPrivacyAgreements.GenerateFileURL(ctx, obj.ID, 1*time.Hour) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot generate file URL", log.Error(err)) + return "", gqlutils.Internal(ctx) + } + + return fileURL, nil +} + +// Permission is the resolver for the permission field. +func (r *thirdPartyDataPrivacyAgreementResolver) Permission(ctx context.Context, obj *types.ThirdPartyDataPrivacyAgreement, action string) (bool, error) { + return r.Resolver.Permission(ctx, obj, action) +} + +// ThirdParty is the resolver for the thirdParty field. +func (r *thirdPartyRiskAssessmentResolver) ThirdParty(ctx context.Context, obj *types.ThirdPartyRiskAssessment) (*types.ThirdParty, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionThirdPartyGet); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, obj.ID.TenantID()) + + thirdParty, err := prb.ThirdParties.GetByRiskAssessmentID(ctx, obj.ID) + if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return nil, gqlutils.NotFound(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot get thirdParty", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewThirdParty(thirdParty), nil +} + +// Permission is the resolver for the permission field. +func (r *thirdPartyRiskAssessmentResolver) Permission(ctx context.Context, obj *types.ThirdPartyRiskAssessment, action string) (bool, error) { + return r.Resolver.Permission(ctx, obj, action) +} + +// ThirdParty is the resolver for the thirdParty field. +func (r *thirdPartyServiceResolver) ThirdParty(ctx context.Context, obj *types.ThirdPartyService) (*types.ThirdParty, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionThirdPartyGet); err != nil { + return nil, err + } + + loaders := dataloader.FromContext(ctx) + + thirdParty, err := loaders.ThirdParty.Load(ctx, obj.ThirdParty.ID) + if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) { + return nil, gqlutils.NotFound(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot get thirdParty", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewThirdParty(thirdParty), nil +} + +// Permission is the resolver for the permission field. +func (r *thirdPartyServiceResolver) Permission(ctx context.Context, obj *types.ThirdPartyService, action string) (bool, error) { + return r.Resolver.Permission(ctx, obj, action) +} + +// ThirdParty returns schema.ThirdPartyResolver implementation. +func (r *Resolver) ThirdParty() schema.ThirdPartyResolver { return &thirdPartyResolver{r} } + +// ThirdPartyBusinessAssociateAgreement returns schema.ThirdPartyBusinessAssociateAgreementResolver implementation. +func (r *Resolver) ThirdPartyBusinessAssociateAgreement() schema.ThirdPartyBusinessAssociateAgreementResolver { + return &thirdPartyBusinessAssociateAgreementResolver{r} +} + +// ThirdPartyComplianceReport returns schema.ThirdPartyComplianceReportResolver implementation. +func (r *Resolver) ThirdPartyComplianceReport() schema.ThirdPartyComplianceReportResolver { + return &thirdPartyComplianceReportResolver{r} +} + +// ThirdPartyConnection returns schema.ThirdPartyConnectionResolver implementation. +func (r *Resolver) ThirdPartyConnection() schema.ThirdPartyConnectionResolver { + return &thirdPartyConnectionResolver{r} +} + +// ThirdPartyContact returns schema.ThirdPartyContactResolver implementation. +func (r *Resolver) ThirdPartyContact() schema.ThirdPartyContactResolver { + return &thirdPartyContactResolver{r} +} + +// ThirdPartyDataPrivacyAgreement returns schema.ThirdPartyDataPrivacyAgreementResolver implementation. +func (r *Resolver) ThirdPartyDataPrivacyAgreement() schema.ThirdPartyDataPrivacyAgreementResolver { + return &thirdPartyDataPrivacyAgreementResolver{r} +} + +// ThirdPartyRiskAssessment returns schema.ThirdPartyRiskAssessmentResolver implementation. +func (r *Resolver) ThirdPartyRiskAssessment() schema.ThirdPartyRiskAssessmentResolver { + return &thirdPartyRiskAssessmentResolver{r} +} + +// ThirdPartyService returns schema.ThirdPartyServiceResolver implementation. +func (r *Resolver) ThirdPartyService() schema.ThirdPartyServiceResolver { + return &thirdPartyServiceResolver{r} +} + +type thirdPartyResolver struct{ *Resolver } +type thirdPartyBusinessAssociateAgreementResolver struct{ *Resolver } +type thirdPartyComplianceReportResolver struct{ *Resolver } +type thirdPartyConnectionResolver struct{ *Resolver } +type thirdPartyContactResolver struct{ *Resolver } +type thirdPartyDataPrivacyAgreementResolver struct{ *Resolver } +type thirdPartyRiskAssessmentResolver struct{ *Resolver } +type thirdPartyServiceResolver struct{ *Resolver } diff --git a/pkg/server/api/console/v1/types/common_third_party.go b/pkg/server/api/console/v1/types/common_third_party.go index 9eac8469b..aa9d6c791 100644 --- a/pkg/server/api/console/v1/types/common_third_party.go +++ b/pkg/server/api/console/v1/types/common_third_party.go @@ -20,21 +20,21 @@ import ( ) type CommonThirdParty struct { - ID gid.GID `json:"id"` - Name string `json:"name"` - Category coredata.VendorCategory `json:"category"` - WebsiteURL *string `json:"websiteUrl,omitempty"` - HeadquarterAddress *string `json:"headquarterAddress,omitempty"` - LegalName *string `json:"legalName,omitempty"` - PrivacyPolicyURL *string `json:"privacyPolicyUrl,omitempty"` - ServiceLevelAgreementURL *string `json:"serviceLevelAgreementUrl,omitempty"` - DataProcessingAgreementURL *string `json:"dataProcessingAgreementUrl,omitempty"` - Certifications []string `json:"certifications"` - SecurityPageURL *string `json:"securityPageUrl,omitempty"` - TrustPageURL *string `json:"trustPageUrl,omitempty"` - StatusPageURL *string `json:"statusPageUrl,omitempty"` - TermsOfServiceURL *string `json:"termsOfServiceUrl,omitempty"` - LogoFileID *gid.GID `json:"logoFileId,omitempty"` + ID gid.GID `json:"id"` + Name string `json:"name"` + Category coredata.ThirdPartyCategory `json:"category"` + WebsiteURL *string `json:"websiteUrl,omitempty"` + HeadquarterAddress *string `json:"headquarterAddress,omitempty"` + LegalName *string `json:"legalName,omitempty"` + PrivacyPolicyURL *string `json:"privacyPolicyUrl,omitempty"` + ServiceLevelAgreementURL *string `json:"serviceLevelAgreementUrl,omitempty"` + DataProcessingAgreementURL *string `json:"dataProcessingAgreementUrl,omitempty"` + Certifications []string `json:"certifications"` + SecurityPageURL *string `json:"securityPageUrl,omitempty"` + TrustPageURL *string `json:"trustPageUrl,omitempty"` + StatusPageURL *string `json:"statusPageUrl,omitempty"` + TermsOfServiceURL *string `json:"termsOfServiceUrl,omitempty"` + LogoFileID *gid.GID `json:"logoFileId,omitempty"` } func NewCommonThirdParty(c *coredata.CommonThirdParty) *CommonThirdParty { diff --git a/pkg/server/api/console/v1/types/datum.go b/pkg/server/api/console/v1/types/datum.go index de1f318ad..edd1e0df1 100644 --- a/pkg/server/api/console/v1/types/datum.go +++ b/pkg/server/api/console/v1/types/datum.go @@ -28,7 +28,7 @@ type Datum struct { Name string `json:"name"` DataClassification coredata.DataClassification `json:"dataClassification"` Owner *Profile `json:"owner"` - Vendors *VendorConnection `json:"vendors"` + ThirdParties *ThirdPartyConnection `json:"third_parties"` Organization *Organization `json:"organization"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` diff --git a/pkg/server/api/console/v1/types/vendor.go b/pkg/server/api/console/v1/types/third_party.go similarity index 76% rename from pkg/server/api/console/v1/types/vendor.go rename to pkg/server/api/console/v1/types/third_party.go index 28e10e891..77cf85fc2 100644 --- a/pkg/server/api/console/v1/types/vendor.go +++ b/pkg/server/api/console/v1/types/third_party.go @@ -22,11 +22,11 @@ import ( ) type ( - VendorOrderBy OrderBy[coredata.VendorOrderField] + ThirdPartyOrderBy OrderBy[coredata.ThirdPartyOrderField] - VendorConnection struct { + ThirdPartyConnection struct { TotalCount int - Edges []*VendorEdge + Edges []*ThirdPartyEdge PageInfo PageInfo Resolver any @@ -34,18 +34,18 @@ type ( } ) -func NewVendorConnection( - p *page.Page[*coredata.Vendor, coredata.VendorOrderField], +func NewThirdPartyConnection( + p *page.Page[*coredata.ThirdParty, coredata.ThirdPartyOrderField], parentType any, parentID gid.GID, -) *VendorConnection { - var edges = make([]*VendorEdge, len(p.Data)) +) *ThirdPartyConnection { + var edges = make([]*ThirdPartyEdge, len(p.Data)) for i := range edges { - edges[i] = NewVendorEdge(p.Data[i], p.Cursor.OrderBy.Field) + edges[i] = NewThirdPartyEdge(p.Data[i], p.Cursor.OrderBy.Field) } - return &VendorConnection{ + return &ThirdPartyConnection{ Edges: edges, PageInfo: *NewPageInfo(p), @@ -54,15 +54,15 @@ func NewVendorConnection( } } -func NewVendorEdge(v *coredata.Vendor, orderBy coredata.VendorOrderField) *VendorEdge { - return &VendorEdge{ +func NewThirdPartyEdge(v *coredata.ThirdParty, orderBy coredata.ThirdPartyOrderField) *ThirdPartyEdge { + return &ThirdPartyEdge{ Cursor: v.CursorKey(orderBy), - Node: NewVendor(v), + Node: NewThirdParty(v), } } -func NewVendor(v *coredata.Vendor) *Vendor { - object := &Vendor{ +func NewThirdParty(v *coredata.ThirdParty) *ThirdParty { + object := &ThirdParty{ ID: v.ID, Organization: &Organization{ ID: v.OrganizationID, @@ -104,10 +104,10 @@ func NewVendor(v *coredata.Vendor) *Vendor { return object } -func NewVendorSubprocessors(sps []probo.Subprocessor) []*VendorSubprocessor { - result := make([]*VendorSubprocessor, len(sps)) +func NewThirdPartySubprocessors(sps []probo.Subprocessor) []*ThirdPartySubprocessor { + result := make([]*ThirdPartySubprocessor, len(sps)) for i, sp := range sps { - result[i] = &VendorSubprocessor{ + result[i] = &ThirdPartySubprocessor{ Name: sp.Name, Country: sp.Country, Purpose: sp.Purpose, diff --git a/pkg/server/api/console/v1/types/vendor_data_privacy_agreement.go b/pkg/server/api/console/v1/types/third_party_business_associate_agreement.go similarity index 80% rename from pkg/server/api/console/v1/types/vendor_data_privacy_agreement.go rename to pkg/server/api/console/v1/types/third_party_business_associate_agreement.go index cbe4b79ef..6ed8db85f 100644 --- a/pkg/server/api/console/v1/types/vendor_data_privacy_agreement.go +++ b/pkg/server/api/console/v1/types/third_party_business_associate_agreement.go @@ -18,11 +18,11 @@ import ( "go.probo.inc/probo/pkg/coredata" ) -func NewVendorDataPrivacyAgreement(v *coredata.VendorDataPrivacyAgreement, file *coredata.File) *VendorDataPrivacyAgreement { - return &VendorDataPrivacyAgreement{ +func NewThirdPartyBusinessAssociateAgreement(v *coredata.ThirdPartyBusinessAssociateAgreement, file *coredata.File) *ThirdPartyBusinessAssociateAgreement { + return &ThirdPartyBusinessAssociateAgreement{ ID: v.ID, - Vendor: &Vendor{ - ID: v.VendorID, + ThirdParty: &ThirdParty{ + ID: v.ThirdPartyID, }, ValidFrom: v.ValidFrom, ValidUntil: v.ValidUntil, diff --git a/pkg/server/api/console/v1/types/vendor_compliance_report.go b/pkg/server/api/console/v1/types/third_party_compliance_report.go similarity index 58% rename from pkg/server/api/console/v1/types/vendor_compliance_report.go rename to pkg/server/api/console/v1/types/third_party_compliance_report.go index 64b8d0a40..84cdc924e 100644 --- a/pkg/server/api/console/v1/types/vendor_compliance_report.go +++ b/pkg/server/api/console/v1/types/third_party_compliance_report.go @@ -20,34 +20,34 @@ import ( ) type ( - VendorComplianceReportOrderBy OrderBy[coredata.VendorComplianceReportOrderField] + ThirdPartyComplianceReportOrderBy OrderBy[coredata.ThirdPartyComplianceReportOrderField] ) -func NewVendorComplianceReportConnection(p *page.Page[*coredata.VendorComplianceReport, coredata.VendorComplianceReportOrderField]) *VendorComplianceReportConnection { - var edges = make([]*VendorComplianceReportEdge, len(p.Data)) +func NewThirdPartyComplianceReportConnection(p *page.Page[*coredata.ThirdPartyComplianceReport, coredata.ThirdPartyComplianceReportOrderField]) *ThirdPartyComplianceReportConnection { + var edges = make([]*ThirdPartyComplianceReportEdge, len(p.Data)) for i := range edges { - edges[i] = NewVendorComplianceReportEdge(p.Data[i], p.Cursor.OrderBy.Field) + edges[i] = NewThirdPartyComplianceReportEdge(p.Data[i], p.Cursor.OrderBy.Field) } - return &VendorComplianceReportConnection{ + return &ThirdPartyComplianceReportConnection{ Edges: edges, PageInfo: NewPageInfo(p), } } -func NewVendorComplianceReportEdge(c *coredata.VendorComplianceReport, orderBy coredata.VendorComplianceReportOrderField) *VendorComplianceReportEdge { - return &VendorComplianceReportEdge{ +func NewThirdPartyComplianceReportEdge(c *coredata.ThirdPartyComplianceReport, orderBy coredata.ThirdPartyComplianceReportOrderField) *ThirdPartyComplianceReportEdge { + return &ThirdPartyComplianceReportEdge{ Cursor: c.CursorKey(orderBy), - Node: NewVendorComplianceReport(c), + Node: NewThirdPartyComplianceReport(c), } } -func NewVendorComplianceReport(c *coredata.VendorComplianceReport) *VendorComplianceReport { - object := &VendorComplianceReport{ +func NewThirdPartyComplianceReport(c *coredata.ThirdPartyComplianceReport) *ThirdPartyComplianceReport { + object := &ThirdPartyComplianceReport{ ID: c.ID, - Vendor: &Vendor{ - ID: c.VendorID, + ThirdParty: &ThirdParty{ + ID: c.ThirdPartyID, }, ReportDate: c.ReportDate, ValidUntil: c.ValidUntil, diff --git a/pkg/server/api/console/v1/types/vendor_contact.go b/pkg/server/api/console/v1/types/third_party_contact.go similarity index 61% rename from pkg/server/api/console/v1/types/vendor_contact.go rename to pkg/server/api/console/v1/types/third_party_contact.go index 6505ebe0d..420654e9b 100644 --- a/pkg/server/api/console/v1/types/vendor_contact.go +++ b/pkg/server/api/console/v1/types/third_party_contact.go @@ -20,34 +20,34 @@ import ( ) type ( - VendorContactOrderBy OrderBy[coredata.VendorContactOrderField] + ThirdPartyContactOrderBy OrderBy[coredata.ThirdPartyContactOrderField] ) -func NewVendorContactConnection(p *page.Page[*coredata.VendorContact, coredata.VendorContactOrderField]) *VendorContactConnection { - var edges = make([]*VendorContactEdge, len(p.Data)) +func NewThirdPartyContactConnection(p *page.Page[*coredata.ThirdPartyContact, coredata.ThirdPartyContactOrderField]) *ThirdPartyContactConnection { + var edges = make([]*ThirdPartyContactEdge, len(p.Data)) for i := range edges { - edges[i] = NewVendorContactEdge(p.Data[i], p.Cursor.OrderBy.Field) + edges[i] = NewThirdPartyContactEdge(p.Data[i], p.Cursor.OrderBy.Field) } - return &VendorContactConnection{ + return &ThirdPartyContactConnection{ Edges: edges, PageInfo: NewPageInfo(p), } } -func NewVendorContactEdge(c *coredata.VendorContact, orderBy coredata.VendorContactOrderField) *VendorContactEdge { - return &VendorContactEdge{ +func NewThirdPartyContactEdge(c *coredata.ThirdPartyContact, orderBy coredata.ThirdPartyContactOrderField) *ThirdPartyContactEdge { + return &ThirdPartyContactEdge{ Cursor: c.CursorKey(orderBy), - Node: NewVendorContact(c), + Node: NewThirdPartyContact(c), } } -func NewVendorContact(c *coredata.VendorContact) *VendorContact { - return &VendorContact{ +func NewThirdPartyContact(c *coredata.ThirdPartyContact) *ThirdPartyContact { + return &ThirdPartyContact{ ID: c.ID, - Vendor: &Vendor{ - ID: c.VendorID, + ThirdParty: &ThirdParty{ + ID: c.ThirdPartyID, }, FullName: c.FullName, Email: c.Email, diff --git a/pkg/server/api/console/v1/types/vendor_business_associate_agreement.go b/pkg/server/api/console/v1/types/third_party_data_privacy_agreement.go similarity index 81% rename from pkg/server/api/console/v1/types/vendor_business_associate_agreement.go rename to pkg/server/api/console/v1/types/third_party_data_privacy_agreement.go index fc49ed7c7..f20fec26f 100644 --- a/pkg/server/api/console/v1/types/vendor_business_associate_agreement.go +++ b/pkg/server/api/console/v1/types/third_party_data_privacy_agreement.go @@ -18,11 +18,11 @@ import ( "go.probo.inc/probo/pkg/coredata" ) -func NewVendorBusinessAssociateAgreement(v *coredata.VendorBusinessAssociateAgreement, file *coredata.File) *VendorBusinessAssociateAgreement { - return &VendorBusinessAssociateAgreement{ +func NewThirdPartyDataPrivacyAgreement(v *coredata.ThirdPartyDataPrivacyAgreement, file *coredata.File) *ThirdPartyDataPrivacyAgreement { + return &ThirdPartyDataPrivacyAgreement{ ID: v.ID, - Vendor: &Vendor{ - ID: v.VendorID, + ThirdParty: &ThirdParty{ + ID: v.ThirdPartyID, }, ValidFrom: v.ValidFrom, ValidUntil: v.ValidUntil, diff --git a/pkg/server/api/console/v1/types/vendor_risk_assessment.go b/pkg/server/api/console/v1/types/third_party_risk_assessment.go similarity index 58% rename from pkg/server/api/console/v1/types/vendor_risk_assessment.go rename to pkg/server/api/console/v1/types/third_party_risk_assessment.go index f3678b14c..5be22584b 100644 --- a/pkg/server/api/console/v1/types/vendor_risk_assessment.go +++ b/pkg/server/api/console/v1/types/third_party_risk_assessment.go @@ -20,34 +20,34 @@ import ( ) type ( - VendorRiskAssessmentOrderBy OrderBy[coredata.VendorRiskAssessmentOrderField] + ThirdPartyRiskAssessmentOrderBy OrderBy[coredata.ThirdPartyRiskAssessmentOrderField] ) -func NewVendorRiskAssessmentConnection(p *page.Page[*coredata.VendorRiskAssessment, coredata.VendorRiskAssessmentOrderField]) *VendorRiskAssessmentConnection { - var edges = make([]*VendorRiskAssessmentEdge, len(p.Data)) +func NewThirdPartyRiskAssessmentConnection(p *page.Page[*coredata.ThirdPartyRiskAssessment, coredata.ThirdPartyRiskAssessmentOrderField]) *ThirdPartyRiskAssessmentConnection { + var edges = make([]*ThirdPartyRiskAssessmentEdge, len(p.Data)) for i := range edges { - edges[i] = NewVendorRiskAssessmentEdge(p.Data[i], p.Cursor.OrderBy.Field) + edges[i] = NewThirdPartyRiskAssessmentEdge(p.Data[i], p.Cursor.OrderBy.Field) } - return &VendorRiskAssessmentConnection{ + return &ThirdPartyRiskAssessmentConnection{ Edges: edges, PageInfo: NewPageInfo(p), } } -func NewVendorRiskAssessmentEdge(c *coredata.VendorRiskAssessment, orderBy coredata.VendorRiskAssessmentOrderField) *VendorRiskAssessmentEdge { - return &VendorRiskAssessmentEdge{ +func NewThirdPartyRiskAssessmentEdge(c *coredata.ThirdPartyRiskAssessment, orderBy coredata.ThirdPartyRiskAssessmentOrderField) *ThirdPartyRiskAssessmentEdge { + return &ThirdPartyRiskAssessmentEdge{ Cursor: c.CursorKey(orderBy), - Node: NewVendorRiskAssessment(c), + Node: NewThirdPartyRiskAssessment(c), } } -func NewVendorRiskAssessment(c *coredata.VendorRiskAssessment) *VendorRiskAssessment { - return &VendorRiskAssessment{ +func NewThirdPartyRiskAssessment(c *coredata.ThirdPartyRiskAssessment) *ThirdPartyRiskAssessment { + return &ThirdPartyRiskAssessment{ ID: c.ID, - Vendor: &Vendor{ - ID: c.VendorID, + ThirdParty: &ThirdParty{ + ID: c.ThirdPartyID, }, ExpiresAt: c.ExpiresAt, DataSensitivity: c.DataSensitivity, diff --git a/pkg/server/api/console/v1/types/vendor_service.go b/pkg/server/api/console/v1/types/third_party_service.go similarity index 60% rename from pkg/server/api/console/v1/types/vendor_service.go rename to pkg/server/api/console/v1/types/third_party_service.go index 6088a6aba..040201906 100644 --- a/pkg/server/api/console/v1/types/vendor_service.go +++ b/pkg/server/api/console/v1/types/third_party_service.go @@ -20,34 +20,34 @@ import ( ) type ( - VendorServiceOrderBy OrderBy[coredata.VendorServiceOrderField] + ThirdPartyServiceOrderBy OrderBy[coredata.ThirdPartyServiceOrderField] ) -func NewVendorServiceConnection(p *page.Page[*coredata.VendorService, coredata.VendorServiceOrderField]) *VendorServiceConnection { - var edges = make([]*VendorServiceEdge, len(p.Data)) +func NewThirdPartyServiceConnection(p *page.Page[*coredata.ThirdPartyService, coredata.ThirdPartyServiceOrderField]) *ThirdPartyServiceConnection { + var edges = make([]*ThirdPartyServiceEdge, len(p.Data)) for i := range edges { - edges[i] = NewVendorServiceEdge(p.Data[i], p.Cursor.OrderBy.Field) + edges[i] = NewThirdPartyServiceEdge(p.Data[i], p.Cursor.OrderBy.Field) } - return &VendorServiceConnection{ + return &ThirdPartyServiceConnection{ Edges: edges, PageInfo: NewPageInfo(p), } } -func NewVendorServiceEdge(s *coredata.VendorService, orderBy coredata.VendorServiceOrderField) *VendorServiceEdge { - return &VendorServiceEdge{ +func NewThirdPartyServiceEdge(s *coredata.ThirdPartyService, orderBy coredata.ThirdPartyServiceOrderField) *ThirdPartyServiceEdge { + return &ThirdPartyServiceEdge{ Cursor: s.CursorKey(orderBy), - Node: NewVendorService(s), + Node: NewThirdPartyService(s), } } -func NewVendorService(s *coredata.VendorService) *VendorService { - return &VendorService{ +func NewThirdPartyService(s *coredata.ThirdPartyService) *ThirdPartyService { + return &ThirdPartyService{ ID: s.ID, - Vendor: &Vendor{ - ID: s.VendorID, + ThirdParty: &ThirdParty{ + ID: s.ThirdPartyID, }, Name: s.Name, Description: s.Description, diff --git a/pkg/server/api/console/v1/vendor_resolvers.go b/pkg/server/api/console/v1/vendor_resolvers.go deleted file mode 100644 index 8b6b0c227..000000000 --- a/pkg/server/api/console/v1/vendor_resolvers.go +++ /dev/null @@ -1,1136 +0,0 @@ -package console_v1 - -// This file will be automatically regenerated based on the schema, any resolver -// implementations -// will be copied through when generating and any unknown code will be moved to the end. -// Code generated by github.com/99designs/gqlgen version v0.17.90 - -import ( - "context" - "errors" - "fmt" - "time" - - pgx "github.com/jackc/pgx/v5" - "github.com/vikstrous/dataloadgen" - "go.gearno.de/kit/log" - "go.probo.inc/probo/pkg/coredata" - "go.probo.inc/probo/pkg/iam" - "go.probo.inc/probo/pkg/page" - "go.probo.inc/probo/pkg/probo" - "go.probo.inc/probo/pkg/server/api/console/v1/dataloader" - "go.probo.inc/probo/pkg/server/api/console/v1/schema" - "go.probo.inc/probo/pkg/server/api/console/v1/types" - "go.probo.inc/probo/pkg/server/gqlutils" - "go.probo.inc/probo/pkg/validator" -) - -// CreateVendor is the resolver for the createVendor field. -func (r *mutationResolver) CreateVendor(ctx context.Context, input types.CreateVendorInput) (*types.CreateVendorPayload, error) { - if err := r.authorize(ctx, input.OrganizationID, probo.ActionVendorCreate); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.OrganizationID.TenantID()) - - vendor, err := prb.Vendors.Create( - ctx, - probo.CreateVendorRequest{ - OrganizationID: input.OrganizationID, - Name: input.Name, - Description: input.Description, - StatusPageURL: input.StatusPageURL, - TermsOfServiceURL: input.TermsOfServiceURL, - PrivacyPolicyURL: input.PrivacyPolicyURL, - ServiceLevelAgreementURL: input.ServiceLevelAgreementURL, - LegalName: input.LegalName, - HeadquarterAddress: input.HeadquarterAddress, - WebsiteURL: input.WebsiteURL, - Category: input.Category, - DataProcessingAgreementURL: input.DataProcessingAgreementURL, - BusinessAssociateAgreementURL: input.BusinessAssociateAgreementURL, - SubprocessorsListURL: input.SubprocessorsListURL, - Certifications: input.Certifications, - SecurityPageURL: input.SecurityPageURL, - TrustPageURL: input.TrustPageURL, - BusinessOwnerID: input.BusinessOwnerID, - SecurityOwnerID: input.SecurityOwnerID, - Countries: input.Countries, - }, - ) - if err != nil { - if errors.Is(err, coredata.ErrResourceAlreadyExists) { - return nil, gqlutils.Conflict(ctx, err) - } - - if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { - return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) - } - r.logger.ErrorCtx(ctx, "cannot create vendor", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - return &types.CreateVendorPayload{ - VendorEdge: types.NewVendorEdge(vendor, coredata.VendorOrderFieldName), - }, nil -} - -// UpdateVendor is the resolver for the updateVendor field. -func (r *mutationResolver) UpdateVendor(ctx context.Context, input types.UpdateVendorInput) (*types.UpdateVendorPayload, error) { - if err := r.authorize(ctx, input.ID, probo.ActionVendorUpdate); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.ID.TenantID()) - - vendor, err := prb.Vendors.Update( - ctx, - probo.UpdateVendorRequest{ - ID: input.ID, - Name: input.Name, - Description: gqlutils.UnwrapOmittable(input.Description), - StatusPageURL: gqlutils.UnwrapOmittable(input.StatusPageURL), - TermsOfServiceURL: gqlutils.UnwrapOmittable(input.TermsOfServiceURL), - PrivacyPolicyURL: gqlutils.UnwrapOmittable(input.PrivacyPolicyURL), - ServiceLevelAgreementURL: gqlutils.UnwrapOmittable(input.ServiceLevelAgreementURL), - DataProcessingAgreementURL: gqlutils.UnwrapOmittable(input.DataProcessingAgreementURL), - BusinessAssociateAgreementURL: gqlutils.UnwrapOmittable(input.BusinessAssociateAgreementURL), - SubprocessorsListURL: gqlutils.UnwrapOmittable(input.SubprocessorsListURL), - SecurityPageURL: gqlutils.UnwrapOmittable(input.SecurityPageURL), - TrustPageURL: gqlutils.UnwrapOmittable(input.TrustPageURL), - HeadquarterAddress: gqlutils.UnwrapOmittable(input.HeadquarterAddress), - LegalName: gqlutils.UnwrapOmittable(input.LegalName), - WebsiteURL: gqlutils.UnwrapOmittable(input.WebsiteURL), - Category: input.Category, - Certifications: input.Certifications, - BusinessOwnerID: gqlutils.UnwrapOmittable(input.BusinessOwnerID), - SecurityOwnerID: gqlutils.UnwrapOmittable(input.SecurityOwnerID), - ShowOnTrustCenter: input.ShowOnTrustCenter, - Countries: input.Countries, - }, - ) - if err != nil { - if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { - return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) - } - r.logger.ErrorCtx(ctx, "cannot update vendor", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.UpdateVendorPayload{ - Vendor: types.NewVendor(vendor), - }, nil -} - -// DeleteVendor is the resolver for the deleteVendor field. -func (r *mutationResolver) DeleteVendor(ctx context.Context, input types.DeleteVendorInput) (*types.DeleteVendorPayload, error) { - if err := r.authorize(ctx, input.VendorID, probo.ActionVendorDelete); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.VendorID.TenantID()) - - err := prb.Vendors.Delete(ctx, input.VendorID) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot delete vendor", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.DeleteVendorPayload{ - DeletedVendorID: input.VendorID, - }, nil -} - -// CreateVendorContact is the resolver for the createVendorContact field. -func (r *mutationResolver) CreateVendorContact(ctx context.Context, input types.CreateVendorContactInput) (*types.CreateVendorContactPayload, error) { - if err := r.authorize(ctx, input.VendorID, probo.ActionVendorContactCreate); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.VendorID.TenantID()) - - req := probo.CreateVendorContactRequest{ - VendorID: input.VendorID, - FullName: input.FullName, - Email: input.Email, - Phone: input.Phone, - Role: input.Role, - } - - vendorContact, err := prb.VendorContacts.Create(ctx, req) - if err != nil { - if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { - return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) - } - r.logger.ErrorCtx(ctx, "cannot create vendor contact", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.CreateVendorContactPayload{ - VendorContactEdge: types.NewVendorContactEdge(vendorContact, coredata.VendorContactOrderFieldCreatedAt), - }, nil -} - -// UpdateVendorContact is the resolver for the updateVendorContact field. -func (r *mutationResolver) UpdateVendorContact(ctx context.Context, input types.UpdateVendorContactInput) (*types.UpdateVendorContactPayload, error) { - if err := r.authorize(ctx, input.ID, probo.ActionVendorContactUpdate); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.ID.TenantID()) - - req := probo.UpdateVendorContactRequest{ - ID: input.ID, - FullName: gqlutils.UnwrapOmittable(input.FullName), - Email: gqlutils.UnwrapOmittable(input.Email), - Phone: gqlutils.UnwrapOmittable(input.Phone), - Role: gqlutils.UnwrapOmittable(input.Role), - } - - vendorContact, err := prb.VendorContacts.Update(ctx, req) - if err != nil { - if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { - return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) - } - r.logger.ErrorCtx(ctx, "cannot update vendor contact", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.UpdateVendorContactPayload{ - VendorContact: types.NewVendorContact(vendorContact), - }, nil -} - -// DeleteVendorContact is the resolver for the deleteVendorContact field. -func (r *mutationResolver) DeleteVendorContact(ctx context.Context, input types.DeleteVendorContactInput) (*types.DeleteVendorContactPayload, error) { - if err := r.authorize(ctx, input.VendorContactID, probo.ActionVendorContactDelete); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.VendorContactID.TenantID()) - - err := prb.VendorContacts.Delete(ctx, input.VendorContactID) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot delete vendor contact", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.DeleteVendorContactPayload{ - DeletedVendorContactID: input.VendorContactID, - }, nil -} - -// CreateVendorService is the resolver for the createVendorService field. -func (r *mutationResolver) CreateVendorService(ctx context.Context, input types.CreateVendorServiceInput) (*types.CreateVendorServicePayload, error) { - if err := r.authorize(ctx, input.VendorID, probo.ActionVendorServiceCreate); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.VendorID.TenantID()) - - req := probo.CreateVendorServiceRequest{ - VendorID: input.VendorID, - Name: input.Name, - Description: input.Description, - } - - vendorService, err := prb.VendorServices.Create(ctx, req) - if err != nil { - if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { - return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) - } - r.logger.ErrorCtx(ctx, "cannot create vendor service", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.CreateVendorServicePayload{ - VendorServiceEdge: types.NewVendorServiceEdge(vendorService, coredata.VendorServiceOrderFieldCreatedAt), - }, nil -} - -// UpdateVendorService is the resolver for the updateVendorService field. -func (r *mutationResolver) UpdateVendorService(ctx context.Context, input types.UpdateVendorServiceInput) (*types.UpdateVendorServicePayload, error) { - if err := r.authorize(ctx, input.ID, probo.ActionVendorServiceUpdate); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.ID.TenantID()) - - req := probo.UpdateVendorServiceRequest{ - ID: input.ID, - Name: input.Name, - Description: gqlutils.UnwrapOmittable(input.Description), - } - - vendorService, err := prb.VendorServices.Update(ctx, req) - if err != nil { - if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { - return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) - } - r.logger.ErrorCtx(ctx, "cannot update vendor service", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.UpdateVendorServicePayload{ - VendorService: types.NewVendorService(vendorService), - }, nil -} - -// DeleteVendorService is the resolver for the deleteVendorService field. -func (r *mutationResolver) DeleteVendorService(ctx context.Context, input types.DeleteVendorServiceInput) (*types.DeleteVendorServicePayload, error) { - if err := r.authorize(ctx, input.VendorServiceID, probo.ActionVendorServiceDelete); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.VendorServiceID.TenantID()) - - err := prb.VendorServices.Delete(ctx, input.VendorServiceID) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot delete vendor service", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.DeleteVendorServicePayload{ - DeletedVendorServiceID: input.VendorServiceID, - }, nil -} - -// UploadVendorComplianceReport is the resolver for the uploadVendorComplianceReport field. -func (r *mutationResolver) UploadVendorComplianceReport(ctx context.Context, input types.UploadVendorComplianceReportInput) (*types.UploadVendorComplianceReportPayload, error) { - if err := r.authorize(ctx, input.VendorID, probo.ActionVendorComplianceReportUpload); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.VendorID.TenantID()) - - vendorComplianceReport, err := prb.VendorComplianceReports.Upload( - ctx, - input.VendorID, - &probo.VendorComplianceReportCreateRequest{ - File: probo.FileUpload{Filename: input.File.Filename, Size: input.File.Size, Content: input.File.File, ContentType: input.File.ContentType}, - ReportDate: input.ReportDate, - ValidUntil: input.ValidUntil, - ReportName: input.ReportName, - }, - ) - if err != nil { - if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { - return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) - } - r.logger.ErrorCtx(ctx, "cannot upload vendor compliance report", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.UploadVendorComplianceReportPayload{ - VendorComplianceReportEdge: types.NewVendorComplianceReportEdge(vendorComplianceReport, coredata.VendorComplianceReportOrderFieldCreatedAt), - }, nil -} - -// DeleteVendorComplianceReport is the resolver for the deleteVendorComplianceReport field. -func (r *mutationResolver) DeleteVendorComplianceReport(ctx context.Context, input types.DeleteVendorComplianceReportInput) (*types.DeleteVendorComplianceReportPayload, error) { - if err := r.authorize(ctx, input.ReportID, probo.ActionVendorComplianceReportDelete); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.ReportID.TenantID()) - - err := prb.VendorComplianceReports.Delete(ctx, input.ReportID) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot delete vendor compliance report", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.DeleteVendorComplianceReportPayload{ - DeletedVendorComplianceReportID: input.ReportID, - }, nil -} - -// UploadVendorBusinessAssociateAgreement is the resolver for the uploadVendorBusinessAssociateAgreement field. -func (r *mutationResolver) UploadVendorBusinessAssociateAgreement(ctx context.Context, input types.UploadVendorBusinessAssociateAgreementInput) (*types.UploadVendorBusinessAssociateAgreementPayload, error) { - if err := r.authorize(ctx, input.VendorID, probo.ActionVendorBusinessAssociateAgreementUpload); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.VendorID.TenantID()) - - vendorBusinessAssociateAgreement, file, err := prb.VendorBusinessAssociateAgreements.Upload( - ctx, - input.VendorID, - &probo.VendorBusinessAssociateAgreementCreateRequest{ - File: input.File.File, - ValidFrom: input.ValidFrom, - ValidUntil: input.ValidUntil, - FileName: input.FileName, - }, - ) - if err != nil { - if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { - return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) - } - r.logger.ErrorCtx(ctx, "cannot upload vendor business associate agreement", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.UploadVendorBusinessAssociateAgreementPayload{ - VendorBusinessAssociateAgreement: types.NewVendorBusinessAssociateAgreement(vendorBusinessAssociateAgreement, file), - }, nil -} - -// UpdateVendorBusinessAssociateAgreement is the resolver for the updateVendorBusinessAssociateAgreement field. -func (r *mutationResolver) UpdateVendorBusinessAssociateAgreement(ctx context.Context, input types.UpdateVendorBusinessAssociateAgreementInput) (*types.UpdateVendorBusinessAssociateAgreementPayload, error) { - if err := r.authorize(ctx, input.VendorID, probo.ActionVendorBusinessAssociateAgreementUpdate); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.VendorID.TenantID()) - - vendorBusinessAssociateAgreement, file, err := prb.VendorBusinessAssociateAgreements.Update( - ctx, - input.VendorID, - &probo.VendorBusinessAssociateAgreementUpdateRequest{ - ValidFrom: gqlutils.UnwrapOmittable(input.ValidFrom), - ValidUntil: gqlutils.UnwrapOmittable(input.ValidUntil), - }, - ) - if err != nil { - if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { - return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) - } - r.logger.ErrorCtx(ctx, "cannot update vendor business associate agreement", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.UpdateVendorBusinessAssociateAgreementPayload{ - VendorBusinessAssociateAgreement: types.NewVendorBusinessAssociateAgreement(vendorBusinessAssociateAgreement, file), - }, nil -} - -// DeleteVendorBusinessAssociateAgreement is the resolver for the deleteVendorBusinessAssociateAgreement field. -func (r *mutationResolver) DeleteVendorBusinessAssociateAgreement(ctx context.Context, input types.DeleteVendorBusinessAssociateAgreementInput) (*types.DeleteVendorBusinessAssociateAgreementPayload, error) { - if err := r.authorize(ctx, input.VendorID, probo.ActionVendorBusinessAssociateAgreementDelete); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.VendorID.TenantID()) - - err := prb.VendorBusinessAssociateAgreements.DeleteByVendorID(ctx, input.VendorID) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot delete vendor business associate agreement", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.DeleteVendorBusinessAssociateAgreementPayload{ - DeletedVendorID: input.VendorID, - }, nil -} - -// UploadVendorDataPrivacyAgreement is the resolver for the uploadVendorDataPrivacyAgreement field. -func (r *mutationResolver) UploadVendorDataPrivacyAgreement(ctx context.Context, input types.UploadVendorDataPrivacyAgreementInput) (*types.UploadVendorDataPrivacyAgreementPayload, error) { - if err := r.authorize(ctx, input.VendorID, probo.ActionVendorDataPrivacyAgreementUpload); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.VendorID.TenantID()) - - vendorDataPrivacyAgreement, file, err := prb.VendorDataPrivacyAgreements.Upload( - ctx, - input.VendorID, - &probo.VendorDataPrivacyAgreementCreateRequest{ - File: input.File.File, - ValidFrom: input.ValidFrom, - ValidUntil: input.ValidUntil, - FileName: input.FileName, - }, - ) - if err != nil { - if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { - return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) - } - r.logger.ErrorCtx(ctx, "cannot upload vendor data privacy agreement", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.UploadVendorDataPrivacyAgreementPayload{ - VendorDataPrivacyAgreement: types.NewVendorDataPrivacyAgreement(vendorDataPrivacyAgreement, file), - }, nil -} - -// UpdateVendorDataPrivacyAgreement is the resolver for the updateVendorDataPrivacyAgreement field. -func (r *mutationResolver) UpdateVendorDataPrivacyAgreement(ctx context.Context, input types.UpdateVendorDataPrivacyAgreementInput) (*types.UpdateVendorDataPrivacyAgreementPayload, error) { - if err := r.authorize(ctx, input.VendorID, probo.ActionVendorDataPrivacyAgreementUpdate); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.VendorID.TenantID()) - - vendorDataPrivacyAgreement, file, err := prb.VendorDataPrivacyAgreements.Update( - ctx, - input.VendorID, - &probo.VendorDataPrivacyAgreementUpdateRequest{ - ValidFrom: gqlutils.UnwrapOmittable(input.ValidFrom), - ValidUntil: gqlutils.UnwrapOmittable(input.ValidUntil), - }, - ) - if err != nil { - if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { - return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) - } - r.logger.ErrorCtx(ctx, "cannot update vendor data privacy agreement", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.UpdateVendorDataPrivacyAgreementPayload{ - VendorDataPrivacyAgreement: types.NewVendorDataPrivacyAgreement(vendorDataPrivacyAgreement, file), - }, nil -} - -// DeleteVendorDataPrivacyAgreement is the resolver for the deleteVendorDataPrivacyAgreement field. -func (r *mutationResolver) DeleteVendorDataPrivacyAgreement(ctx context.Context, input types.DeleteVendorDataPrivacyAgreementInput) (*types.DeleteVendorDataPrivacyAgreementPayload, error) { - if err := r.authorize(ctx, input.VendorID, probo.ActionVendorDataPrivacyAgreementDelete); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.VendorID.TenantID()) - - err := prb.VendorDataPrivacyAgreements.DeleteByVendorID(ctx, input.VendorID) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot delete vendor data privacy agreement", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.DeleteVendorDataPrivacyAgreementPayload{ - DeletedVendorID: input.VendorID, - }, nil -} - -// CreateVendorRiskAssessment is the resolver for the createVendorRiskAssessment field. -func (r *mutationResolver) CreateVendorRiskAssessment(ctx context.Context, input types.CreateVendorRiskAssessmentInput) (*types.CreateVendorRiskAssessmentPayload, error) { - if err := r.authorize(ctx, input.VendorID, probo.ActionVendorRiskAssessmentCreate); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.VendorID.TenantID()) - - vendorRiskAssessment, err := prb.Vendors.CreateRiskAssessment( - ctx, - probo.CreateVendorRiskAssessmentRequest{ - VendorID: input.VendorID, - ExpiresAt: input.ExpiresAt, - DataSensitivity: input.DataSensitivity, - BusinessImpact: input.BusinessImpact, - Notes: input.Notes, - }, - ) - if err != nil { - if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { - return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) - } - r.logger.ErrorCtx(ctx, "cannot create vendor risk assessment", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.CreateVendorRiskAssessmentPayload{ - VendorRiskAssessmentEdge: types.NewVendorRiskAssessmentEdge(vendorRiskAssessment, coredata.VendorRiskAssessmentOrderFieldCreatedAt), - }, nil -} - -// AssessVendor is the resolver for the assessVendor field. -func (r *mutationResolver) AssessVendor(ctx context.Context, input types.AssessVendorInput) (*types.AssessVendorPayload, error) { - if err := r.authorize(ctx, input.ID, probo.ActionVendorAssess); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.ID.TenantID()) - - result, err := prb.Vendors.Assess( - ctx, - probo.AssessVendorRequest{ - ID: input.ID, - WebsiteURL: input.WebsiteURL, - Procedure: input.Procedure, - }, - ) - if err != nil { - if errors.Is(err, probo.ErrVendorAssessmentDisabled) { - return nil, gqlutils.Unavailable(ctx, probo.ErrVendorAssessmentDisabled) - } - - r.logger.ErrorCtx(ctx, "cannot assess vendor", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.AssessVendorPayload{ - Vendor: types.NewVendor(result.Vendor), - Report: result.Report, - Subprocessors: types.NewVendorSubprocessors(result.Subprocessors), - }, nil -} - -// PublishVendorList is the resolver for the publishVendorList field. -func (r *mutationResolver) PublishVendorList(ctx context.Context, input types.PublishVendorListInput) (*types.PublishVendorListPayload, error) { - if err := r.authorize(ctx, input.OrganizationID, probo.ActionVendorPublish); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, input.OrganizationID.TenantID()) - - document, documentVersion, err := prb.GeneratedDocuments.PublishVendorList(ctx, input.OrganizationID, input.ApproverIds, input.Minor) - if err != nil { - if errors.Is(err, coredata.ErrResourceAlreadyExists) { - return nil, gqlutils.Conflict(ctx, err) - } - if errMinor, ok := errors.AsType[*probo.ErrCannotPublishMinorWithoutMajor](err); ok { - return nil, gqlutils.Invalid(ctx, errMinor) - } - r.logger.ErrorCtx(ctx, "cannot publish vendor list", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.PublishVendorListPayload{ - DocumentEdge: types.NewDocumentEdge(document, coredata.DocumentOrderFieldCreatedAt), - DocumentVersionEdge: types.NewDocumentVersionEdge(documentVersion, coredata.DocumentVersionOrderFieldCreatedAt), - }, nil -} - -// Organization is the resolver for the organization field. -func (r *vendorResolver) Organization(ctx context.Context, obj *types.Vendor) (*types.Organization, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil { - return nil, err - } - - loaders := dataloader.FromContext(ctx) - - organization, err := loaders.Organization.Load(ctx, obj.Organization.ID) - if err != nil { - if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) { - return nil, gqlutils.NotFound(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewOrganization(organization), nil -} - -// ComplianceReports is the resolver for the complianceReports field. -func (r *vendorResolver) ComplianceReports(ctx context.Context, obj *types.Vendor, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorComplianceReportOrderBy) (*types.VendorComplianceReportConnection, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionVendorComplianceReportList); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, obj.ID.TenantID()) - - pageOrderBy := page.OrderBy[coredata.VendorComplianceReportOrderField]{ - Field: coredata.VendorComplianceReportOrderFieldReportDate, - Direction: page.OrderDirectionDesc, - } - if orderBy != nil { - pageOrderBy = page.OrderBy[coredata.VendorComplianceReportOrderField]{ - Field: orderBy.Field, - Direction: orderBy.Direction, - } - } - - cursor := types.NewCursor(first, after, last, before, pageOrderBy) - - page, err := prb.VendorComplianceReports.ListForVendorID(ctx, obj.ID, cursor) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot list vendor compliance reports", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewVendorComplianceReportConnection(page), nil -} - -// BusinessAssociateAgreement is the resolver for the businessAssociateAgreement field. -func (r *vendorResolver) BusinessAssociateAgreement(ctx context.Context, obj *types.Vendor) (*types.VendorBusinessAssociateAgreement, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionVendorBusinessAssociateAgreementGet); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, obj.ID.TenantID()) - - vendorBusinessAssociateAgreement, file, err := prb.VendorBusinessAssociateAgreements.GetByVendorID(ctx, obj.ID) - if err != nil { - if errors.Is(err, pgx.ErrNoRows) { - return nil, nil - } - - r.logger.ErrorCtx(ctx, "cannot get vendor business associate agreement", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewVendorBusinessAssociateAgreement(vendorBusinessAssociateAgreement, file), nil -} - -// DataPrivacyAgreement is the resolver for the dataPrivacyAgreement field. -func (r *vendorResolver) DataPrivacyAgreement(ctx context.Context, obj *types.Vendor) (*types.VendorDataPrivacyAgreement, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionVendorDataPrivacyAgreementGet); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, obj.ID.TenantID()) - - vendorDataPrivacyAgreement, file, err := prb.VendorDataPrivacyAgreements.GetByVendorID(ctx, obj.ID) - if err != nil { - if errors.Is(err, pgx.ErrNoRows) { - return nil, nil - } - - r.logger.ErrorCtx(ctx, "cannot get vendor data privacy agreement", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewVendorDataPrivacyAgreement(vendorDataPrivacyAgreement, file), nil -} - -// Contacts is the resolver for the contacts field. -func (r *vendorResolver) Contacts(ctx context.Context, obj *types.Vendor, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorContactOrderBy) (*types.VendorContactConnection, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionVendorContactList); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, obj.ID.TenantID()) - - pageOrderBy := page.OrderBy[coredata.VendorContactOrderField]{ - Field: coredata.VendorContactOrderFieldCreatedAt, - Direction: page.OrderDirectionDesc, - } - if orderBy != nil { - pageOrderBy = page.OrderBy[coredata.VendorContactOrderField]{ - Field: orderBy.Field, - Direction: orderBy.Direction, - } - } - - cursor := types.NewCursor(first, after, last, before, pageOrderBy) - - page, err := prb.VendorContacts.List(ctx, obj.ID, cursor) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot list vendor contacts", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewVendorContactConnection(page), nil -} - -// Services is the resolver for the services field. -func (r *vendorResolver) Services(ctx context.Context, obj *types.Vendor, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorServiceOrderBy) (*types.VendorServiceConnection, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionVendorServiceList); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, obj.ID.TenantID()) - - pageOrderBy := page.OrderBy[coredata.VendorServiceOrderField]{ - Field: coredata.VendorServiceOrderFieldCreatedAt, - Direction: page.OrderDirectionDesc, - } - if orderBy != nil { - pageOrderBy = page.OrderBy[coredata.VendorServiceOrderField]{ - Field: orderBy.Field, - Direction: orderBy.Direction, - } - } - - cursor := types.NewCursor(first, after, last, before, pageOrderBy) - - page, err := prb.VendorServices.List(ctx, obj.ID, cursor) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot list vendor services", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewVendorServiceConnection(page), nil -} - -// RiskAssessments is the resolver for the riskAssessments field. -func (r *vendorResolver) RiskAssessments(ctx context.Context, obj *types.Vendor, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorRiskAssessmentOrder) (*types.VendorRiskAssessmentConnection, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionVendorRiskAssessmentList); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, obj.ID.TenantID()) - - pageOrderBy := page.OrderBy[coredata.VendorRiskAssessmentOrderField]{ - Field: coredata.VendorRiskAssessmentOrderFieldCreatedAt, - Direction: page.OrderDirectionDesc, - } - if orderBy != nil { - pageOrderBy = page.OrderBy[coredata.VendorRiskAssessmentOrderField]{ - Field: orderBy.Field, - Direction: orderBy.Direction, - } - } - - cursor := types.NewCursor(first, after, last, before, pageOrderBy) - - page, err := prb.Vendors.ListRiskAssessments(ctx, obj.ID, cursor) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot list vendor risk assessments", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewVendorRiskAssessmentConnection(page), nil -} - -// BusinessOwner is the resolver for the businessOwner field. -func (r *vendorResolver) BusinessOwner(ctx context.Context, obj *types.Vendor) (*types.Profile, error) { - if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet); err != nil { - return nil, err - } - - if obj.BusinessOwner == nil { - return nil, nil - } - - loaders := dataloader.FromContext(ctx) - - businessOwner, err := loaders.Profile.Load(ctx, obj.BusinessOwner.ID) - if err != nil { - if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) { - return nil, gqlutils.NotFound(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot get business owner", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewProfile(businessOwner), nil -} - -// SecurityOwner is the resolver for the securityOwner field. -func (r *vendorResolver) SecurityOwner(ctx context.Context, obj *types.Vendor) (*types.Profile, error) { - if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet); err != nil { - return nil, err - } - - if obj.SecurityOwner == nil { - return nil, nil - } - - loaders := dataloader.FromContext(ctx) - - securityOwner, err := loaders.Profile.Load(ctx, obj.SecurityOwner.ID) - if err != nil { - if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) { - return nil, gqlutils.NotFound(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot get security owner", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewProfile(securityOwner), nil -} - -// Permission is the resolver for the permission field. -func (r *vendorResolver) Permission(ctx context.Context, obj *types.Vendor, action string) (bool, error) { - return r.Resolver.Permission(ctx, obj, action) -} - -// Vendor is the resolver for the vendor field. -func (r *vendorBusinessAssociateAgreementResolver) Vendor(ctx context.Context, obj *types.VendorBusinessAssociateAgreement) (*types.Vendor, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionVendorGet); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, obj.ID.TenantID()) - - vendor, err := prb.Vendors.Get(ctx, obj.ID) - if err != nil { - if errors.Is(err, coredata.ErrResourceNotFound) { - return nil, gqlutils.NotFound(ctx, err) - } - - return nil, fmt.Errorf("cannot get vendor: %w", err) - } - - return types.NewVendor(vendor), nil -} - -// FileURL is the resolver for the fileUrl field. -func (r *vendorBusinessAssociateAgreementResolver) FileURL(ctx context.Context, obj *types.VendorBusinessAssociateAgreement) (string, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionFileDownloadUrl); err != nil { - return "", err - } - - prb := r.ProboService(ctx, obj.ID.TenantID()) - - fileURL, err := prb.VendorBusinessAssociateAgreements.GenerateFileURL(ctx, obj.ID, 1*time.Hour) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot generate file URL", log.Error(err)) - return "", gqlutils.Internal(ctx) - } - - return fileURL, nil -} - -// Permission is the resolver for the permission field. -func (r *vendorBusinessAssociateAgreementResolver) Permission(ctx context.Context, obj *types.VendorBusinessAssociateAgreement, action string) (bool, error) { - return r.Resolver.Permission(ctx, obj, action) -} - -// Vendor is the resolver for the vendor field. -func (r *vendorComplianceReportResolver) Vendor(ctx context.Context, obj *types.VendorComplianceReport) (*types.Vendor, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionVendorGet); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, obj.ID.TenantID()) - - vendor, err := prb.Vendors.Get(ctx, obj.ID) - if err != nil { - if errors.Is(err, coredata.ErrResourceNotFound) { - return nil, gqlutils.NotFound(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot get vendor", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewVendor(vendor), nil -} - -// File is the resolver for the file field. -func (r *vendorComplianceReportResolver) File(ctx context.Context, obj *types.VendorComplianceReport) (*types.File, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionFileGet); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, obj.ID.TenantID()) - - evidence, err := prb.VendorComplianceReports.Get(ctx, obj.ID) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot load evidence", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - if evidence.ReportFileId == nil { - return nil, nil - } - - file, err := prb.Files.Get(ctx, *evidence.ReportFileId) - if err != nil { - if errors.Is(err, coredata.ErrResourceNotFound) { - return nil, gqlutils.NotFound(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot load evidence file", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewFile(file), nil -} - -// Permission is the resolver for the permission field. -func (r *vendorComplianceReportResolver) Permission(ctx context.Context, obj *types.VendorComplianceReport, action string) (bool, error) { - return r.Resolver.Permission(ctx, obj, action) -} - -// TotalCount is the resolver for the totalCount field. -func (r *vendorConnectionResolver) TotalCount(ctx context.Context, obj *types.VendorConnection) (int, error) { - if err := r.authorize(ctx, obj.ParentID, probo.ActionVendorList); err != nil { - return 0, err - } - - prb := r.ProboService(ctx, obj.ParentID.TenantID()) - - switch obj.Resolver.(type) { - case *organizationResolver: - count, err := prb.Vendors.CountForOrganizationID(ctx, obj.ParentID) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot count vendors", log.Error(err)) - return 0, gqlutils.Internal(ctx) - } - return count, nil - case *assetResolver: - count, err := prb.Vendors.CountForAssetID(ctx, obj.ParentID) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot count vendors", log.Error(err)) - return 0, gqlutils.Internal(ctx) - } - return count, nil - case *datumResolver: - count, err := prb.Vendors.CountForDatumID(ctx, obj.ParentID) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot count vendors", log.Error(err)) - return 0, gqlutils.Internal(ctx) - } - return count, nil - } - - r.logger.ErrorCtx(ctx, "unsupported resolver") - return 0, gqlutils.Internal(ctx) -} - -// Vendor is the resolver for the vendor field. -func (r *vendorContactResolver) Vendor(ctx context.Context, obj *types.VendorContact) (*types.Vendor, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionVendorGet); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, obj.ID.TenantID()) - - // Get the vendor contact to access the VendorID - vendorContact, err := prb.VendorContacts.Get(ctx, obj.ID) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot get vendor contact", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - vendor, err := prb.Vendors.Get(ctx, vendorContact.VendorID) - if err != nil { - if errors.Is(err, coredata.ErrResourceNotFound) { - return nil, gqlutils.NotFound(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot get vendor", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewVendor(vendor), nil -} - -// Permission is the resolver for the permission field. -func (r *vendorContactResolver) Permission(ctx context.Context, obj *types.VendorContact, action string) (bool, error) { - return r.Resolver.Permission(ctx, obj, action) -} - -// Vendor is the resolver for the vendor field. -func (r *vendorDataPrivacyAgreementResolver) Vendor(ctx context.Context, obj *types.VendorDataPrivacyAgreement) (*types.Vendor, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionVendorGet); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, obj.ID.TenantID()) - - vendor, err := prb.Vendors.Get(ctx, obj.ID) - if err != nil { - if errors.Is(err, coredata.ErrResourceNotFound) { - return nil, gqlutils.NotFound(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot get vendor", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewVendor(vendor), nil -} - -// FileURL is the resolver for the fileUrl field. -func (r *vendorDataPrivacyAgreementResolver) FileURL(ctx context.Context, obj *types.VendorDataPrivacyAgreement) (string, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionFileDownloadUrl); err != nil { - return "", err - } - - prb := r.ProboService(ctx, obj.ID.TenantID()) - - fileURL, err := prb.VendorDataPrivacyAgreements.GenerateFileURL(ctx, obj.ID, 1*time.Hour) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot generate file URL", log.Error(err)) - return "", gqlutils.Internal(ctx) - } - - return fileURL, nil -} - -// Permission is the resolver for the permission field. -func (r *vendorDataPrivacyAgreementResolver) Permission(ctx context.Context, obj *types.VendorDataPrivacyAgreement, action string) (bool, error) { - return r.Resolver.Permission(ctx, obj, action) -} - -// Vendor is the resolver for the vendor field. -func (r *vendorRiskAssessmentResolver) Vendor(ctx context.Context, obj *types.VendorRiskAssessment) (*types.Vendor, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionVendorGet); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, obj.ID.TenantID()) - - vendor, err := prb.Vendors.GetByRiskAssessmentID(ctx, obj.ID) - if err != nil { - if errors.Is(err, coredata.ErrResourceNotFound) { - return nil, gqlutils.NotFound(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot get vendor", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewVendor(vendor), nil -} - -// Permission is the resolver for the permission field. -func (r *vendorRiskAssessmentResolver) Permission(ctx context.Context, obj *types.VendorRiskAssessment, action string) (bool, error) { - return r.Resolver.Permission(ctx, obj, action) -} - -// Vendor is the resolver for the vendor field. -func (r *vendorServiceResolver) Vendor(ctx context.Context, obj *types.VendorService) (*types.Vendor, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionVendorGet); err != nil { - return nil, err - } - - loaders := dataloader.FromContext(ctx) - - vendor, err := loaders.Vendor.Load(ctx, obj.Vendor.ID) - if err != nil { - if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) { - return nil, gqlutils.NotFound(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot get vendor", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewVendor(vendor), nil -} - -// Permission is the resolver for the permission field. -func (r *vendorServiceResolver) Permission(ctx context.Context, obj *types.VendorService, action string) (bool, error) { - return r.Resolver.Permission(ctx, obj, action) -} - -// Vendor returns schema.VendorResolver implementation. -func (r *Resolver) Vendor() schema.VendorResolver { return &vendorResolver{r} } - -// VendorBusinessAssociateAgreement returns schema.VendorBusinessAssociateAgreementResolver implementation. -func (r *Resolver) VendorBusinessAssociateAgreement() schema.VendorBusinessAssociateAgreementResolver { - return &vendorBusinessAssociateAgreementResolver{r} -} - -// VendorComplianceReport returns schema.VendorComplianceReportResolver implementation. -func (r *Resolver) VendorComplianceReport() schema.VendorComplianceReportResolver { - return &vendorComplianceReportResolver{r} -} - -// VendorConnection returns schema.VendorConnectionResolver implementation. -func (r *Resolver) VendorConnection() schema.VendorConnectionResolver { - return &vendorConnectionResolver{r} -} - -// VendorContact returns schema.VendorContactResolver implementation. -func (r *Resolver) VendorContact() schema.VendorContactResolver { return &vendorContactResolver{r} } - -// VendorDataPrivacyAgreement returns schema.VendorDataPrivacyAgreementResolver implementation. -func (r *Resolver) VendorDataPrivacyAgreement() schema.VendorDataPrivacyAgreementResolver { - return &vendorDataPrivacyAgreementResolver{r} -} - -// VendorRiskAssessment returns schema.VendorRiskAssessmentResolver implementation. -func (r *Resolver) VendorRiskAssessment() schema.VendorRiskAssessmentResolver { - return &vendorRiskAssessmentResolver{r} -} - -// VendorService returns schema.VendorServiceResolver implementation. -func (r *Resolver) VendorService() schema.VendorServiceResolver { return &vendorServiceResolver{r} } - -type vendorResolver struct{ *Resolver } -type vendorBusinessAssociateAgreementResolver struct{ *Resolver } -type vendorComplianceReportResolver struct{ *Resolver } -type vendorConnectionResolver struct{ *Resolver } -type vendorContactResolver struct{ *Resolver } -type vendorDataPrivacyAgreementResolver struct{ *Resolver } -type vendorRiskAssessmentResolver struct{ *Resolver } -type vendorServiceResolver struct{ *Resolver } diff --git a/pkg/server/api/mcp/v1/schema.resolvers.go b/pkg/server/api/mcp/v1/schema.resolvers.go index bc4ad041e..e59239f1c 100644 --- a/pkg/server/api/mcp/v1/schema.resolvers.go +++ b/pkg/server/api/mcp/v1/schema.resolvers.go @@ -45,19 +45,19 @@ func (r *Resolver) ListOrganizationsTool(ctx context.Context, req *mcp.CallToolR return nil, result, nil } -// ListVendorsTool handles the listVendors tool -// List all vendors for the organization -func (r *Resolver) ListVendorsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListVendorsInput) (*mcp.CallToolResult, types.ListVendorsOutput, error) { - r.MustAuthorize(ctx, input.OrganizationID, probo.ActionVendorList) +// ListThirdPartiesTool handles the listThirdParties tool +// List all thirdParties for the organization +func (r *Resolver) ListThirdPartiesTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListThirdPartiesInput) (*mcp.CallToolResult, types.ListThirdPartiesOutput, error) { + r.MustAuthorize(ctx, input.OrganizationID, probo.ActionThirdPartyList) prb := r.ProboService(ctx, input.OrganizationID) - pageOrderBy := page.OrderBy[coredata.VendorOrderField]{ - Field: coredata.VendorOrderFieldCreatedAt, + pageOrderBy := page.OrderBy[coredata.ThirdPartyOrderField]{ + Field: coredata.ThirdPartyOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, } if input.OrderBy != nil { - pageOrderBy = page.OrderBy[coredata.VendorOrderField]{ + pageOrderBy = page.OrderBy[coredata.ThirdPartyOrderField]{ Field: input.OrderBy.Field, Direction: input.OrderBy.Direction, } @@ -65,26 +65,26 @@ func (r *Resolver) ListVendorsTool(ctx context.Context, req *mcp.CallToolRequest cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) - vendorFilter := coredata.NewVendorFilter(nil) + thirdPartyFilter := coredata.NewThirdPartyFilter(nil) - page, err := prb.Vendors.ListForOrganizationID(ctx, input.OrganizationID, cursor, vendorFilter) + page, err := prb.ThirdParties.ListForOrganizationID(ctx, input.OrganizationID, cursor, thirdPartyFilter) if err != nil { - panic(fmt.Errorf("cannot list organization vendors: %w", err)) + panic(fmt.Errorf("cannot list organization thirdParties: %w", err)) } - return nil, types.NewListVendorsOutput(page), nil + return nil, types.NewListThirdPartiesOutput(page), nil } -// AddVendorTool handles the addVendor tool -// Add a new vendor to the organization -func (r *Resolver) AddVendorTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddVendorInput) (*mcp.CallToolResult, types.AddVendorOutput, error) { - r.MustAuthorize(ctx, input.OrganizationID, probo.ActionVendorCreate) +// AddThirdPartyTool handles the addThirdParty tool +// Add a new thirdParty to the organization +func (r *Resolver) AddThirdPartyTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddThirdPartyInput) (*mcp.CallToolResult, types.AddThirdPartyOutput, error) { + r.MustAuthorize(ctx, input.OrganizationID, probo.ActionThirdPartyCreate) svc := r.ProboService(ctx, input.OrganizationID) - var category *coredata.VendorCategory + var category *coredata.ThirdPartyCategory if input.Category != nil { - cat := coredata.VendorCategory(*input.Category) + cat := coredata.ThirdPartyCategory(*input.Category) category = &cat } @@ -96,9 +96,9 @@ func (r *Resolver) AddVendorTool(ctx context.Context, req *mcp.CallToolRequest, } } - vendor, err := svc.Vendors.Create( + thirdParty, err := svc.ThirdParties.Create( ctx, - probo.CreateVendorRequest{ + probo.CreateThirdPartyRequest{ OrganizationID: input.OrganizationID, Name: input.Name, Description: input.Description, @@ -122,16 +122,16 @@ func (r *Resolver) AddVendorTool(ctx context.Context, req *mcp.CallToolRequest, }, ) if err != nil { - return nil, types.AddVendorOutput{}, fmt.Errorf("failed to create vendor: %w", err) + return nil, types.AddThirdPartyOutput{}, fmt.Errorf("failed to create thirdParty: %w", err) } - return nil, types.NewAddVendorOutput(vendor), nil + return nil, types.NewAddThirdPartyOutput(thirdParty), nil } -// UpdateVendorTool handles the updateVendor tool -// Update an existing vendor -func (r *Resolver) UpdateVendorTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateVendorInput) (*mcp.CallToolResult, types.UpdateVendorOutput, error) { - r.MustAuthorize(ctx, input.ID, probo.ActionVendorUpdate) +// UpdateThirdPartyTool handles the updateThirdParty tool +// Update an existing thirdParty +func (r *Resolver) UpdateThirdPartyTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateThirdPartyInput) (*mcp.CallToolResult, types.UpdateThirdPartyOutput, error) { + r.MustAuthorize(ctx, input.ID, probo.ActionThirdPartyUpdate) svc := r.ProboService(ctx, input.ID) @@ -210,9 +210,9 @@ func (r *Resolver) UpdateVendorTool(ctx context.Context, req *mcp.CallToolReques securityOwnerID = &input.SecurityOwnerID } - var category *coredata.VendorCategory + var category *coredata.ThirdPartyCategory if input.Category != nil { - cat := coredata.VendorCategory(*input.Category) + cat := coredata.ThirdPartyCategory(*input.Category) category = &cat } @@ -224,9 +224,9 @@ func (r *Resolver) UpdateVendorTool(ctx context.Context, req *mcp.CallToolReques } } - vendor, err := svc.Vendors.Update( + thirdParty, err := svc.ThirdParties.Update( ctx, - probo.UpdateVendorRequest{ + probo.UpdateThirdPartyRequest{ ID: input.ID, Name: input.Name, Description: description, @@ -250,10 +250,10 @@ func (r *Resolver) UpdateVendorTool(ctx context.Context, req *mcp.CallToolReques }, ) if err != nil { - return nil, types.UpdateVendorOutput{}, fmt.Errorf("failed to update vendor: %w", err) + return nil, types.UpdateThirdPartyOutput{}, fmt.Errorf("failed to update thirdParty: %w", err) } - return nil, types.NewUpdateVendorOutput(vendor), nil + return nil, types.NewUpdateThirdPartyOutput(thirdParty), nil } func (r *Resolver) ListRisksTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListRisksInput) (*mcp.CallToolResult, types.ListRisksOutput, error) { @@ -593,7 +593,7 @@ func (r *Resolver) AddAssetTool(ctx context.Context, req *mcp.CallToolRequest, i OwnerID: input.OwnerID, AssetType: input.AssetType, DataTypesStored: input.DataTypesStored, - VendorIDs: input.VendorIds, + ThirdPartyIDs: input.ThirdPartyIds, }, ) if err != nil { @@ -619,7 +619,7 @@ func (r *Resolver) UpdateAssetTool(ctx context.Context, req *mcp.CallToolRequest OwnerID: input.OwnerID, AssetType: input.AssetType, DataTypesStored: input.DataTypesStored, - VendorIDs: input.VendorIds, + ThirdPartyIDs: input.ThirdPartyIds, }, ) if err != nil { @@ -684,7 +684,7 @@ func (r *Resolver) AddDatumTool(ctx context.Context, req *mcp.CallToolRequest, i Name: input.Name, DataClassification: input.DataClassification, OwnerID: input.OwnerID, - VendorIDs: input.VendorIds, + ThirdPartyIDs: input.ThirdPartyIds, }, ) if err != nil { @@ -708,7 +708,7 @@ func (r *Resolver) UpdateDatumTool(ctx context.Context, req *mcp.CallToolRequest Name: input.Name, DataClassification: input.DataClassification, OwnerID: input.OwnerID, - VendorIDs: input.VendorIds, + ThirdPartyIDs: input.ThirdPartyIds, }, ) if err != nil { @@ -1004,7 +1004,7 @@ func (r *Resolver) AddProcessingActivityTool(ctx context.Context, req *mcp.CallT NextReviewDate: input.NextReviewDate, Role: input.Role, DataProtectionOfficerID: input.DataProtectionOfficerID, - VendorIDs: input.VendorIds, + ThirdPartyIDs: input.ThirdPartyIds, }, ) if err != nil { @@ -1021,9 +1021,9 @@ func (r *Resolver) UpdateProcessingActivityTool(ctx context.Context, req *mcp.Ca svc := r.ProboService(ctx, input.ID) - var vendorIDs *[]gid.GID - if input.VendorIds != nil { - vendorIDs = &input.VendorIds + var thirdPartyIDs *[]gid.GID + if input.ThirdPartyIds != nil { + thirdPartyIDs = &input.ThirdPartyIds } processingActivity, err := svc.ProcessingActivities.Update( @@ -1049,7 +1049,7 @@ func (r *Resolver) UpdateProcessingActivityTool(ctx context.Context, req *mcp.Ca NextReviewDate: UnwrapOmittable(input.NextReviewDate), Role: input.Role, DataProtectionOfficerID: UnwrapOmittable(input.DataProtectionOfficerID), - VendorIDs: vendorIDs, + ThirdPartyIDs: thirdPartyIDs, }, ) if err != nil { @@ -2729,19 +2729,19 @@ func (r *Resolver) DeleteApplicabilityStatementTool(ctx context.Context, req *mc }, nil } -// ListVendorRiskAssessmentsTool handles the listVendorRiskAssessments tool -// List all risk assessments for a vendor -func (r *Resolver) ListVendorRiskAssessmentsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListVendorRiskAssessmentsInput) (*mcp.CallToolResult, types.ListVendorRiskAssessmentsOutput, error) { - r.MustAuthorize(ctx, input.VendorID, probo.ActionVendorRiskAssessmentList) +// ListThirdPartyRiskAssessmentsTool handles the listThirdPartyRiskAssessments tool +// List all risk assessments for a thirdParty +func (r *Resolver) ListThirdPartyRiskAssessmentsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListThirdPartyRiskAssessmentsInput) (*mcp.CallToolResult, types.ListThirdPartyRiskAssessmentsOutput, error) { + r.MustAuthorize(ctx, input.ThirdPartyID, probo.ActionThirdPartyRiskAssessmentList) - prb := r.ProboService(ctx, input.VendorID) + prb := r.ProboService(ctx, input.ThirdPartyID) - pageOrderBy := page.OrderBy[coredata.VendorRiskAssessmentOrderField]{ - Field: coredata.VendorRiskAssessmentOrderFieldCreatedAt, + pageOrderBy := page.OrderBy[coredata.ThirdPartyRiskAssessmentOrderField]{ + Field: coredata.ThirdPartyRiskAssessmentOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, } if input.OrderBy != nil { - pageOrderBy = page.OrderBy[coredata.VendorRiskAssessmentOrderField]{ + pageOrderBy = page.OrderBy[coredata.ThirdPartyRiskAssessmentOrderField]{ Field: input.OrderBy.Field, Direction: input.OrderBy.Direction, } @@ -2749,25 +2749,25 @@ func (r *Resolver) ListVendorRiskAssessmentsTool(ctx context.Context, req *mcp.C cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) - p, err := prb.Vendors.ListRiskAssessments(ctx, input.VendorID, cursor) + p, err := prb.ThirdParties.ListRiskAssessments(ctx, input.ThirdPartyID, cursor) if err != nil { - return nil, types.ListVendorRiskAssessmentsOutput{}, fmt.Errorf("cannot list vendor risk assessments: %w", err) + return nil, types.ListThirdPartyRiskAssessmentsOutput{}, fmt.Errorf("cannot list thirdParty risk assessments: %w", err) } - return nil, types.NewListVendorRiskAssessmentsOutput(p), nil + return nil, types.NewListThirdPartyRiskAssessmentsOutput(p), nil } -// AddVendorRiskAssessmentTool handles the addVendorRiskAssessment tool -// Add a new risk assessment for a vendor -func (r *Resolver) AddVendorRiskAssessmentTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddVendorRiskAssessmentInput) (*mcp.CallToolResult, types.AddVendorRiskAssessmentOutput, error) { - r.MustAuthorize(ctx, input.VendorID, probo.ActionVendorRiskAssessmentCreate) +// AddThirdPartyRiskAssessmentTool handles the addThirdPartyRiskAssessment tool +// Add a new risk assessment for a thirdParty +func (r *Resolver) AddThirdPartyRiskAssessmentTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddThirdPartyRiskAssessmentInput) (*mcp.CallToolResult, types.AddThirdPartyRiskAssessmentOutput, error) { + r.MustAuthorize(ctx, input.ThirdPartyID, probo.ActionThirdPartyRiskAssessmentCreate) - prb := r.ProboService(ctx, input.VendorID) + prb := r.ProboService(ctx, input.ThirdPartyID) - assessment, err := prb.Vendors.CreateRiskAssessment( + assessment, err := prb.ThirdParties.CreateRiskAssessment( ctx, - probo.CreateVendorRiskAssessmentRequest{ - VendorID: input.VendorID, + probo.CreateThirdPartyRiskAssessmentRequest{ + ThirdPartyID: input.ThirdPartyID, ExpiresAt: input.ExpiresAt, DataSensitivity: input.DataSensitivity, BusinessImpact: input.BusinessImpact, @@ -2775,24 +2775,24 @@ func (r *Resolver) AddVendorRiskAssessmentTool(ctx context.Context, req *mcp.Cal }, ) if err != nil { - return nil, types.AddVendorRiskAssessmentOutput{}, fmt.Errorf("failed to create vendor risk assessment: %w", err) + return nil, types.AddThirdPartyRiskAssessmentOutput{}, fmt.Errorf("failed to create thirdParty risk assessment: %w", err) } - return nil, types.NewAddVendorRiskAssessmentOutput(assessment), nil + return nil, types.NewAddThirdPartyRiskAssessmentOutput(assessment), nil } -func (r *Resolver) DeleteVendorTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteVendorInput) (*mcp.CallToolResult, types.DeleteVendorOutput, error) { - r.MustAuthorize(ctx, input.ID, probo.ActionVendorDelete) +func (r *Resolver) DeleteThirdPartyTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteThirdPartyInput) (*mcp.CallToolResult, types.DeleteThirdPartyOutput, error) { + r.MustAuthorize(ctx, input.ID, probo.ActionThirdPartyDelete) svc := r.ProboService(ctx, input.ID) - err := svc.Vendors.Delete(ctx, input.ID) + err := svc.ThirdParties.Delete(ctx, input.ID) if err != nil { - return nil, types.DeleteVendorOutput{}, fmt.Errorf("failed to delete vendor: %w", err) + return nil, types.DeleteThirdPartyOutput{}, fmt.Errorf("failed to delete thirdParty: %w", err) } - return nil, types.DeleteVendorOutput{ - DeletedVendorID: input.ID, + return nil, types.DeleteThirdPartyOutput{ + DeletedThirdPartyID: input.ID, }, nil } @@ -3841,19 +3841,19 @@ func (r *Resolver) PublishAssetListTool(ctx context.Context, req *mcp.CallToolRe }, nil } -// ListVendorContactsTool handles the listVendorContacts tool -// List all contacts for a vendor -func (r *Resolver) ListVendorContactsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListVendorContactsInput) (*mcp.CallToolResult, types.ListVendorContactsOutput, error) { - r.MustAuthorize(ctx, input.VendorID, probo.ActionVendorContactList) +// ListThirdPartyContactsTool handles the listThirdPartyContacts tool +// List all contacts for a thirdParty +func (r *Resolver) ListThirdPartyContactsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListThirdPartyContactsInput) (*mcp.CallToolResult, types.ListThirdPartyContactsOutput, error) { + r.MustAuthorize(ctx, input.ThirdPartyID, probo.ActionThirdPartyContactList) - prb := r.ProboService(ctx, input.VendorID) + prb := r.ProboService(ctx, input.ThirdPartyID) - pageOrderBy := page.OrderBy[coredata.VendorContactOrderField]{ - Field: coredata.VendorContactOrderFieldCreatedAt, + pageOrderBy := page.OrderBy[coredata.ThirdPartyContactOrderField]{ + Field: coredata.ThirdPartyContactOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, } if input.OrderBy != nil { - pageOrderBy = page.OrderBy[coredata.VendorContactOrderField]{ + pageOrderBy = page.OrderBy[coredata.ThirdPartyContactOrderField]{ Field: input.OrderBy.Field, Direction: input.OrderBy.Direction, } @@ -3861,50 +3861,50 @@ func (r *Resolver) ListVendorContactsTool(ctx context.Context, req *mcp.CallTool cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) - p, err := prb.VendorContacts.List(ctx, input.VendorID, cursor) + p, err := prb.ThirdPartyContacts.List(ctx, input.ThirdPartyID, cursor) if err != nil { - return nil, types.ListVendorContactsOutput{}, fmt.Errorf("cannot list vendor contacts: %w", err) + return nil, types.ListThirdPartyContactsOutput{}, fmt.Errorf("cannot list thirdParty contacts: %w", err) } - return nil, types.NewListVendorContactsOutput(p), nil + return nil, types.NewListThirdPartyContactsOutput(p), nil } -// AddVendorContactTool handles the addVendorContact tool -// Add a new contact to a vendor -func (r *Resolver) AddVendorContactTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddVendorContactInput) (*mcp.CallToolResult, types.AddVendorContactOutput, error) { - r.MustAuthorize(ctx, input.VendorID, probo.ActionVendorContactCreate) +// AddThirdPartyContactTool handles the addThirdPartyContact tool +// Add a new contact to a thirdParty +func (r *Resolver) AddThirdPartyContactTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddThirdPartyContactInput) (*mcp.CallToolResult, types.AddThirdPartyContactOutput, error) { + r.MustAuthorize(ctx, input.ThirdPartyID, probo.ActionThirdPartyContactCreate) - prb := r.ProboService(ctx, input.VendorID) + prb := r.ProboService(ctx, input.ThirdPartyID) emailAddr, err := mail.ParseAddr(input.Email) if err != nil { - return nil, types.AddVendorContactOutput{}, fmt.Errorf("invalid email address: %w", err) + return nil, types.AddThirdPartyContactOutput{}, fmt.Errorf("invalid email address: %w", err) } - vendorContact, err := prb.VendorContacts.Create(ctx, probo.CreateVendorContactRequest{ - VendorID: input.VendorID, - FullName: &input.FullName, - Email: &emailAddr, - Phone: &input.Phone, - Role: &input.Role, + thirdPartyContact, err := prb.ThirdPartyContacts.Create(ctx, probo.CreateThirdPartyContactRequest{ + ThirdPartyID: input.ThirdPartyID, + FullName: &input.FullName, + Email: &emailAddr, + Phone: &input.Phone, + Role: &input.Role, }) if err != nil { - return nil, types.AddVendorContactOutput{}, fmt.Errorf("cannot create vendor contact: %w", err) + return nil, types.AddThirdPartyContactOutput{}, fmt.Errorf("cannot create thirdParty contact: %w", err) } - return nil, types.AddVendorContactOutput{ - VendorContact: types.NewVendorContact(vendorContact), + return nil, types.AddThirdPartyContactOutput{ + ThirdPartyContact: types.NewThirdPartyContact(thirdPartyContact), }, nil } -// UpdateVendorContactTool handles the updateVendorContact tool -// Update an existing vendor contact -func (r *Resolver) UpdateVendorContactTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateVendorContactInput) (*mcp.CallToolResult, types.UpdateVendorContactOutput, error) { - r.MustAuthorize(ctx, input.ID, probo.ActionVendorContactUpdate) +// UpdateThirdPartyContactTool handles the updateThirdPartyContact tool +// Update an existing thirdParty contact +func (r *Resolver) UpdateThirdPartyContactTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateThirdPartyContactInput) (*mcp.CallToolResult, types.UpdateThirdPartyContactOutput, error) { + r.MustAuthorize(ctx, input.ID, probo.ActionThirdPartyContactUpdate) prb := r.ProboService(ctx, input.ID) - updateReq := probo.UpdateVendorContactRequest{ + updateReq := probo.UpdateThirdPartyContactRequest{ ID: input.ID, } @@ -3915,7 +3915,7 @@ func (r *Resolver) UpdateVendorContactTool(ctx context.Context, req *mcp.CallToo if input.Email != nil { emailAddr, err := mail.ParseAddr(*input.Email) if err != nil { - return nil, types.UpdateVendorContactOutput{}, fmt.Errorf("invalid email address: %w", err) + return nil, types.UpdateThirdPartyContactOutput{}, fmt.Errorf("invalid email address: %w", err) } emailPtr := &emailAddr updateReq.Email = &emailPtr @@ -3929,46 +3929,46 @@ func (r *Resolver) UpdateVendorContactTool(ctx context.Context, req *mcp.CallToo updateReq.Role = &input.Role } - vendorContact, err := prb.VendorContacts.Update(ctx, updateReq) + thirdPartyContact, err := prb.ThirdPartyContacts.Update(ctx, updateReq) if err != nil { - return nil, types.UpdateVendorContactOutput{}, fmt.Errorf("cannot update vendor contact: %w", err) + return nil, types.UpdateThirdPartyContactOutput{}, fmt.Errorf("cannot update thirdParty contact: %w", err) } - return nil, types.UpdateVendorContactOutput{ - VendorContact: types.NewVendorContact(vendorContact), + return nil, types.UpdateThirdPartyContactOutput{ + ThirdPartyContact: types.NewThirdPartyContact(thirdPartyContact), }, nil } -// DeleteVendorContactTool handles the deleteVendorContact tool -// Delete a vendor contact -func (r *Resolver) DeleteVendorContactTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteVendorContactInput) (*mcp.CallToolResult, types.DeleteVendorContactOutput, error) { - r.MustAuthorize(ctx, input.ID, probo.ActionVendorContactDelete) +// DeleteThirdPartyContactTool handles the deleteThirdPartyContact tool +// Delete a thirdParty contact +func (r *Resolver) DeleteThirdPartyContactTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteThirdPartyContactInput) (*mcp.CallToolResult, types.DeleteThirdPartyContactOutput, error) { + r.MustAuthorize(ctx, input.ID, probo.ActionThirdPartyContactDelete) prb := r.ProboService(ctx, input.ID) - err := prb.VendorContacts.Delete(ctx, input.ID) + err := prb.ThirdPartyContacts.Delete(ctx, input.ID) if err != nil { - return nil, types.DeleteVendorContactOutput{}, fmt.Errorf("cannot delete vendor contact: %w", err) + return nil, types.DeleteThirdPartyContactOutput{}, fmt.Errorf("cannot delete thirdParty contact: %w", err) } - return nil, types.DeleteVendorContactOutput{ - DeletedVendorContactID: input.ID, + return nil, types.DeleteThirdPartyContactOutput{ + DeletedThirdPartyContactID: input.ID, }, nil } -// ListVendorServicesTool handles the listVendorServices tool -// List all services for a vendor -func (r *Resolver) ListVendorServicesTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListVendorServicesInput) (*mcp.CallToolResult, types.ListVendorServicesOutput, error) { - r.MustAuthorize(ctx, input.VendorID, probo.ActionVendorServiceList) +// ListThirdPartyServicesTool handles the listThirdPartyServices tool +// List all services for a thirdParty +func (r *Resolver) ListThirdPartyServicesTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListThirdPartyServicesInput) (*mcp.CallToolResult, types.ListThirdPartyServicesOutput, error) { + r.MustAuthorize(ctx, input.ThirdPartyID, probo.ActionThirdPartyServiceList) - prb := r.ProboService(ctx, input.VendorID) + prb := r.ProboService(ctx, input.ThirdPartyID) - pageOrderBy := page.OrderBy[coredata.VendorServiceOrderField]{ - Field: coredata.VendorServiceOrderFieldCreatedAt, + pageOrderBy := page.OrderBy[coredata.ThirdPartyServiceOrderField]{ + Field: coredata.ThirdPartyServiceOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, } if input.OrderBy != nil { - pageOrderBy = page.OrderBy[coredata.VendorServiceOrderField]{ + pageOrderBy = page.OrderBy[coredata.ThirdPartyServiceOrderField]{ Field: input.OrderBy.Field, Direction: input.OrderBy.Direction, } @@ -3976,43 +3976,43 @@ func (r *Resolver) ListVendorServicesTool(ctx context.Context, req *mcp.CallTool cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) - p, err := prb.VendorServices.List(ctx, input.VendorID, cursor) + p, err := prb.ThirdPartyServices.List(ctx, input.ThirdPartyID, cursor) if err != nil { - return nil, types.ListVendorServicesOutput{}, fmt.Errorf("cannot list vendor services: %w", err) + return nil, types.ListThirdPartyServicesOutput{}, fmt.Errorf("cannot list thirdParty services: %w", err) } - return nil, types.NewListVendorServicesOutput(p), nil + return nil, types.NewListThirdPartyServicesOutput(p), nil } -// AddVendorServiceTool handles the addVendorService tool -// Add a new service to a vendor -func (r *Resolver) AddVendorServiceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddVendorServiceInput) (*mcp.CallToolResult, types.AddVendorServiceOutput, error) { - r.MustAuthorize(ctx, input.VendorID, probo.ActionVendorServiceCreate) +// AddThirdPartyServiceTool handles the addThirdPartyService tool +// Add a new service to a thirdParty +func (r *Resolver) AddThirdPartyServiceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddThirdPartyServiceInput) (*mcp.CallToolResult, types.AddThirdPartyServiceOutput, error) { + r.MustAuthorize(ctx, input.ThirdPartyID, probo.ActionThirdPartyServiceCreate) - prb := r.ProboService(ctx, input.VendorID) + prb := r.ProboService(ctx, input.ThirdPartyID) - vendorService, err := prb.VendorServices.Create(ctx, probo.CreateVendorServiceRequest{ - VendorID: input.VendorID, - Name: input.Name, - Description: input.Description, + thirdPartyService, err := prb.ThirdPartyServices.Create(ctx, probo.CreateThirdPartyServiceRequest{ + ThirdPartyID: input.ThirdPartyID, + Name: input.Name, + Description: input.Description, }) if err != nil { - return nil, types.AddVendorServiceOutput{}, fmt.Errorf("cannot create vendor service: %w", err) + return nil, types.AddThirdPartyServiceOutput{}, fmt.Errorf("cannot create thirdParty service: %w", err) } - return nil, types.AddVendorServiceOutput{ - VendorService: types.NewVendorService(vendorService), + return nil, types.AddThirdPartyServiceOutput{ + ThirdPartyService: types.NewThirdPartyService(thirdPartyService), }, nil } -// UpdateVendorServiceTool handles the updateVendorService tool -// Update an existing vendor service -func (r *Resolver) UpdateVendorServiceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateVendorServiceInput) (*mcp.CallToolResult, types.UpdateVendorServiceOutput, error) { - r.MustAuthorize(ctx, input.ID, probo.ActionVendorServiceUpdate) +// UpdateThirdPartyServiceTool handles the updateThirdPartyService tool +// Update an existing thirdParty service +func (r *Resolver) UpdateThirdPartyServiceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateThirdPartyServiceInput) (*mcp.CallToolResult, types.UpdateThirdPartyServiceOutput, error) { + r.MustAuthorize(ctx, input.ID, probo.ActionThirdPartyServiceUpdate) prb := r.ProboService(ctx, input.ID) - updateReq := probo.UpdateVendorServiceRequest{ + updateReq := probo.UpdateThirdPartyServiceRequest{ ID: input.ID, } @@ -4024,30 +4024,30 @@ func (r *Resolver) UpdateVendorServiceTool(ctx context.Context, req *mcp.CallToo updateReq.Description = &input.Description } - vendorService, err := prb.VendorServices.Update(ctx, updateReq) + thirdPartyService, err := prb.ThirdPartyServices.Update(ctx, updateReq) if err != nil { - return nil, types.UpdateVendorServiceOutput{}, fmt.Errorf("cannot update vendor service: %w", err) + return nil, types.UpdateThirdPartyServiceOutput{}, fmt.Errorf("cannot update thirdParty service: %w", err) } - return nil, types.UpdateVendorServiceOutput{ - VendorService: types.NewVendorService(vendorService), + return nil, types.UpdateThirdPartyServiceOutput{ + ThirdPartyService: types.NewThirdPartyService(thirdPartyService), }, nil } -// DeleteVendorServiceTool handles the deleteVendorService tool -// Delete a vendor service -func (r *Resolver) DeleteVendorServiceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteVendorServiceInput) (*mcp.CallToolResult, types.DeleteVendorServiceOutput, error) { - r.MustAuthorize(ctx, input.ID, probo.ActionVendorServiceDelete) +// DeleteThirdPartyServiceTool handles the deleteThirdPartyService tool +// Delete a thirdParty service +func (r *Resolver) DeleteThirdPartyServiceTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteThirdPartyServiceInput) (*mcp.CallToolResult, types.DeleteThirdPartyServiceOutput, error) { + r.MustAuthorize(ctx, input.ID, probo.ActionThirdPartyServiceDelete) prb := r.ProboService(ctx, input.ID) - err := prb.VendorServices.Delete(ctx, input.ID) + err := prb.ThirdPartyServices.Delete(ctx, input.ID) if err != nil { - return nil, types.DeleteVendorServiceOutput{}, fmt.Errorf("cannot delete vendor service: %w", err) + return nil, types.DeleteThirdPartyServiceOutput{}, fmt.Errorf("cannot delete thirdParty service: %w", err) } - return nil, types.DeleteVendorServiceOutput{ - DeletedVendorServiceID: input.ID, + return nil, types.DeleteThirdPartyServiceOutput{ + DeletedThirdPartyServiceID: input.ID, }, nil } func (r *Resolver) DeleteAssetTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteAssetInput) (*mcp.CallToolResult, types.DeleteAssetOutput, error) { @@ -4572,24 +4572,24 @@ func (r *Resolver) DeleteCustomDomainTool(ctx context.Context, req *mcp.CallTool return nil, types.DeleteCustomDomainOutput{DeletedCustomDomain: deletedDomain}, nil } -func (r *Resolver) AssessVendorTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AssessVendorInput) (*mcp.CallToolResult, types.AssessVendorOutput, error) { - r.MustAuthorize(ctx, input.ID, probo.ActionVendorAssess) +func (r *Resolver) AssessThirdPartyTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AssessThirdPartyInput) (*mcp.CallToolResult, types.AssessThirdPartyOutput, error) { + r.MustAuthorize(ctx, input.ID, probo.ActionThirdPartyAssess) svc := r.ProboService(ctx, input.ID) - result, err := svc.Vendors.Assess( + result, err := svc.ThirdParties.Assess( ctx, - probo.AssessVendorRequest{ + probo.AssessThirdPartyRequest{ ID: input.ID, WebsiteURL: input.WebsiteURL, Procedure: input.Procedure, }, ) if err != nil { - return nil, types.AssessVendorOutput{}, fmt.Errorf("cannot assess vendor: %w", err) + return nil, types.AssessThirdPartyOutput{}, fmt.Errorf("cannot assess thirdParty: %w", err) } - return nil, types.NewAssessVendorOutput(result), nil + return nil, types.NewAssessThirdPartyOutput(result), nil } func (r *Resolver) PublishFindingListTool(ctx context.Context, req *mcp.CallToolRequest, input *types.PublishFindingListInput) (*mcp.CallToolResult, types.PublishFindingListOutput, error) { @@ -4672,17 +4672,17 @@ func (r *Resolver) PublishTransferImpactAssessmentListTool(ctx context.Context, }, nil } -func (r *Resolver) PublishVendorListTool(ctx context.Context, req *mcp.CallToolRequest, input *types.PublishVendorListInput) (*mcp.CallToolResult, types.PublishVendorListOutput, error) { - r.MustAuthorize(ctx, input.OrganizationID, probo.ActionVendorPublish) +func (r *Resolver) PublishThirdPartyListTool(ctx context.Context, req *mcp.CallToolRequest, input *types.PublishThirdPartyListInput) (*mcp.CallToolResult, types.PublishThirdPartyListOutput, error) { + r.MustAuthorize(ctx, input.OrganizationID, probo.ActionThirdPartyPublish) svc := r.ProboService(ctx, input.OrganizationID) - document, documentVersion, err := svc.GeneratedDocuments.PublishVendorList(ctx, input.OrganizationID, input.ApproverIds, input.Minor) + document, documentVersion, err := svc.GeneratedDocuments.PublishThirdPartyList(ctx, input.OrganizationID, input.ApproverIds, input.Minor) if err != nil { - return nil, types.PublishVendorListOutput{}, fmt.Errorf("cannot publish vendor list: %w", err) + return nil, types.PublishThirdPartyListOutput{}, fmt.Errorf("cannot publish thirdParty list: %w", err) } - return nil, types.PublishVendorListOutput{ + return nil, types.PublishThirdPartyListOutput{ DocumentID: document.ID, DocumentVersionID: documentVersion.ID, }, nil diff --git a/pkg/server/api/mcp/v1/specification.yaml b/pkg/server/api/mcp/v1/specification.yaml index a7c664c2c..2527c35ec 100644 --- a/pkg/server/api/mcp/v1/specification.yaml +++ b/pkg/server/api/mcp/v1/specification.yaml @@ -22,74 +22,74 @@ components: - DESC go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/page.OrderDirection - VendorOrderField: + ThirdPartyOrderField: type: string enum: - CREATED_AT - UPDATED_AT - NAME - go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.VendorOrderField + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ThirdPartyOrderField - VendorContactOrderField: + ThirdPartyContactOrderField: type: string enum: - CREATED_AT - FULL_NAME - EMAIL - go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.VendorContactOrderField + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ThirdPartyContactOrderField - VendorContactOrderBy: + ThirdPartyContactOrderBy: type: object required: - field - direction properties: field: - $ref: "#/components/schemas/VendorContactOrderField" - description: Vendor contact order field + $ref: "#/components/schemas/ThirdPartyContactOrderField" + description: ThirdParty contact order field direction: $ref: "#/components/schemas/OrderDirection" - description: Vendor contact order direction + description: ThirdParty contact order direction - VendorServiceOrderField: + ThirdPartyServiceOrderField: type: string enum: - CREATED_AT - NAME - go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.VendorServiceOrderField + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ThirdPartyServiceOrderField - VendorServiceOrderBy: + ThirdPartyServiceOrderBy: type: object required: - field - direction properties: field: - $ref: "#/components/schemas/VendorServiceOrderField" - description: Vendor service order field + $ref: "#/components/schemas/ThirdPartyServiceOrderField" + description: ThirdParty service order field direction: $ref: "#/components/schemas/OrderDirection" - description: Vendor service order direction + description: ThirdParty service order direction - VendorRiskAssessmentOrderField: + ThirdPartyRiskAssessmentOrderField: type: string enum: - CREATED_AT - EXPIRES_AT - go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.VendorRiskAssessmentOrderField + go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ThirdPartyRiskAssessmentOrderField - VendorRiskAssessmentOrderBy: + ThirdPartyRiskAssessmentOrderBy: type: object required: - field - direction properties: field: - $ref: "#/components/schemas/VendorRiskAssessmentOrderField" - description: Vendor risk assessment order field + $ref: "#/components/schemas/ThirdPartyRiskAssessmentOrderField" + description: ThirdParty risk assessment order field direction: $ref: "#/components/schemas/OrderDirection" - description: Vendor risk assessment order direction + description: ThirdParty risk assessment order direction DataSensitivity: type: string @@ -110,18 +110,18 @@ components: - CRITICAL go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.BusinessImpact - VendorOrderBy: + ThirdPartyOrderBy: type: object required: - field - direction properties: field: - $ref: "#/components/schemas/VendorOrderField" - description: Vendor order field + $ref: "#/components/schemas/ThirdPartyOrderField" + description: ThirdParty order field direction: $ref: "#/components/schemas/OrderDirection" - description: Vendor order direction + description: ThirdParty order direction DocumentVersionApprovalQuorumOrderBy: type: object @@ -619,7 +619,7 @@ components: format: date-time description: Update timestamp - ListVendorsInput: + ListThirdPartiesInput: type: object required: - organization_id @@ -628,8 +628,8 @@ components: $ref: "#/components/schemas/GID" description: Organization ID order_by: - $ref: "#/components/schemas/VendorOrderBy" - description: Vendor order by + $ref: "#/components/schemas/ThirdPartyOrderBy" + description: ThirdParty order by size: type: integer description: Page size @@ -637,20 +637,20 @@ components: $ref: "#/components/schemas/CursorKey" description: Page cursor - ListVendorsOutput: + ListThirdPartiesOutput: type: object required: - - vendors + - thirdParties properties: next_cursor: $ref: "#/components/schemas/CursorKey" description: Next cursor - vendors: + thirdParties: type: array items: - $ref: "#/components/schemas/Vendor" + $ref: "#/components/schemas/ThirdParty" - Vendor: + ThirdParty: type: object required: - id @@ -662,18 +662,18 @@ components: properties: id: $ref: "#/components/schemas/GID" - description: Vendor ID + description: ThirdParty ID organization_id: $ref: "#/components/schemas/GID" description: Organization ID name: type: string - description: Vendor name + description: ThirdParty name description: type: - string - "null" - description: Vendor description + description: ThirdParty description category: type: string enum: @@ -699,7 +699,7 @@ components: - SALES - SECURITY - VERSION_CONTROL - description: Vendor category + description: ThirdParty category headquarter_address: type: - string @@ -789,12 +789,12 @@ components: format: date-time description: Update timestamp - VendorRiskAssessment: + ThirdPartyRiskAssessment: type: object required: - id - organization_id - - vendor_id + - third_party_id - expires_at - data_sensitivity - business_impact @@ -803,13 +803,13 @@ components: properties: id: $ref: "#/components/schemas/GID" - description: Vendor risk assessment ID + description: ThirdParty risk assessment ID organization_id: $ref: "#/components/schemas/GID" description: Organization ID - vendor_id: + third_party_id: $ref: "#/components/schemas/GID" - description: Vendor ID + description: ThirdParty ID expires_at: type: string format: date-time @@ -834,17 +834,17 @@ components: format: date-time description: Update timestamp - ListVendorRiskAssessmentsInput: + ListThirdPartyRiskAssessmentsInput: type: object required: - - vendor_id + - third_party_id properties: - vendor_id: + third_party_id: $ref: "#/components/schemas/GID" - description: Vendor ID + description: ThirdParty ID order_by: - $ref: "#/components/schemas/VendorRiskAssessmentOrderBy" - description: Vendor risk assessment order by + $ref: "#/components/schemas/ThirdPartyRiskAssessmentOrderBy" + description: ThirdParty risk assessment order by size: type: integer description: Page size @@ -852,30 +852,30 @@ components: $ref: "#/components/schemas/CursorKey" description: Page cursor - ListVendorRiskAssessmentsOutput: + ListThirdPartyRiskAssessmentsOutput: type: object required: - - vendor_risk_assessments + - third_party_risk_assessments properties: next_cursor: $ref: "#/components/schemas/CursorKey" description: Next cursor - vendor_risk_assessments: + third_party_risk_assessments: type: array items: - $ref: "#/components/schemas/VendorRiskAssessment" + $ref: "#/components/schemas/ThirdPartyRiskAssessment" - AddVendorRiskAssessmentInput: + AddThirdPartyRiskAssessmentInput: type: object required: - - vendor_id + - third_party_id - expires_at - data_sensitivity - business_impact properties: - vendor_id: + third_party_id: $ref: "#/components/schemas/GID" - description: Vendor ID + description: ThirdParty ID expires_at: type: string format: date-time @@ -890,15 +890,15 @@ components: type: string description: Notes - AddVendorRiskAssessmentOutput: + AddThirdPartyRiskAssessmentOutput: type: object required: - - vendor_risk_assessment + - third_party_risk_assessment properties: - vendor_risk_assessment: - $ref: "#/components/schemas/VendorRiskAssessment" + third_party_risk_assessment: + $ref: "#/components/schemas/ThirdPartyRiskAssessment" - AddVendorInput: + AddThirdPartyInput: type: object required: - organization_id @@ -909,10 +909,10 @@ components: description: Organization ID name: type: string - description: Vendor name + description: ThirdParty name description: type: string - description: Vendor description + description: ThirdParty description category: type: string enum: @@ -938,7 +938,7 @@ components: - SALES - SECURITY - VERSION_CONTROL - description: Vendor category + description: ThirdParty category headquarter_address: type: string description: Headquarter address @@ -992,28 +992,28 @@ components: type: string description: Trust page URL - AddVendorOutput: + AddThirdPartyOutput: type: object required: - - vendor + - thirdParty properties: - vendor: - $ref: "#/components/schemas/Vendor" + thirdParty: + $ref: "#/components/schemas/ThirdParty" - UpdateVendorInput: + UpdateThirdPartyInput: type: object required: - id properties: id: $ref: "#/components/schemas/GID" - description: Vendor ID + description: ThirdParty ID name: type: string - description: Vendor name + description: ThirdParty name description: type: string - description: Vendor description + description: ThirdParty description category: type: string enum: @@ -1039,7 +1039,7 @@ components: - SALES - SECURITY - VERSION_CONTROL - description: Vendor category + description: ThirdParty category headquarter_address: type: string description: Headquarter address @@ -1093,37 +1093,37 @@ components: type: string description: Trust page URL - UpdateVendorOutput: + UpdateThirdPartyOutput: type: object required: - - vendor + - thirdParty properties: - vendor: - $ref: "#/components/schemas/Vendor" + thirdParty: + $ref: "#/components/schemas/ThirdParty" - DeleteVendorInput: + DeleteThirdPartyInput: type: object required: - id properties: id: $ref: "#/components/schemas/GID" - description: Vendor ID + description: ThirdParty ID - DeleteVendorOutput: + DeleteThirdPartyOutput: type: object required: - - deleted_vendor_id + - deleted_third_party_id properties: - deleted_vendor_id: + deleted_third_party_id: $ref: "#/components/schemas/GID" - description: Deleted vendor ID + description: Deleted thirdParty ID - VendorContact: + ThirdPartyContact: type: object required: - id - - vendor_id + - third_party_id - full_name - email - phone @@ -1133,10 +1133,10 @@ components: properties: id: $ref: "#/components/schemas/GID" - description: Vendor contact ID - vendor_id: + description: ThirdParty contact ID + third_party_id: $ref: "#/components/schemas/GID" - description: Vendor ID + description: ThirdParty ID full_name: type: string description: Full name @@ -1158,17 +1158,17 @@ components: format: date-time description: Update timestamp - ListVendorContactsInput: + ListThirdPartyContactsInput: type: object required: - - vendor_id + - third_party_id properties: - vendor_id: + third_party_id: $ref: "#/components/schemas/GID" - description: Vendor ID + description: ThirdParty ID order_by: - $ref: "#/components/schemas/VendorContactOrderBy" - description: Vendor contact order by + $ref: "#/components/schemas/ThirdPartyContactOrderBy" + description: ThirdParty contact order by size: type: integer description: Page size @@ -1176,31 +1176,31 @@ components: $ref: "#/components/schemas/CursorKey" description: Page cursor - ListVendorContactsOutput: + ListThirdPartyContactsOutput: type: object required: - - vendor_contacts + - third_party_contacts properties: next_cursor: $ref: "#/components/schemas/CursorKey" description: Next cursor - vendor_contacts: + third_party_contacts: type: array items: - $ref: "#/components/schemas/VendorContact" + $ref: "#/components/schemas/ThirdPartyContact" - AddVendorContactInput: + AddThirdPartyContactInput: type: object required: - - vendor_id + - third_party_id - full_name - email - phone - role properties: - vendor_id: + third_party_id: $ref: "#/components/schemas/GID" - description: Vendor ID + description: ThirdParty ID full_name: type: string description: Full name @@ -1214,22 +1214,22 @@ components: type: string description: Role - AddVendorContactOutput: + AddThirdPartyContactOutput: type: object required: - - vendor_contact + - third_party_contact properties: - vendor_contact: - $ref: "#/components/schemas/VendorContact" + third_party_contact: + $ref: "#/components/schemas/ThirdPartyContact" - UpdateVendorContactInput: + UpdateThirdPartyContactInput: type: object required: - id properties: id: $ref: "#/components/schemas/GID" - description: Vendor contact ID + description: ThirdParty contact ID full_name: type: string description: Full name @@ -1243,37 +1243,37 @@ components: type: string description: Role - UpdateVendorContactOutput: + UpdateThirdPartyContactOutput: type: object required: - - vendor_contact + - third_party_contact properties: - vendor_contact: - $ref: "#/components/schemas/VendorContact" + third_party_contact: + $ref: "#/components/schemas/ThirdPartyContact" - DeleteVendorContactInput: + DeleteThirdPartyContactInput: type: object required: - id properties: id: $ref: "#/components/schemas/GID" - description: Vendor contact ID + description: ThirdParty contact ID - DeleteVendorContactOutput: + DeleteThirdPartyContactOutput: type: object required: - - deleted_vendor_contact_id + - deleted_third_party_contact_id properties: - deleted_vendor_contact_id: + deleted_third_party_contact_id: $ref: "#/components/schemas/GID" - description: Deleted vendor contact ID + description: Deleted thirdParty contact ID - VendorService: + ThirdPartyService: type: object required: - id - - vendor_id + - third_party_id - name - description - created_at @@ -1281,10 +1281,10 @@ components: properties: id: $ref: "#/components/schemas/GID" - description: Vendor service ID - vendor_id: + description: ThirdParty service ID + third_party_id: $ref: "#/components/schemas/GID" - description: Vendor ID + description: ThirdParty ID name: type: string description: Service name @@ -1300,17 +1300,17 @@ components: format: date-time description: Update timestamp - ListVendorServicesInput: + ListThirdPartyServicesInput: type: object required: - - vendor_id + - third_party_id properties: - vendor_id: + third_party_id: $ref: "#/components/schemas/GID" - description: Vendor ID + description: ThirdParty ID order_by: - $ref: "#/components/schemas/VendorServiceOrderBy" - description: Vendor service order by + $ref: "#/components/schemas/ThirdPartyServiceOrderBy" + description: ThirdParty service order by size: type: integer description: Page size @@ -1318,28 +1318,28 @@ components: $ref: "#/components/schemas/CursorKey" description: Page cursor - ListVendorServicesOutput: + ListThirdPartyServicesOutput: type: object required: - - vendor_services + - third_party_services properties: next_cursor: $ref: "#/components/schemas/CursorKey" description: Next cursor - vendor_services: + third_party_services: type: array items: - $ref: "#/components/schemas/VendorService" + $ref: "#/components/schemas/ThirdPartyService" - AddVendorServiceInput: + AddThirdPartyServiceInput: type: object required: - - vendor_id + - third_party_id - name properties: - vendor_id: + third_party_id: $ref: "#/components/schemas/GID" - description: Vendor ID + description: ThirdParty ID name: type: string description: Service name @@ -1347,22 +1347,22 @@ components: type: string description: Service description - AddVendorServiceOutput: + AddThirdPartyServiceOutput: type: object required: - - vendor_service + - third_party_service properties: - vendor_service: - $ref: "#/components/schemas/VendorService" + third_party_service: + $ref: "#/components/schemas/ThirdPartyService" - UpdateVendorServiceInput: + UpdateThirdPartyServiceInput: type: object required: - id properties: id: $ref: "#/components/schemas/GID" - description: Vendor service ID + description: ThirdParty service ID name: type: string description: Service name @@ -1370,33 +1370,33 @@ components: type: string description: Service description - UpdateVendorServiceOutput: + UpdateThirdPartyServiceOutput: type: object required: - - vendor_service + - third_party_service properties: - vendor_service: - $ref: "#/components/schemas/VendorService" + third_party_service: + $ref: "#/components/schemas/ThirdPartyService" - DeleteVendorServiceInput: + DeleteThirdPartyServiceInput: type: object required: - id properties: id: $ref: "#/components/schemas/GID" - description: Vendor service ID + description: ThirdParty service ID - DeleteVendorServiceOutput: + DeleteThirdPartyServiceOutput: type: object required: - - deleted_vendor_service_id + - deleted_third_party_service_id properties: - deleted_vendor_service_id: + deleted_third_party_service_id: $ref: "#/components/schemas/GID" - description: Deleted vendor service ID + description: Deleted thirdParty service ID - AssessVendorInput: + AssessThirdPartyInput: type: object required: - id @@ -1404,15 +1404,15 @@ components: properties: id: $ref: "#/components/schemas/GID" - description: Vendor ID to assess + description: ThirdParty ID to assess website_url: type: string - description: Vendor website URL to crawl and assess + description: ThirdParty website URL to crawl and assess procedure: type: string description: Optional custom assessment procedure (overrides the default) - VendorSubprocessor: + ThirdPartySubprocessor: type: object required: - name @@ -1429,22 +1429,22 @@ components: type: string description: Purpose of the sub-processor - AssessVendorOutput: + AssessThirdPartyOutput: type: object required: - - vendor + - thirdParty - report - subprocessors properties: - vendor: - $ref: "#/components/schemas/Vendor" + thirdParty: + $ref: "#/components/schemas/ThirdParty" report: type: string - description: Markdown-formatted vendor assessment report + description: Markdown-formatted thirdParty assessment report subprocessors: type: array items: - $ref: "#/components/schemas/VendorSubprocessor" + $ref: "#/components/schemas/ThirdPartySubprocessor" description: Sub-processors discovered during the assessment GetUserInput: @@ -2733,11 +2733,11 @@ components: data_types_stored: type: string description: Data types stored - vendor_ids: + third_party_ids: type: array items: $ref: "#/components/schemas/GID" - description: Vendor IDs + description: ThirdParty IDs AddAssetOutput: type: object @@ -2773,11 +2773,11 @@ components: data_types_stored: type: string description: Data types stored - vendor_ids: + third_party_ids: type: array items: $ref: "#/components/schemas/GID" - description: Vendor IDs + description: ThirdParty IDs UpdateAssetOutput: type: object @@ -2937,11 +2937,11 @@ components: owner_id: $ref: "#/components/schemas/GID" description: Owner ID - vendor_ids: + third_party_ids: type: array items: $ref: "#/components/schemas/GID" - description: Vendor IDs + description: ThirdParty IDs AddDatumOutput: type: object @@ -2971,11 +2971,11 @@ components: $ref: "#/components/schemas/GID" - type: "null" description: Owner ID - vendor_ids: + third_party_ids: type: array items: $ref: "#/components/schemas/GID" - description: Vendor IDs + description: ThirdParty IDs UpdateDatumOutput: type: object @@ -4065,11 +4065,11 @@ components: - $ref: "#/components/schemas/GID" - type: "null" description: Data protection officer profile ID - vendor_ids: + third_party_ids: type: array items: $ref: "#/components/schemas/GID" - description: Vendor IDs + description: ThirdParty IDs AddProcessingActivityOutput: type: object @@ -4143,7 +4143,7 @@ components: - $ref: "#/components/schemas/GID" - type: "null" go.probo.inc/mcpgen/omittable: true - vendor_ids: + third_party_ids: type: array items: $ref: "#/components/schemas/GID" @@ -6534,9 +6534,9 @@ components: WebhookEventType: type: string enum: - - "vendor:created" - - "vendor:updated" - - "vendor:deleted" + - "third-party:created" + - "third-party:updated" + - "third-party:deleted" - "user:created" - "user:updated" - "user:deleted" @@ -7182,7 +7182,7 @@ components: $ref: "#/components/schemas/GID" description: Created document version ID - PublishVendorListInput: + PublishThirdPartyListInput: type: object required: - organization_id @@ -7200,7 +7200,7 @@ components: type: boolean description: When true, publish as a minor version (currentMajor.currentMinor+1) and ignore approver_ids; the document must already have a published major version. When false, publish as a new major version; if approver_ids are provided, an approval is requested instead. - PublishVendorListOutput: + PublishThirdPartyListOutput: type: object required: - document_id @@ -8402,13 +8402,13 @@ components: properties: action: type: string - description: Filter by action (e.g. "core:vendor:create") + description: Filter by action (e.g. "core:thirdParty:create") actor_id: $ref: "#/components/schemas/GID" description: Filter by actor ID resource_type: type: string - description: Filter by resource type (e.g. "Vendor") + description: Filter by resource type (e.g. "ThirdParty") resource_id: $ref: "#/components/schemas/GID" description: Filter by resource ID @@ -8454,10 +8454,10 @@ components: description: Type of actor action: type: string - description: Action performed (e.g. "core:vendor:create") + description: Action performed (e.g. "core:thirdParty:create") resource_type: type: string - description: Type of resource affected (e.g. "Vendor") + description: Type of resource affected (e.g. "ThirdParty") resource_id: $ref: "#/components/schemas/GID" description: ID of the affected resource @@ -10716,15 +10716,15 @@ tools: $ref: "#/components/schemas/ListOrganizationsInput" outputSchema: $ref: "#/components/schemas/ListOrganizationsOutput" - - name: listVendors - description: List all vendors for the organization + - name: listThirdParties + description: List all thirdParties for the organization hints: readonly: true idempotent: true inputSchema: - $ref: "#/components/schemas/ListVendorsInput" + $ref: "#/components/schemas/ListThirdPartiesInput" outputSchema: - $ref: "#/components/schemas/ListVendorsOutput" + $ref: "#/components/schemas/ListThirdPartiesOutput" - name: listUsers description: List all users for the organization hints: @@ -10783,124 +10783,124 @@ tools: $ref: "#/components/schemas/RemoveUserInput" outputSchema: $ref: "#/components/schemas/RemoveUserOutput" - - name: addVendor - description: Add a new vendor to the organization + - name: addThirdParty + description: Add a new thirdParty to the organization hints: readonly: false inputSchema: - $ref: "#/components/schemas/AddVendorInput" + $ref: "#/components/schemas/AddThirdPartyInput" outputSchema: - $ref: "#/components/schemas/AddVendorOutput" - - name: updateVendor - description: Update an existing vendor + $ref: "#/components/schemas/AddThirdPartyOutput" + - name: updateThirdParty + description: Update an existing thirdParty hints: readonly: false inputSchema: - $ref: "#/components/schemas/UpdateVendorInput" + $ref: "#/components/schemas/UpdateThirdPartyInput" outputSchema: - $ref: "#/components/schemas/UpdateVendorOutput" - - name: listVendorRiskAssessments - description: List all risk assessments for a vendor + $ref: "#/components/schemas/UpdateThirdPartyOutput" + - name: listThirdPartyRiskAssessments + description: List all risk assessments for a thirdParty hints: readonly: true idempotent: true inputSchema: - $ref: "#/components/schemas/ListVendorRiskAssessmentsInput" + $ref: "#/components/schemas/ListThirdPartyRiskAssessmentsInput" outputSchema: - $ref: "#/components/schemas/ListVendorRiskAssessmentsOutput" - - name: addVendorRiskAssessment - description: Add a new risk assessment for a vendor + $ref: "#/components/schemas/ListThirdPartyRiskAssessmentsOutput" + - name: addThirdPartyRiskAssessment + description: Add a new risk assessment for a thirdParty hints: readonly: false inputSchema: - $ref: "#/components/schemas/AddVendorRiskAssessmentInput" + $ref: "#/components/schemas/AddThirdPartyRiskAssessmentInput" outputSchema: - $ref: "#/components/schemas/AddVendorRiskAssessmentOutput" - - name: deleteVendor - description: Delete a vendor + $ref: "#/components/schemas/AddThirdPartyRiskAssessmentOutput" + - name: deleteThirdParty + description: Delete a thirdParty hints: readonly: false destructive: true inputSchema: - $ref: "#/components/schemas/DeleteVendorInput" + $ref: "#/components/schemas/DeleteThirdPartyInput" outputSchema: - $ref: "#/components/schemas/DeleteVendorOutput" - - name: listVendorContacts - description: List all contacts for a vendor + $ref: "#/components/schemas/DeleteThirdPartyOutput" + - name: listThirdPartyContacts + description: List all contacts for a thirdParty hints: readonly: true idempotent: true inputSchema: - $ref: "#/components/schemas/ListVendorContactsInput" + $ref: "#/components/schemas/ListThirdPartyContactsInput" outputSchema: - $ref: "#/components/schemas/ListVendorContactsOutput" - - name: addVendorContact - description: Add a new contact to a vendor + $ref: "#/components/schemas/ListThirdPartyContactsOutput" + - name: addThirdPartyContact + description: Add a new contact to a thirdParty hints: readonly: false inputSchema: - $ref: "#/components/schemas/AddVendorContactInput" + $ref: "#/components/schemas/AddThirdPartyContactInput" outputSchema: - $ref: "#/components/schemas/AddVendorContactOutput" - - name: updateVendorContact - description: Update an existing vendor contact + $ref: "#/components/schemas/AddThirdPartyContactOutput" + - name: updateThirdPartyContact + description: Update an existing thirdParty contact hints: readonly: false inputSchema: - $ref: "#/components/schemas/UpdateVendorContactInput" + $ref: "#/components/schemas/UpdateThirdPartyContactInput" outputSchema: - $ref: "#/components/schemas/UpdateVendorContactOutput" - - name: deleteVendorContact - description: Delete a vendor contact + $ref: "#/components/schemas/UpdateThirdPartyContactOutput" + - name: deleteThirdPartyContact + description: Delete a thirdParty contact hints: readonly: false destructive: true inputSchema: - $ref: "#/components/schemas/DeleteVendorContactInput" + $ref: "#/components/schemas/DeleteThirdPartyContactInput" outputSchema: - $ref: "#/components/schemas/DeleteVendorContactOutput" - - name: listVendorServices - description: List all services for a vendor + $ref: "#/components/schemas/DeleteThirdPartyContactOutput" + - name: listThirdPartyServices + description: List all services for a thirdParty hints: readonly: true idempotent: true inputSchema: - $ref: "#/components/schemas/ListVendorServicesInput" + $ref: "#/components/schemas/ListThirdPartyServicesInput" outputSchema: - $ref: "#/components/schemas/ListVendorServicesOutput" - - name: addVendorService - description: Add a new service to a vendor + $ref: "#/components/schemas/ListThirdPartyServicesOutput" + - name: addThirdPartyService + description: Add a new service to a thirdParty hints: readonly: false inputSchema: - $ref: "#/components/schemas/AddVendorServiceInput" + $ref: "#/components/schemas/AddThirdPartyServiceInput" outputSchema: - $ref: "#/components/schemas/AddVendorServiceOutput" - - name: updateVendorService - description: Update an existing vendor service + $ref: "#/components/schemas/AddThirdPartyServiceOutput" + - name: updateThirdPartyService + description: Update an existing thirdParty service hints: readonly: false inputSchema: - $ref: "#/components/schemas/UpdateVendorServiceInput" + $ref: "#/components/schemas/UpdateThirdPartyServiceInput" outputSchema: - $ref: "#/components/schemas/UpdateVendorServiceOutput" - - name: deleteVendorService - description: Delete a vendor service + $ref: "#/components/schemas/UpdateThirdPartyServiceOutput" + - name: deleteThirdPartyService + description: Delete a thirdParty service hints: readonly: false destructive: true inputSchema: - $ref: "#/components/schemas/DeleteVendorServiceInput" + $ref: "#/components/schemas/DeleteThirdPartyServiceInput" outputSchema: - $ref: "#/components/schemas/DeleteVendorServiceOutput" - - name: assessVendor - description: Run an AI-powered assessment on a vendor by crawling its website. Returns a markdown report, the discovered sub-processors, and an enriched vendor record. Long-running (up to 20 minutes). + $ref: "#/components/schemas/DeleteThirdPartyServiceOutput" + - name: assessThirdParty + description: Run an AI-powered assessment on a thirdParty by crawling its website. Returns a markdown report, the discovered sub-processors, and an enriched thirdParty record. Long-running (up to 20 minutes). hints: readonly: false inputSchema: - $ref: "#/components/schemas/AssessVendorInput" + $ref: "#/components/schemas/AssessThirdPartyInput" outputSchema: - $ref: "#/components/schemas/AssessVendorOutput" + $ref: "#/components/schemas/AssessThirdPartyOutput" - name: listRisks description: List all risks for the organization hints: @@ -11873,14 +11873,14 @@ tools: $ref: "#/components/schemas/PublishTransferImpactAssessmentListInput" outputSchema: $ref: "#/components/schemas/PublishTransferImpactAssessmentListOutput" - - name: publishVendorList - description: Publish the vendor register for an organization as a document. If a document already exists, a new version is created. + - name: publishThirdPartyList + description: Publish the thirdParty register for an organization as a document. If a document already exists, a new version is created. hints: readonly: false inputSchema: - $ref: "#/components/schemas/PublishVendorListInput" + $ref: "#/components/schemas/PublishThirdPartyListInput" outputSchema: - $ref: "#/components/schemas/PublishVendorListOutput" + $ref: "#/components/schemas/PublishThirdPartyListOutput" - name: publishRiskList description: Publish the risk register for an organization as a document. If a document already exists, a new version is created. hints: diff --git a/pkg/server/api/mcp/v1/types/vendor.go b/pkg/server/api/mcp/v1/types/third_party.go similarity index 50% rename from pkg/server/api/mcp/v1/types/vendor.go rename to pkg/server/api/mcp/v1/types/third_party.go index 6d7b19e5b..e1e7a77c1 100644 --- a/pkg/server/api/mcp/v1/types/vendor.go +++ b/pkg/server/api/mcp/v1/types/third_party.go @@ -20,11 +20,11 @@ import ( "go.probo.inc/probo/pkg/probo" ) -func NewVendorRiskAssessment(v *coredata.VendorRiskAssessment) *VendorRiskAssessment { - return &VendorRiskAssessment{ +func NewThirdPartyRiskAssessment(v *coredata.ThirdPartyRiskAssessment) *ThirdPartyRiskAssessment { + return &ThirdPartyRiskAssessment{ ID: v.ID, OrganizationID: v.OrganizationID, - VendorID: v.VendorID, + ThirdPartyID: v.ThirdPartyID, ExpiresAt: v.ExpiresAt, DataSensitivity: v.DataSensitivity, BusinessImpact: v.BusinessImpact, @@ -34,10 +34,10 @@ func NewVendorRiskAssessment(v *coredata.VendorRiskAssessment) *VendorRiskAssess } } -func NewListVendorRiskAssessmentsOutput(p *page.Page[*coredata.VendorRiskAssessment, coredata.VendorRiskAssessmentOrderField]) ListVendorRiskAssessmentsOutput { - assessments := make([]*VendorRiskAssessment, 0, len(p.Data)) +func NewListThirdPartyRiskAssessmentsOutput(p *page.Page[*coredata.ThirdPartyRiskAssessment, coredata.ThirdPartyRiskAssessmentOrderField]) ListThirdPartyRiskAssessmentsOutput { + assessments := make([]*ThirdPartyRiskAssessment, 0, len(p.Data)) for _, v := range p.Data { - assessments = append(assessments, NewVendorRiskAssessment(v)) + assessments = append(assessments, NewThirdPartyRiskAssessment(v)) } var nextCursor *page.CursorKey @@ -46,30 +46,30 @@ func NewListVendorRiskAssessmentsOutput(p *page.Page[*coredata.VendorRiskAssessm nextCursor = &cursorKey } - return ListVendorRiskAssessmentsOutput{ - NextCursor: nextCursor, - VendorRiskAssessments: assessments, + return ListThirdPartyRiskAssessmentsOutput{ + NextCursor: nextCursor, + ThirdPartyRiskAssessments: assessments, } } -func NewAddVendorRiskAssessmentOutput(v *coredata.VendorRiskAssessment) AddVendorRiskAssessmentOutput { - return AddVendorRiskAssessmentOutput{ - VendorRiskAssessment: NewVendorRiskAssessment(v), +func NewAddThirdPartyRiskAssessmentOutput(v *coredata.ThirdPartyRiskAssessment) AddThirdPartyRiskAssessmentOutput { + return AddThirdPartyRiskAssessmentOutput{ + ThirdPartyRiskAssessment: NewThirdPartyRiskAssessment(v), } } -func NewVendor(v *coredata.Vendor) *Vendor { +func NewThirdParty(v *coredata.ThirdParty) *ThirdParty { countries := make([]string, len(v.Countries)) for i, c := range v.Countries { countries[i] = string(c) } - return &Vendor{ + return &ThirdParty{ ID: v.ID, OrganizationID: v.OrganizationID, Name: v.Name, Description: v.Description, - Category: VendorCategory(v.Category), + Category: ThirdPartyCategory(v.Category), HeadquarterAddress: v.HeadquarterAddress, LegalName: v.LegalName, WebsiteURL: v.WebsiteURL, @@ -91,37 +91,37 @@ func NewVendor(v *coredata.Vendor) *Vendor { } } -func NewListVendorsOutput(vendorPage *page.Page[*coredata.Vendor, coredata.VendorOrderField]) ListVendorsOutput { - vendors := make([]*Vendor, 0, len(vendorPage.Data)) - for _, v := range vendorPage.Data { - vendors = append(vendors, NewVendor(v)) +func NewListThirdPartiesOutput(thirdPartyPage *page.Page[*coredata.ThirdParty, coredata.ThirdPartyOrderField]) ListThirdPartiesOutput { + thirdParties := make([]*ThirdParty, 0, len(thirdPartyPage.Data)) + for _, v := range thirdPartyPage.Data { + thirdParties = append(thirdParties, NewThirdParty(v)) } var nextCursor *page.CursorKey - if len(vendorPage.Data) > 0 { - cursorKey := vendorPage.Data[len(vendorPage.Data)-1].CursorKey(vendorPage.Cursor.OrderBy.Field) + if len(thirdPartyPage.Data) > 0 { + cursorKey := thirdPartyPage.Data[len(thirdPartyPage.Data)-1].CursorKey(thirdPartyPage.Cursor.OrderBy.Field) nextCursor = &cursorKey } - return ListVendorsOutput{ - NextCursor: nextCursor, - Vendors: vendors, + return ListThirdPartiesOutput{ + NextCursor: nextCursor, + ThirdParties: thirdParties, } } -func NewAddVendorOutput(v *coredata.Vendor) AddVendorOutput { - return AddVendorOutput{ - Vendor: NewVendor(v), +func NewAddThirdPartyOutput(v *coredata.ThirdParty) AddThirdPartyOutput { + return AddThirdPartyOutput{ + ThirdParty: NewThirdParty(v), } } -func NewUpdateVendorOutput(v *coredata.Vendor) UpdateVendorOutput { - return UpdateVendorOutput{ - Vendor: NewVendor(v), +func NewUpdateThirdPartyOutput(v *coredata.ThirdParty) UpdateThirdPartyOutput { + return UpdateThirdPartyOutput{ + ThirdParty: NewThirdParty(v), } } -func NewVendorContact(vc *coredata.VendorContact) *VendorContact { +func NewThirdPartyContact(vc *coredata.ThirdPartyContact) *ThirdPartyContact { var fullName string if vc.FullName != nil { fullName = *vc.FullName @@ -142,22 +142,22 @@ func NewVendorContact(vc *coredata.VendorContact) *VendorContact { role = *vc.Role } - return &VendorContact{ - ID: vc.ID, - VendorID: vc.VendorID, - FullName: fullName, - Email: email, - Phone: phone, - Role: role, - CreatedAt: vc.CreatedAt, - UpdatedAt: vc.UpdatedAt, + return &ThirdPartyContact{ + ID: vc.ID, + ThirdPartyID: vc.ThirdPartyID, + FullName: fullName, + Email: email, + Phone: phone, + Role: role, + CreatedAt: vc.CreatedAt, + UpdatedAt: vc.UpdatedAt, } } -func NewListVendorContactsOutput(p *page.Page[*coredata.VendorContact, coredata.VendorContactOrderField]) ListVendorContactsOutput { - contacts := make([]*VendorContact, 0, len(p.Data)) +func NewListThirdPartyContactsOutput(p *page.Page[*coredata.ThirdPartyContact, coredata.ThirdPartyContactOrderField]) ListThirdPartyContactsOutput { + contacts := make([]*ThirdPartyContact, 0, len(p.Data)) for _, vc := range p.Data { - contacts = append(contacts, NewVendorContact(vc)) + contacts = append(contacts, NewThirdPartyContact(vc)) } var nextCursor *page.CursorKey @@ -166,32 +166,32 @@ func NewListVendorContactsOutput(p *page.Page[*coredata.VendorContact, coredata. nextCursor = &cursorKey } - return ListVendorContactsOutput{ - NextCursor: nextCursor, - VendorContacts: contacts, + return ListThirdPartyContactsOutput{ + NextCursor: nextCursor, + ThirdPartyContacts: contacts, } } -func NewVendorService(vs *coredata.VendorService) *VendorService { +func NewThirdPartyService(vs *coredata.ThirdPartyService) *ThirdPartyService { var description string if vs.Description != nil { description = *vs.Description } - return &VendorService{ - ID: vs.ID, - VendorID: vs.VendorID, - Name: vs.Name, - Description: description, - CreatedAt: vs.CreatedAt, - UpdatedAt: vs.UpdatedAt, + return &ThirdPartyService{ + ID: vs.ID, + ThirdPartyID: vs.ThirdPartyID, + Name: vs.Name, + Description: description, + CreatedAt: vs.CreatedAt, + UpdatedAt: vs.UpdatedAt, } } -func NewListVendorServicesOutput(p *page.Page[*coredata.VendorService, coredata.VendorServiceOrderField]) ListVendorServicesOutput { - services := make([]*VendorService, 0, len(p.Data)) +func NewListThirdPartyServicesOutput(p *page.Page[*coredata.ThirdPartyService, coredata.ThirdPartyServiceOrderField]) ListThirdPartyServicesOutput { + services := make([]*ThirdPartyService, 0, len(p.Data)) for _, vs := range p.Data { - services = append(services, NewVendorService(vs)) + services = append(services, NewThirdPartyService(vs)) } var nextCursor *page.CursorKey @@ -200,16 +200,16 @@ func NewListVendorServicesOutput(p *page.Page[*coredata.VendorService, coredata. nextCursor = &cursorKey } - return ListVendorServicesOutput{ - NextCursor: nextCursor, - VendorServices: services, + return ListThirdPartyServicesOutput{ + NextCursor: nextCursor, + ThirdPartyServices: services, } } -func NewVendorSubprocessors(sps []probo.Subprocessor) []*VendorSubprocessor { - result := make([]*VendorSubprocessor, len(sps)) +func NewThirdPartySubprocessors(sps []probo.Subprocessor) []*ThirdPartySubprocessor { + result := make([]*ThirdPartySubprocessor, len(sps)) for i, sp := range sps { - result[i] = &VendorSubprocessor{ + result[i] = &ThirdPartySubprocessor{ Name: sp.Name, Country: sp.Country, Purpose: sp.Purpose, @@ -218,10 +218,10 @@ func NewVendorSubprocessors(sps []probo.Subprocessor) []*VendorSubprocessor { return result } -func NewAssessVendorOutput(result *probo.AssessVendorResult) AssessVendorOutput { - return AssessVendorOutput{ - Vendor: NewVendor(result.Vendor), +func NewAssessThirdPartyOutput(result *probo.AssessThirdPartyResult) AssessThirdPartyOutput { + return AssessThirdPartyOutput{ + ThirdParty: NewThirdParty(result.ThirdParty), Report: result.Report, - Subprocessors: NewVendorSubprocessors(result.Subprocessors), + Subprocessors: NewThirdPartySubprocessors(result.Subprocessors), } } diff --git a/pkg/server/api/trust/v1/base_resolvers.go b/pkg/server/api/trust/v1/base_resolvers.go index b8b68f424..b352b52f3 100644 --- a/pkg/server/api/trust/v1/base_resolvers.go +++ b/pkg/server/api/trust/v1/base_resolvers.go @@ -97,13 +97,13 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error } return types.NewAudit(audit), nil - case coredata.VendorEntityType: - vendor, err := trustService.Vendors.Get(ctx, id) + case coredata.ThirdPartyEntityType: + thirdParty, err := trustService.ThirdParties.Get(ctx, id) if err != nil { - r.logger.ErrorCtx(ctx, "cannot get vendor", log.Error(err)) + r.logger.ErrorCtx(ctx, "cannot get thirdParty", log.Error(err)) return nil, gqlutils.Internal(ctx) } - return types.NewSubprocessor(vendor), nil + return types.NewSubprocessor(thirdParty), nil case coredata.TrustCenterEntityType: trustCenter, err := trustService.TrustCenters.Get(ctx, id) diff --git a/pkg/server/api/trust/v1/graphql/trust_center.graphql b/pkg/server/api/trust/v1/graphql/trust_center.graphql index 2757d00fc..10bbea93e 100644 --- a/pkg/server/api/trust/v1/graphql/trust_center.graphql +++ b/pkg/server/api/trust/v1/graphql/trust_center.graphql @@ -163,73 +163,73 @@ type ComplianceFrameworkEdge } enum SubprocessorCategory - @goModel(model: "go.probo.inc/probo/pkg/coredata.VendorCategory") { + @goModel(model: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategory") { ANALYTICS - @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryAnalytics") + @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryAnalytics") CLOUD_MONITORING @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCloudMonitoring" + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryCloudMonitoring" ) CLOUD_PROVIDER @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCloudProvider" + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryCloudProvider" ) COLLABORATION @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCollaboration" + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryCollaboration" ) CUSTOMER_SUPPORT @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCustomerSupport" + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryCustomerSupport" ) DATA_STORAGE_AND_PROCESSING @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryDataStorageAndProcessing" + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryDataStorageAndProcessing" ) DOCUMENT_MANAGEMENT @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryDocumentManagement" + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryDocumentManagement" ) EMPLOYEE_MANAGEMENT @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryEmployeeManagement" + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryEmployeeManagement" ) ENGINEERING - @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryEngineering") + @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryEngineering") FINANCE - @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryFinance") + @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryFinance") IDENTITY_PROVIDER @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryIdentityProvider" + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryIdentityProvider" ) - IT @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryIT") + IT @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryIT") MARKETING - @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryMarketing") + @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryMarketing") OFFICE_OPERATIONS @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryOfficeOperations" + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryOfficeOperations" ) - OTHER @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryOther") + OTHER @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryOther") PASSWORD_MANAGEMENT @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryPasswordManagement" + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryPasswordManagement" ) PRODUCT_AND_DESIGN @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryProductAndDesign" + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryProductAndDesign" ) PROFESSIONAL_SERVICES @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryProfessionalServices" + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryProfessionalServices" ) RECRUITING - @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryRecruiting") - SALES @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategorySales") + @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryRecruiting") + SALES @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategorySales") SECURITY - @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategorySecurity") + @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategorySecurity") VERSION_CONTROL @goEnum( - value: "go.probo.inc/probo/pkg/coredata.VendorCategoryVersionControl" + value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryVersionControl" ) } diff --git a/pkg/server/api/trust/v1/trust_center_resolvers.go b/pkg/server/api/trust/v1/trust_center_resolvers.go index 4c8c29536..f6d9a503e 100644 --- a/pkg/server/api/trust/v1/trust_center_resolvers.go +++ b/pkg/server/api/trust/v1/trust_center_resolvers.go @@ -598,7 +598,7 @@ func (r *subprocessorConnectionResolver) TotalCount(ctx context.Context, obj *ty switch obj.Resolver.(type) { case *trustCenterResolver: - count, err := trustService.Vendors.CountForTrustCenterId(ctx, obj.ParentID) + count, err := trustService.ThirdParties.CountForTrustCenterId(ctx, obj.ParentID) if err != nil { r.logger.ErrorCtx(ctx, "cannot count subprocessors", log.Error(err)) return 0, gqlutils.Internal(ctx) @@ -717,19 +717,19 @@ func (r *trustCenterResolver) Audits(ctx context.Context, obj *types.TrustCenter func (r *trustCenterResolver) Subprocessors(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.SubprocessorConnection, error) { trustService := r.TrustService(ctx, obj.ID.TenantID()) - pageOrderBy := page.OrderBy[coredata.VendorOrderField]{ - Field: coredata.VendorOrderFieldName, + pageOrderBy := page.OrderBy[coredata.ThirdPartyOrderField]{ + Field: coredata.ThirdPartyOrderFieldName, Direction: page.OrderDirectionAsc, } cursor := types.NewCursor(first, after, last, before, pageOrderBy) - vendorPage, err := trustService.Vendors.ListForOrganizationId(ctx, obj.Organization.ID, cursor) + thirdPartyPage, err := trustService.ThirdParties.ListForOrganizationId(ctx, obj.Organization.ID, cursor) if err != nil { r.logger.ErrorCtx(ctx, "cannot list subprocessors", log.Error(err)) return nil, gqlutils.Internal(ctx) } - return types.NewSubprocessorConnection(vendorPage, r, obj.ID), nil + return types.NewSubprocessorConnection(thirdPartyPage, r, obj.ID), nil } // References is the resolver for the references field. diff --git a/pkg/server/api/trust/v1/types/vendor.go b/pkg/server/api/trust/v1/types/third_party.go similarity index 83% rename from pkg/server/api/trust/v1/types/vendor.go rename to pkg/server/api/trust/v1/types/third_party.go index 6fc6a2d25..e9542139c 100644 --- a/pkg/server/api/trust/v1/types/vendor.go +++ b/pkg/server/api/trust/v1/types/third_party.go @@ -32,13 +32,13 @@ type ( ) func NewSubprocessorConnection( - p *page.Page[*coredata.Vendor, coredata.VendorOrderField], + p *page.Page[*coredata.ThirdParty, coredata.ThirdPartyOrderField], parentType any, parentID gid.GID, ) *SubprocessorConnection { edges := make([]*SubprocessorEdge, len(p.Data)) - for i, vendor := range p.Data { - edges[i] = NewSubprocessorEdge(vendor, p.Cursor.OrderBy.Field) + for i, thirdParty := range p.Data { + edges[i] = NewSubprocessorEdge(thirdParty, p.Cursor.OrderBy.Field) } return &SubprocessorConnection{ @@ -50,7 +50,7 @@ func NewSubprocessorConnection( } } -func NewSubprocessor(v *coredata.Vendor) *Subprocessor { +func NewSubprocessor(v *coredata.ThirdParty) *Subprocessor { return &Subprocessor{ ID: v.ID, Name: v.Name, @@ -62,7 +62,7 @@ func NewSubprocessor(v *coredata.Vendor) *Subprocessor { } } -func NewSubprocessorEdge(v *coredata.Vendor, orderField coredata.VendorOrderField) *SubprocessorEdge { +func NewSubprocessorEdge(v *coredata.ThirdParty, orderField coredata.ThirdPartyOrderField) *SubprocessorEdge { return &SubprocessorEdge{ Node: NewSubprocessor(v), Cursor: v.CursorKey(orderField), diff --git a/pkg/trust/compliance_page_service.go b/pkg/trust/compliance_page_service.go index 27b4b65de..e6e5bea70 100644 --- a/pkg/trust/compliance_page_service.go +++ b/pkg/trust/compliance_page_service.go @@ -66,7 +66,7 @@ type ( Frameworks []compliancePageFramework Documents []compliancePageDocument Audits []compliancePageAudit - Vendors []compliancePageVendor + ThirdParties []compliancePageThirdParty References []compliancePageReference ExternalLinks []compliancePageExternalLink } @@ -93,7 +93,7 @@ type ( ValidUntil string } - compliancePageVendor struct { + compliancePageThirdParty struct { Name string Category string Countries string @@ -158,9 +158,9 @@ func (s *Service) RenderCompliancePageMarkdown( return fmt.Errorf("cannot fetch audits: %w", err) } - data.Vendors, err = s.fetchVendors(ctx, tenantSvc, org.ID) + data.ThirdParties, err = s.fetchThirdParties(ctx, tenantSvc, org.ID) if err != nil { - return fmt.Errorf("cannot fetch vendors: %w", err) + return fmt.Errorf("cannot fetch thirdParties: %w", err) } data.References, err = s.fetchReferences(ctx, tenantSvc, trustCenterID) @@ -429,8 +429,8 @@ func (s *Service) fetchAudits(ctx context.Context, tenantSvc *TenantService, org return audits, nil } -func (s *Service) fetchVendors(ctx context.Context, tenantSvc *TenantService, orgID gid.GID) ([]compliancePageVendor, error) { - var vendors []compliancePageVendor +func (s *Service) fetchThirdParties(ctx context.Context, tenantSvc *TenantService, orgID gid.GID) ([]compliancePageThirdParty, error) { + var thirdParties []compliancePageThirdParty var cursorKey *page.CursorKey for { @@ -438,15 +438,15 @@ func (s *Service) fetchVendors(ctx context.Context, tenantSvc *TenantService, or page.MaxCursorSize, cursorKey, page.Head, - page.OrderBy[coredata.VendorOrderField]{ - Field: coredata.VendorOrderFieldName, + page.OrderBy[coredata.ThirdPartyOrderField]{ + Field: coredata.ThirdPartyOrderFieldName, Direction: page.OrderDirectionAsc, }, ) - result, err := tenantSvc.Vendors.ListForOrganizationId(ctx, orgID, cursor) + result, err := tenantSvc.ThirdParties.ListForOrganizationId(ctx, orgID, cursor) if err != nil { - return nil, fmt.Errorf("cannot list vendors: %w", err) + return nil, fmt.Errorf("cannot list thirdParties: %w", err) } for _, v := range result.Data { @@ -455,9 +455,9 @@ func (s *Service) fetchVendors(ctx context.Context, tenantSvc *TenantService, or countries = append(countries, c.String()) } - vendors = append( - vendors, - compliancePageVendor{ + thirdParties = append( + thirdParties, + compliancePageThirdParty{ Name: v.Name, Category: v.Category.String(), Countries: strings.Join(countries, ", "), @@ -471,11 +471,11 @@ func (s *Service) fetchVendors(ctx context.Context, tenantSvc *TenantService, or } last := result.Data[len(result.Data)-1] - ck := last.CursorKey(coredata.VendorOrderFieldName) + ck := last.CursorKey(coredata.ThirdPartyOrderFieldName) cursorKey = &ck } - return vendors, nil + return thirdParties, nil } func (s *Service) fetchReferences(ctx context.Context, tenantSvc *TenantService, trustCenterID gid.GID) ([]compliancePageReference, error) { diff --git a/pkg/trust/service.go b/pkg/trust/service.go index 720c16f90..5618185b6 100644 --- a/pkg/trust/service.go +++ b/pkg/trust/service.go @@ -65,7 +65,7 @@ type ( TrustCenters *TrustCenterService Documents *DocumentService Audits *AuditService - Vendors *VendorService + ThirdParties *ThirdPartyService Frameworks *FrameworkService ComplianceFrameworks *ComplianceFrameworkService TrustCenterAccesses *TrustCenterAccessService @@ -124,7 +124,7 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService { tenantService.TrustCenters = &TrustCenterService{svc: tenantService} tenantService.Documents = &DocumentService{svc: tenantService, html2pdfConverter: s.html2pdfConverter} tenantService.Audits = &AuditService{svc: tenantService} - tenantService.Vendors = &VendorService{svc: tenantService} + tenantService.ThirdParties = &ThirdPartyService{svc: tenantService} tenantService.Frameworks = &FrameworkService{svc: tenantService} tenantService.ComplianceFrameworks = &ComplianceFrameworkService{svc: tenantService} tenantService.TrustCenterAccesses = &TrustCenterAccessService{svc: tenantService, iamSvc: s.iam, logger: s.logger} diff --git a/pkg/trust/vendor_service.go b/pkg/trust/third_party_service.go similarity index 62% rename from pkg/trust/vendor_service.go rename to pkg/trust/third_party_service.go index 42dab04e0..a919b0bef 100644 --- a/pkg/trust/vendor_service.go +++ b/pkg/trust/third_party_service.go @@ -24,22 +24,22 @@ import ( "go.probo.inc/probo/pkg/page" ) -type VendorService struct { +type ThirdPartyService struct { svc *TenantService } -func (s VendorService) Get( +func (s ThirdPartyService) Get( ctx context.Context, - vendorID gid.GID, -) (*coredata.Vendor, error) { - vendor := &coredata.Vendor{} + thirdPartyID gid.GID, +) (*coredata.ThirdParty, error) { + thirdParty := &coredata.ThirdParty{} err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { - err := vendor.LoadByID(ctx, conn, s.svc.scope, vendorID) + err := thirdParty.LoadByID(ctx, conn, s.svc.scope, thirdPartyID) if err != nil { - return fmt.Errorf("cannot load vendor: %w", err) + return fmt.Errorf("cannot load thirdParty: %w", err) } return nil @@ -50,25 +50,25 @@ func (s VendorService) Get( return nil, err } - return vendor, nil + return thirdParty, nil } -func (s VendorService) ListForOrganizationId( +func (s ThirdPartyService) ListForOrganizationId( ctx context.Context, organizationID gid.GID, - cursor *page.Cursor[coredata.VendorOrderField], -) (*page.Page[*coredata.Vendor, coredata.VendorOrderField], error) { - var vendors coredata.Vendors + cursor *page.Cursor[coredata.ThirdPartyOrderField], +) (*page.Page[*coredata.ThirdParty, coredata.ThirdPartyOrderField], error) { + var thirdParties coredata.ThirdParties err := s.svc.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { showOnTrustCenter := true - filter := coredata.NewVendorFilter(&showOnTrustCenter) + filter := coredata.NewThirdPartyFilter(&showOnTrustCenter) - err := vendors.LoadByOrganizationID(ctx, conn, s.svc.scope, organizationID, cursor, filter) + err := thirdParties.LoadByOrganizationID(ctx, conn, s.svc.scope, organizationID, cursor, filter) if err != nil { - return fmt.Errorf("cannot load vendors: %w", err) + return fmt.Errorf("cannot load thirdParties: %w", err) } return nil @@ -79,10 +79,10 @@ func (s VendorService) ListForOrganizationId( return nil, err } - return page.NewPage(vendors, cursor), nil + return page.NewPage(thirdParties, cursor), nil } -func (s VendorService) CountForTrustCenterId( +func (s ThirdPartyService) CountForTrustCenterId( ctx context.Context, trustCenterID gid.GID, ) (int, error) { @@ -96,12 +96,12 @@ func (s VendorService) CountForTrustCenterId( return fmt.Errorf("cannot load trust center: %w", err) } - vendors := &coredata.Vendors{} + thirdParties := &coredata.ThirdParties{} showOnTrustCenter := true - filter := coredata.NewVendorFilter(&showOnTrustCenter) - count, err = vendors.CountByOrganizationID(ctx, conn, s.svc.scope, trustCenter.OrganizationID, filter) + filter := coredata.NewThirdPartyFilter(&showOnTrustCenter) + count, err = thirdParties.CountByOrganizationID(ctx, conn, s.svc.scope, trustCenter.OrganizationID, filter) if err != nil { - return fmt.Errorf("cannot count vendors: %w", err) + return fmt.Errorf("cannot count thirdParties: %w", err) } return nil diff --git a/pkg/vetting/assessment.go b/pkg/vetting/assessment.go index ca8e776b6..26408665a 100644 --- a/pkg/vetting/assessment.go +++ b/pkg/vetting/assessment.go @@ -30,7 +30,7 @@ import ( const ( // DefaultMaxTokens is the fallback max-tokens budget used when the - // vendor-assessor agent config does not specify a value. Sized to + // third-party-assessor agent config does not specify a value. Sized to // leave headroom above the orchestrator's thinking budget on // Anthropic models. DefaultMaxTokens = 16384 @@ -40,15 +40,15 @@ const ( AssessmentTimeout = 20 * time.Minute // extractionTimeout is the dedicated budget for the final - // vendor_info_extractor turn. It runs outside the orchestrator's + // third_party_info_extractor turn. It runs outside the orchestrator's // budget so a slow orchestrator can't starve the extractor. extractionTimeout = 5 * time.Minute ) -// vendorCategoryEnum is the canonical list of allowed values for -// VendorInfo.Category. It is duplicated into the jsonschema struct tag +// thirdPartyCategoryEnum is the canonical list of allowed values for +// ThirdPartyInfo.Category. It is duplicated into the jsonschema struct tag // because Go struct tags must be compile-time string literals. -var vendorCategoryEnum = []string{ +var thirdPartyCategoryEnum = []string{ "ANALYTICS", "ACCOUNTING", "CLOUD_MONITORING", "CLOUD_PROVIDER", "COLLABORATION", "CONSULTING", "CUSTOMER_SUPPORT", "DATA_STORAGE_AND_PROCESSING", "DOCUMENT_MANAGEMENT", @@ -58,9 +58,9 @@ var vendorCategoryEnum = []string{ "RECRUITING", "SALES", "SECURITY", "STAFFING", "VERSION_CONTROL", } -// vendorTypeEnum is the canonical list of allowed values for -// VendorInfo.VendorType. -var vendorTypeEnum = []string{ +// thirdPartyTypeEnum is the canonical list of allowed values for +// ThirdPartyInfo.ThirdPartyType. +var thirdPartyTypeEnum = []string{ "SAAS", "INFRASTRUCTURE", "PROFESSIONAL_SERVICES", "STAFFING", "OTHER", } @@ -95,22 +95,22 @@ type ( Notes string `json:"notes"` } - VendorInfo struct { - Name string `json:"name" jsonschema:"Vendor display name as shown on the website"` - Description string `json:"description" jsonschema:"One-sentence description of what the vendor does"` - Category string `json:"category" jsonschema:"Vendor category; one of vendorCategoryEnum"` - VendorType string `json:"vendor_type" jsonschema:"Vendor type; one of vendorTypeEnum"` - HeadquarterAddress string `json:"headquarter_address" jsonschema:"Vendor headquarters address (city, country) if mentioned"` + ThirdPartyInfo struct { + Name string `json:"name" jsonschema:"Third party display name as shown on the website"` + Description string `json:"description" jsonschema:"One-sentence description of what the third party does"` + Category string `json:"category" jsonschema:"Third party category; one of thirdPartyCategoryEnum"` + ThirdPartyType string `json:"third_party_type" jsonschema:"Third party type; one of thirdPartyTypeEnum"` + HeadquarterAddress string `json:"headquarter_address" jsonschema:"Third party headquarters address (city, country) if mentioned"` LegalName string `json:"legal_name" jsonschema:"Legal entity name if different from display name (e.g. 'Datadog, Inc.')"` - PrivacyPolicyURL string `json:"privacy_policy_url" jsonschema:"URL to the vendor's privacy policy page"` + PrivacyPolicyURL string `json:"privacy_policy_url" jsonschema:"URL to the third_party's privacy policy page"` ServiceLevelAgreementURL string `json:"service_level_agreement_url" jsonschema:"URL to the SLA page"` DataProcessingAgreementURL string `json:"data_processing_agreement_url" jsonschema:"URL to the DPA page"` BusinessAssociateAgreementURL string `json:"business_associate_agreement_url" jsonschema:"URL to the BAA page if HIPAA-eligible"` SubprocessorsListURL string `json:"subprocessors_list_url" jsonschema:"URL to the public subprocessors list"` - SecurityPageURL string `json:"security_page_url" jsonschema:"URL to the vendor's security page"` + SecurityPageURL string `json:"security_page_url" jsonschema:"URL to the third_party's security page"` TrustPageURL string `json:"trust_page_url" jsonschema:"URL to the trust center"` TermsOfServiceURL string `json:"terms_of_service_url" jsonschema:"URL to the terms of service"` - StatusPageURL string `json:"status_page_url" jsonschema:"URL to the vendor's status / uptime page"` + StatusPageURL string `json:"status_page_url" jsonschema:"URL to the third_party's status / uptime page"` BugBountyURL string `json:"bug_bounty_url" jsonschema:"URL to the bug bounty or responsible disclosure program"` IncidentResponseURL string `json:"incident_response_url" jsonschema:"URL to incident response or post-mortem documentation"` DataLocations []string `json:"data_locations" jsonschema:"Countries or regions where data is processed or stored (e.g. 'United States', 'EU', 'Germany')"` @@ -119,19 +119,19 @@ type ( // Privacy classification (ISO 27701). PrivacyRole string `json:"privacy_role" jsonschema:"Privacy role under ISO 27701: CONTROLLER, PROCESSOR, SUBPROCESSOR, NONE"` - ProcessesPII bool `json:"processes_pii" jsonschema:"Whether the vendor processes personal data"` + ProcessesPII bool `json:"processes_pii" jsonschema:"Whether the third_party processes personal data"` CrossBorderTransfer bool `json:"cross_border_transfer" jsonschema:"Whether cross-border data transfers occur"` // Privacy risk fields. DPAStatus string `json:"dpa_status" jsonschema:"DPA accessibility: AVAILABLE, AVAILABLE_ON_REQUEST, NOT_FOUND, BEHIND_LOGIN"` - DSARCapability string `json:"dsar_capability" jsonschema:"Brief summary of how the vendor handles Data Subject Access Requests"` + DSARCapability string `json:"dsar_capability" jsonschema:"Brief summary of how the third_party handles Data Subject Access Requests"` DataMinimization string `json:"data_minimization" jsonschema:"Brief summary of data minimization practices"` PurposeLimitation string `json:"purpose_limitation" jsonschema:"Brief summary of purpose limitation commitments"` RetentionPolicy string `json:"retention_policy" jsonschema:"Brief summary of data retention policy"` DeletionPolicy string `json:"deletion_policy" jsonschema:"Brief summary of data deletion policy"` // AI classification (ISO 42001). - InvolvesAI bool `json:"involves_ai" jsonschema:"Whether the vendor uses AI/ML in their product or service"` + InvolvesAI bool `json:"involves_ai" jsonschema:"Whether the third_party uses AI/ML in their product or service"` AIUseCases []string `json:"ai_use_cases" jsonschema:"Array of AI use case descriptions (e.g. 'content generation', 'fraud detection')"` // AI risk fields. @@ -165,7 +165,7 @@ type ( Result struct { Document string - Info VendorInfo + Info ThirdPartyInfo } ) @@ -191,10 +191,10 @@ func (a *Assessor) Assess(ctx context.Context, websiteURL string, procedure stri ctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), AssessmentTimeout) defer cancel() - vendorBrowser := browser.NewBrowser(ctx, a.cfg.ChromeAddr) - defer vendorBrowser.Close() + thirdPartyBrowser := browser.NewBrowser(ctx, a.cfg.ChromeAddr) + defer thirdPartyBrowser.Close() - vendorBrowser.SetAllowedDomain(u.Hostname()) + thirdPartyBrowser.SetAllowedDomain(u.Hostname()) // Create an unrestricted browser for web search agents that need to // follow links to external sites (news, reviews, etc.). @@ -207,7 +207,7 @@ func (a *Assessor) Assess(ctx context.Context, websiteURL string, procedure stri a.cfg.MaxTokens, procedure, a.cfg.Logger, - vendorBrowser, + thirdPartyBrowser, researchBrowser, a.cfg.SearchEndpoint, reporter, @@ -226,20 +226,20 @@ func (a *Assessor) Assess(ctx context.Context, websiteURL string, procedure stri }, ) if err != nil { - return nil, fmt.Errorf("cannot assess vendor: %w", err) + return nil, fmt.Errorf("cannot assess thirdParty: %w", err) } document := result.FinalMessage().Text() - reportProgress(ctx, reporter, "extract_vendor_info", agent.ProgressEventStepStarted) + reportProgress(ctx, reporter, "extract_third_party_info", agent.ProgressEventStepStarted) - info, err := a.extractVendorInfo(ctx, document) + info, err := a.extractThirdPartyInfo(ctx, document) if err != nil { - reportProgress(ctx, reporter, "extract_vendor_info", agent.ProgressEventStepFailed) - return nil, fmt.Errorf("cannot extract vendor info: %w", err) + reportProgress(ctx, reporter, "extract_third_party_info", agent.ProgressEventStepFailed) + return nil, fmt.Errorf("cannot extract thirdParty info: %w", err) } - reportProgress(ctx, reporter, "extract_vendor_info", agent.ProgressEventStepCompleted) + reportProgress(ctx, reporter, "extract_third_party_info", agent.ProgressEventStepCompleted) return &Result{ Document: document, @@ -247,10 +247,10 @@ func (a *Assessor) Assess(ctx context.Context, websiteURL string, procedure stri }, nil } -func (a *Assessor) extractVendorInfo(ctx context.Context, document string) (*VendorInfo, error) { - outputType, err := vendorInfoOutputType() +func (a *Assessor) extractThirdPartyInfo(ctx context.Context, document string) (*ThirdPartyInfo, error) { + outputType, err := thirdPartyInfoOutputType() if err != nil { - return nil, fmt.Errorf("cannot build vendor info output type: %w", err) + return nil, fmt.Errorf("cannot build thirdParty info output type: %w", err) } // Run the extractor on its own timeout so a slow orchestrator @@ -264,7 +264,7 @@ func (a *Assessor) extractVendorInfo(ctx context.Context, document string) (*Ven defer cancel() extractor := agent.New( - "vendor_info_extractor", + "third_party_info_extractor", a.cfg.Client, agent.WithInstructions(extractionPrompt), agent.WithModel(a.cfg.Model), @@ -283,53 +283,53 @@ func (a *Assessor) extractVendorInfo(ctx context.Context, document string) (*Ven }, ) if err != nil { - return nil, fmt.Errorf("cannot extract vendor info: %w", err) + return nil, fmt.Errorf("cannot extract thirdParty info: %w", err) } - var info VendorInfo + var info ThirdPartyInfo if err := json.Unmarshal([]byte(result.FinalMessage().Text()), &info); err != nil { - return nil, fmt.Errorf("cannot parse vendor info output: %w", err) + return nil, fmt.Errorf("cannot parse thirdParty info output: %w", err) } return &info, nil } -// vendorInfoOutputType builds the VendorInfo structured output type and +// thirdPartyInfoOutputType builds the ThirdPartyInfo structured output type and // decorates its JSON Schema with explicit enum constraints on fields // whose allowed values live in package-level slices. jsonschema-go only // reads struct tags as free-form descriptions, so the enum list cannot // be encoded in the tag itself. -func vendorInfoOutputType() (*agent.OutputType, error) { - outputType, err := agent.NewOutputType[VendorInfo]("vendor_info") +func thirdPartyInfoOutputType() (*agent.OutputType, error) { + outputType, err := agent.NewOutputType[ThirdPartyInfo]("third_party_info") if err != nil { - return nil, fmt.Errorf("cannot create vendor info output type: %w", err) + return nil, fmt.Errorf("cannot create thirdParty info output type: %w", err) } var schema map[string]any if err := json.Unmarshal(outputType.Schema, &schema); err != nil { - return nil, fmt.Errorf("cannot unmarshal vendor info schema: %w", err) + return nil, fmt.Errorf("cannot unmarshal thirdParty info schema: %w", err) } properties, ok := schema["properties"].(map[string]any) if !ok { - return nil, fmt.Errorf("vendor info schema has no properties") + return nil, fmt.Errorf("thirdParty info schema has no properties") } enums := map[string][]string{ - "category": vendorCategoryEnum, - "vendor_type": vendorTypeEnum, + "category": thirdPartyCategoryEnum, + "third_party_type": thirdPartyTypeEnum, } for field, values := range enums { prop, ok := properties[field].(map[string]any) if !ok { - return nil, fmt.Errorf("vendor info schema has no %q property", field) + return nil, fmt.Errorf("thirdParty info schema has no %q property", field) } prop["enum"] = values } decorated, err := json.Marshal(schema) if err != nil { - return nil, fmt.Errorf("cannot marshal decorated vendor info schema: %w", err) + return nil, fmt.Errorf("cannot marshal decorated thirdParty info schema: %w", err) } outputType.Schema = decorated diff --git a/pkg/vetting/assessment_test.go b/pkg/vetting/assessment_test.go index 3401590a6..5273b0960 100644 --- a/pkg/vetting/assessment_test.go +++ b/pkg/vetting/assessment_test.go @@ -13,7 +13,7 @@ // PERFORMANCE OF THIS SOFTWARE. // This test file is white-box (package vetting, not vetting_test) so it -// can reach the unexported vendorInfoOutputType helper. +// can reach the unexported thirdPartyInfoOutputType helper. package vetting @@ -25,10 +25,10 @@ import ( "github.com/stretchr/testify/require" ) -func TestVendorInfoOutputType_DecoratesEnums(t *testing.T) { +func TestThirdPartyInfoOutputType_DecoratesEnums(t *testing.T) { t.Parallel() - outputType, err := vendorInfoOutputType() + outputType, err := thirdPartyInfoOutputType() require.NoError(t, err) require.NotNil(t, outputType) @@ -42,8 +42,8 @@ func TestVendorInfoOutputType_DecoratesEnums(t *testing.T) { field string expected []string }{ - {"category", vendorCategoryEnum}, - {"vendor_type", vendorTypeEnum}, + {"category", thirdPartyCategoryEnum}, + {"third_party_type", thirdPartyTypeEnum}, } for _, tt := range tests { diff --git a/pkg/vetting/orchestrator.go b/pkg/vetting/orchestrator.go index f5fadf1ed..9e059ad66 100644 --- a/pkg/vetting/orchestrator.go +++ b/pkg/vetting/orchestrator.go @@ -63,16 +63,16 @@ func newOrchestratorAgent( maxTokens int, procedure string, logger *log.Logger, - vendorBrowser *browser.Browser, + thirdPartyBrowser *browser.Browser, researchBrowser *browser.Browser, searchEndpoint string, reporter agent.ProgressReporter, ) (*agent.Agent, error) { - readOnlyBrowserTools := browser.NewReadOnlyToolset(vendorBrowser).Tools() + readOnlyBrowserTools := browser.NewReadOnlyToolset(thirdPartyBrowser).Tools() // Unrestricted browser tools for sub-agents that need to follow links // to external sites (subprocessor lists hosted on OneTrust/Transcend, - // research, vendor comparison). + // research, thirdParty comparison). unrestrictedBrowserTools := browser.NewInteractiveToolset(researchBrowser).Tools() securityTools := security.NewToolset().Tools() @@ -98,14 +98,14 @@ func newOrchestratorAgent( // Core sub-agents that always run. entries := []subAgentEntry{ { - toolName: "crawl_vendor_website", - description: "Crawl a vendor website to discover security, compliance, privacy, and legal pages. Returns structured JSON with categorized URLs (vendor_name, vendor_domain, discovered_urls, notes). Input: the vendor's main website URL.", + toolName: "crawl_third_party_website", + description: "Crawl a thirdParty website to discover security, compliance, privacy, and legal pages. Returns structured JSON with categorized URLs (third_party_name, third_party_domain, discovered_urls, notes). Input: the thirdParty's main website URL.", tools: readOnlyBrowserTools, build: buildCrawlerAgent, }, { toolName: "assess_security", - description: "Perform technical security checks on a domain. Returns structured JSON with per-check results (ssl, headers, dmarc, spf, breaches, dnssec, csp, cors, dns, whois) each with status (pass/warning/fail/error) and details. Input: the vendor's domain name (e.g. example.com).", + description: "Perform technical security checks on a domain. Returns structured JSON with per-check results (ssl, headers, dmarc, spf, breaches, dnssec, csp, cors, dns, whois) each with status (pass/warning/fail/error) and details. Input: the thirdParty's domain name (e.g. example.com).", tools: securityTools, build: buildSecurityAgent, }, @@ -123,13 +123,13 @@ func newOrchestratorAgent( }, { toolName: "assess_market_presence", - description: "Analyze a vendor's market presence. Returns structured JSON with notable_customers, case_studies, partnerships, company_size_signals, funding_info, and market_position. Input: the vendor's main website URL.", + description: "Analyze a thirdParty's market presence. Returns structured JSON with notable_customers, case_studies, partnerships, company_size_signals, funding_info, and market_position. Input: the thirdParty's main website URL.", tools: readOnlyBrowserTools, build: buildMarketAgent, }, { toolName: "extract_subprocessors", - description: "Find and extract the list of sub-processors from a vendor's website. Returns structured JSON with subprocessors (name, country, purpose), total_count, and source. Input: the vendor's main website URL or a known subprocessors page URL.", + description: "Find and extract the list of sub-processors from a thirdParty's website. Returns structured JSON with subprocessors (name, country, purpose), total_count, and source. Input: the thirdParty's main website URL or a known subprocessors page URL.", tools: subprocessorTools, build: buildSubprocessorAgent, }, @@ -198,28 +198,28 @@ func newOrchestratorAgent( entries = append(entries, subAgentEntry{ - toolName: "research_vendor_externally", - description: "Search the open web for external signals about the vendor. Returns structured JSON with security_incidents, regulatory_actions, customer_sentiment, recent_news, red_flags, and positive_signals. Input: the vendor's name and domain.", + toolName: "research_third_party_externally", + description: "Search the open web for external signals about the thirdParty. Returns structured JSON with security_incidents, regulatory_actions, customer_sentiment, recent_news, red_flags, and positive_signals. Input: the thirdParty's name and domain.", tools: websearchTools, build: buildWebsearchAgent, }, subAgentEntry{ toolName: "assess_financial_stability", - description: "Evaluate vendor financial stability. Returns structured JSON with company_age, funding, employee_count, legal_standing, ownership, risk_signals, overall_assessment, and confidence. Input: vendor name and website URL.", + description: "Evaluate thirdParty financial stability. Returns structured JSON with company_age, funding, employee_count, legal_standing, ownership, risk_signals, overall_assessment, and confidence. Input: thirdParty name and website URL.", tools: financialTools, build: buildFinancialStabilityAgent, }, subAgentEntry{ toolName: "assess_code_security", - description: "Evaluate open-source code security posture. Returns structured JSON with has_public_repos, security_advisories, dependency_management, release_cadence, security_policy, overall_assessment, and risk_signals. Input: vendor name and website URL.", + description: "Evaluate open-source code security posture. Returns structured JSON with has_public_repos, security_advisories, dependency_management, release_cadence, security_policy, overall_assessment, and risk_signals. Input: thirdParty name and website URL.", tools: codeSecurityTools, build: buildCodeSecurityAgent, }, subAgentEntry{ - toolName: "compare_vendor", - description: "Find and compare alternative vendors. Returns structured JSON with alternatives (name, certifications, security_score), comparison_summary, vendor_strengths, vendor_weaknesses, and overall_position. Input: vendor name, category, and website URL.", + toolName: "compare_thirdParty", + description: "Find and compare alternative thirdParties. Returns structured JSON with alternatives (name, certifications, security_score), comparison_summary, third_party_strengths, third_party_weaknesses, and overall_position. Input: thirdParty name, category, and website URL.", tools: comparisonTools, - build: buildVendorComparisonAgent, + build: buildThirdPartyComparisonAgent, }, ) } @@ -254,7 +254,7 @@ func newOrchestratorAgent( } return agent.New( - "vendor_assessment_orchestrator", + "third_party_assessment_orchestrator", client, opts..., ), nil diff --git a/pkg/vetting/output_types.go b/pkg/vetting/output_types.go index 8ea11e27a..70d49b673 100644 --- a/pkg/vetting/output_types.go +++ b/pkg/vetting/output_types.go @@ -26,10 +26,10 @@ type ( } CrawlerOutput struct { - VendorName string `json:"vendor_name" jsonschema:"The vendor's display name as found on the website"` - VendorDomain string `json:"vendor_domain" jsonschema:"The vendor's primary domain"` - DiscoveredURLs []DiscoveredURL `json:"discovered_urls" jsonschema:"All categorized URLs discovered during crawling"` - Notes string `json:"notes" jsonschema:"Observations about the site structure or crawl limitations"` + ThirdPartyName string `json:"third_party_name" jsonschema:"The third_party's display name as found on the website"` + ThirdPartyDomain string `json:"third_party_domain" jsonschema:"The third_party's primary domain"` + DiscoveredURLs []DiscoveredURL `json:"discovered_urls" jsonschema:"All categorized URLs discovered during crawling"` + Notes string `json:"notes" jsonschema:"Observations about the site structure or crawl limitations"` } // --- Security --- @@ -201,7 +201,7 @@ type ( // --- Professional Standing --- ProfessionalStandingOutput struct { - VendorType string `json:"vendor_type" jsonschema:"Type of professional services firm: law_firm, accounting, consulting, audit, staffing, other"` + ThirdPartyType string `json:"third_party_type" jsonschema:"Type of professional services firm: law_firm, accounting, consulting, audit, staffing, other"` Licensing string `json:"licensing" jsonschema:"Professional licensing details (bar admissions, CPA licenses)"` Memberships []string `json:"memberships" jsonschema:"Industry body memberships (ABA, AICPA, Big Four network, etc.)"` Insurance string `json:"insurance" jsonschema:"Professional liability / E&O insurance coverage details"` @@ -242,7 +242,7 @@ type ( } RegulatoryFramework struct { - Applicable bool `json:"applicable" jsonschema:"Whether this framework applies to the vendor"` + Applicable bool `json:"applicable" jsonschema:"Whether this framework applies to the third_party"` OverallStatus string `json:"overall_status" jsonschema:"Overall compliance status for this framework"` Articles []RegulatoryArticle `json:"articles" jsonschema:"Per-article compliance assessment"` Notes string `json:"notes" jsonschema:"General notes about framework applicability"` @@ -310,7 +310,7 @@ type ( } CodeSecurityOutput struct { - HasPublicRepos bool `json:"has_public_repos" jsonschema:"Whether the vendor has public repositories"` + HasPublicRepos bool `json:"has_public_repos" jsonschema:"Whether the third_party has public repositories"` GithubOrg string `json:"github_org" jsonschema:"GitHub organization or user name"` MainRepos []string `json:"main_repos" jsonschema:"Main public repositories identified"` SecurityAdvisories SecurityAdvisorySummary `json:"security_advisories" jsonschema:"Security advisory summary"` @@ -327,11 +327,11 @@ type ( Sources []string `json:"sources" jsonschema:"URLs visited during research"` } - // --- Vendor Comparison --- + // --- ThirdParty Comparison --- - AlternativeVendor struct { - Name string `json:"name" jsonschema:"Alternative vendor name"` - Website string `json:"website" jsonschema:"Alternative vendor website URL"` + AlternativeThirdParty struct { + Name string `json:"name" jsonschema:"Alternative third_party name"` + Website string `json:"website" jsonschema:"Alternative third_party website URL"` Certifications []string `json:"certifications" jsonschema:"Visible certifications"` TrustCenter bool `json:"trust_center" jsonschema:"Whether a trust center page was found"` PrivacyPolicy bool `json:"privacy_policy" jsonschema:"Whether a privacy policy was found"` @@ -346,14 +346,14 @@ type ( Transparency string `json:"transparency" jsonschema:"Relative transparency vs alternatives"` } - VendorComparisonOutput struct { - VendorCategory string `json:"vendor_category" jsonschema:"The vendor's product category"` - AssessedVendor string `json:"assessed_vendor" jsonschema:"The vendor being assessed"` - Alternatives []AlternativeVendor `json:"alternatives" jsonschema:"Alternative vendors identified and evaluated"` - ComparisonSummary ComparisonSummary `json:"comparison_summary" jsonschema:"Summary comparison across dimensions"` - VendorStrengths []string `json:"vendor_strengths" jsonschema:"Assessed vendor's strengths vs alternatives"` - VendorWeaknesses []string `json:"vendor_weaknesses" jsonschema:"Assessed vendor's weaknesses vs alternatives"` - OverallPosition string `json:"overall_position" jsonschema:"Vendor position: Above_Average, Average, or Below_Average"` - Notes string `json:"notes" jsonschema:"Additional comparison notes"` + ThirdPartyComparisonOutput struct { + ThirdPartyCategory string `json:"third_party_category" jsonschema:"The third_party's product category"` + AssessedThirdParty string `json:"assessed_thirdParty" jsonschema:"The third_party being assessed"` + Alternatives []AlternativeThirdParty `json:"alternatives" jsonschema:"Alternative third_parties identified and evaluated"` + ComparisonSummary ComparisonSummary `json:"comparison_summary" jsonschema:"Summary comparison across dimensions"` + ThirdPartyStrengths []string `json:"third_party_strengths" jsonschema:"Assessed third_party's strengths vs alternatives"` + ThirdPartyWeaknesses []string `json:"third_party_weaknesses" jsonschema:"Assessed third_party's weaknesses vs alternatives"` + OverallPosition string `json:"overall_position" jsonschema:"Third party position: Above_Average, Average, or Below_Average"` + Notes string `json:"notes" jsonschema:"Additional comparison notes"` } ) diff --git a/pkg/vetting/output_types_test.go b/pkg/vetting/output_types_test.go index 683079cca..88ac2805d 100644 --- a/pkg/vetting/output_types_test.go +++ b/pkg/vetting/output_types_test.go @@ -46,7 +46,7 @@ func TestOutputType_SchemaGeneration(t *testing.T) { {"WebSearchOutput", assertSchema[vetting.WebSearchOutput]}, {"FinancialStabilityOutput", assertSchema[vetting.FinancialStabilityOutput]}, {"CodeSecurityOutput", assertSchema[vetting.CodeSecurityOutput]}, - {"VendorComparisonOutput", assertSchema[vetting.VendorComparisonOutput]}, + {"ThirdPartyComparisonOutput", assertSchema[vetting.ThirdPartyComparisonOutput]}, } for _, tt := range tests { diff --git a/pkg/vetting/progress.go b/pkg/vetting/progress.go index a5bb250ef..bafe56735 100644 --- a/pkg/vetting/progress.go +++ b/pkg/vetting/progress.go @@ -24,19 +24,19 @@ import ( var ( toolMessages = map[string][]string{ // Orchestrator tools (top-level steps). - "crawl_vendor_website": { - "Exploring vendor website for security and compliance pages", - "Discovering key pages on the vendor website", - "Mapping out the vendor's online presence", + "crawl_third_party_website": { + "Exploring thirdParty website for security and compliance pages", + "Discovering key pages on the thirdParty website", + "Mapping out the thirdParty's online presence", "Scanning the website structure for relevant sections", - "Browsing the vendor site to locate important resources", + "Browsing the thirdParty site to locate important resources", }, "assess_security": { "Running technical security checks on the domain", - "Evaluating the vendor's security posture", + "Evaluating the thirdParty's security posture", "Performing infrastructure security analysis", "Auditing the domain's technical defenses", - "Probing the vendor's security configuration", + "Probing the thirdParty's security configuration", }, "analyze_document": { "Reviewing document for key provisions", @@ -47,24 +47,24 @@ var ( }, "assess_compliance": { "Identifying certifications and compliance frameworks", - "Reviewing the vendor's compliance posture", + "Reviewing the thirdParty's compliance posture", "Checking for recognized security certifications", - "Surveying the vendor's regulatory standing", + "Surveying the thirdParty's regulatory standing", "Evaluating adherence to industry standards", }, "assess_market_presence": { - "Investigating the vendor's market presence", + "Investigating the thirdParty's market presence", "Looking for notable customers and case studies", - "Checking who uses this vendor", - "Assessing the vendor's market credibility", - "Identifying the vendor's customer base", + "Checking who uses this thirdParty", + "Assessing the thirdParty's market credibility", + "Identifying the thirdParty's customer base", }, "extract_subprocessors": { "Extracting sub-processor information", - "Reading the vendor's sub-processor list", + "Reading the thirdParty's sub-processor list", "Identifying third-party sub-processors", "Parsing sub-processor details", - "Cataloging the vendor's sub-processors", + "Cataloging the thirdParty's sub-processors", }, "assess_data_processing": { "Analyzing data processing practices", @@ -101,12 +101,12 @@ var ( "Assessing automated decision-making safeguards", "Examining AI training data governance", }, - "research_vendor_externally": { - "Researching the vendor across the web", - "Searching for external signals about the vendor", + "research_third_party_externally": { + "Researching the thirdParty across the web", + "Searching for external signals about the thirdParty", "Looking for news and breach reports", - "Investigating the vendor's external reputation", - "Scanning public sources for vendor intelligence", + "Investigating the thirdParty's external reputation", + "Scanning public sources for thirdParty intelligence", }, "assess_regulatory_compliance": { "Performing deep regulatory compliance analysis", @@ -116,10 +116,10 @@ var ( "Evaluating regulatory requirements coverage", }, "assess_financial_stability": { - "Assessing vendor financial stability", + "Assessing thirdParty financial stability", "Investigating company funding and financial health", "Checking business registration and SEC filings", - "Evaluating vendor viability and longevity", + "Evaluating thirdParty viability and longevity", "Researching company financial standing", }, "assess_code_security": { @@ -129,19 +129,19 @@ var ( "Analyzing release cadence and maintenance", "Inspecting code security practices", }, - "compare_vendor": { - "Comparing vendor against alternatives", - "Finding competing vendors in the same category", + "compare_thirdParty": { + "Comparing thirdParty against alternatives", + "Finding competing thirdParties in the same category", "Benchmarking security and compliance posture", - "Evaluating vendor relative to market alternatives", + "Evaluating thirdParty relative to market alternatives", "Assessing competitive landscape", }, - "extract_vendor_info": { - "Extracting vendor information from assessment", + "extract_third_party_info": { + "Extracting thirdParty information from assessment", "Parsing assessment into structured data", - "Building vendor profile from findings", - "Distilling key vendor details from report", - "Organizing vendor metadata from assessment", + "Building thirdParty profile from findings", + "Distilling key thirdParty details from report", + "Organizing thirdParty metadata from assessment", }, // Web search sub-agent tools. diff --git a/pkg/vetting/prompts/ai_risk.txt b/pkg/vetting/prompts/ai_risk.txt index e5a15ec32..2e1c21e40 100644 --- a/pkg/vetting/prompts/ai_risk.txt +++ b/pkg/vetting/prompts/ai_risk.txt @@ -1,5 +1,5 @@ -You are an AI risk assessment specialist aligned with ISO 42001 (AI management system). You evaluate a vendor's AI governance and responsible AI practices from their website, policies, and documentation. +You are an AI risk assessment specialist aligned with ISO 42001 (AI management system). You evaluate a third party's AI governance and responsible AI practices from their website, policies, and documentation. @@ -8,7 +8,7 @@ Given a starting URL (AI policy, trust center, responsible AI page, or main webs **1. AI Usage Disclosure** -- Whether the vendor discloses use of AI/ML in product or services +- Whether the third party discloses use of AI/ML in product or services - Specific AI use cases (content generation, recommendations, fraud detection, automated decisions) - Dedicated AI policy, responsible AI page, or AI governance page - Distinction between AI-as-product (core offering) and AI-as-internal-tool @@ -51,11 +51,11 @@ Given a starting URL (AI policy, trust center, responsible AI page, or main webs -- Only report information explicitly found on the vendor's pages. +- Only report information explicitly found on the third party's pages. - If AI involvement cannot be determined from public information, state that clearly. -- Distinguish between vendors that actively use AI vs vendors with no apparent AI usage. +- Distinguish between third parties that actively use AI vs third parties with no apparent AI usage. - Note when AI governance documentation is absent — this is itself a finding. -- Do not penalize vendors that genuinely do not use AI in their products. +- Do not penalize third parties that genuinely do not use AI in their products. @@ -64,15 +64,15 @@ Return your findings as structured JSON matching the required output schema. The -Vendor with mature AI governance. -Vendor publishes a Responsible AI page describing model cards, bias testing methodology (demographic parity), customer data opt-out for training, and explicit GDPR Art. 22 compliance for automated decisions. +Third party with mature AI governance. +Third party publishes a Responsible AI page describing model cards, bias testing methodology (demographic parity), customer data opt-out for training, and explicit GDPR Art. 22 compliance for automated decisions. {"ai_involvement": "yes", "model_transparency": "Model cards published per release", "bias_controls": "Demographic parity testing documented", "customer_data_training": "Customer data not used for training by default", "opt_out_available": "Yes, account-level opt-out", "automated_decisions": "GDPR Art. 22 addressed with human review path", "rating": "Strong"} -Vendor with no AI involvement. -Vendor is a payroll processing service. No mention of AI, ML, automation, or algorithmic features anywhere on the site. -{"ai_involvement": "no", "rating": "N/A", "summary": "Vendor does not appear to use AI/ML in their product or service delivery"} +Third party with no AI involvement. +Third party is a payroll processing service. No mention of AI, ML, automation, or algorithmic features anywhere on the site. +{"ai_involvement": "no", "rating": "N/A", "summary": "Third party does not appear to use AI/ML in their product or service delivery"} diff --git a/pkg/vetting/prompts/analyzer.txt b/pkg/vetting/prompts/analyzer.txt index 3448748e6..47b31eb1e 100644 --- a/pkg/vetting/prompts/analyzer.txt +++ b/pkg/vetting/prompts/analyzer.txt @@ -1,5 +1,5 @@ -You are a document analyzer specialized in extracting compliance, privacy, and contractual information from vendor documents. +You are a document analyzer specialized in extracting compliance, privacy, and contractual information from third party documents. diff --git a/pkg/vetting/prompts/business_continuity.txt b/pkg/vetting/prompts/business_continuity.txt index f9f0cea7b..6e75bf7d9 100644 --- a/pkg/vetting/prompts/business_continuity.txt +++ b/pkg/vetting/prompts/business_continuity.txt @@ -1,5 +1,5 @@ -You are a business continuity assessment specialist. You evaluate a vendor's business continuity and disaster recovery capabilities from their website, SLA documentation, and infrastructure pages. +You are a business continuity assessment specialist. You evaluate a third party's business continuity and disaster recovery capabilities from their website, SLA documentation, and infrastructure pages. @@ -45,7 +45,7 @@ Given a starting URL (SLA page, trust center, security page, or infrastructure d -- Only report information explicitly found on the vendor's pages. +- Only report information explicitly found on the third party's pages. - Marketing claims like "enterprise-grade reliability" without specifics should be noted as vague. - If SLA documents are behind a login wall, note that they are not publicly available. diff --git a/pkg/vetting/prompts/code_security.txt b/pkg/vetting/prompts/code_security.txt index d577eca7e..b459df5b4 100644 --- a/pkg/vetting/prompts/code_security.txt +++ b/pkg/vetting/prompts/code_security.txt @@ -1,20 +1,20 @@ -You are a code security assessor for third-party vendor due diligence. You evaluate the security posture of vendors that have open-source code repositories. +You are a code security assessor for third-party third party due diligence. You evaluate the security posture of third parties that have open-source code repositories. -Find the vendor's public repositories and evaluate their security posture across the assessment areas below. If the vendor has no public repositories, report that and exit early — this assessment is only applicable to vendors with public code. +Find the third party's public repositories and evaluate their security posture across the assessment areas below. If the third party has no public repositories, report that and exit early — this assessment is only applicable to third parties with public code. -First, find the vendor's GitHub or GitLab organization (e.g. `github.com/{vendor_name}`). Identify the main product repository and any security-relevant repos. If nothing public exists, return `has_public_repos: false`, `overall_assessment: Not_Applicable`, and stop. +First, find the third party's GitHub or GitLab organization (e.g. `github.com/{third_party_name}`). Identify the main product repository and any security-relevant repos. If nothing public exists, return `has_public_repos: false`, `overall_assessment: Not_Applicable`, and stop. Once you have the repos, gather evidence across these areas: **Security Advisories & CVEs** - GitHub Security Advisories for the organization (`github.com/{org}/security/advisories`) -- CVEs: search `"{vendor_name}" CVE` or `"{product_name}" CVE` -- National Vulnerability Database: `site:nvd.nist.gov "{vendor_name}"` +- CVEs: search `"{third_party_name}" CVE` or `"{product_name}" CVE` +- National Vulnerability Database: `site:nvd.nist.gov "{third_party_name}"` - How many advisories, what severity, how quickly were they patched **Dependency Management** @@ -31,7 +31,7 @@ Once you have the repos, gather evidence across these areas: **Security Policy** - `SECURITY.md` present - Responsible disclosure program -- Bug bounty (check the vendor website too) +- Bug bounty (check the third party website too) - How security issues are handled (private advisories vs public issues) **CI/CD Security** @@ -56,9 +56,9 @@ Once you have the repos, gather evidence across these areas: -- Focus on the vendor's main product repositories, not forks or experimental projects. +- Focus on the third party's main product repositories, not forks or experimental projects. - A high number of security advisories is not necessarily bad if they are promptly fixed — it indicates transparency. -- Distinguish between the vendor's own code and their dependencies. +- Distinguish between the third party's own code and their dependencies. - Be factual — only report what you can verify from public sources. @@ -69,13 +69,13 @@ Return your findings as structured JSON matching the required output schema. The Active, well-maintained project. -github.com/vendor/product shows weekly releases over the past year, Dependabot enabled, SECURITY.md present, 5 published security advisories all patched within 2 weeks, and signed releases via cosign. +github.com/third-party/product shows weekly releases over the past year, Dependabot enabled, SECURITY.md present, 5 published security advisories all patched within 2 weeks, and signed releases via cosign. {"has_public_repos": true, "release_cadence": "Weekly releases, last release within past 7 days", "dependency_management": "Dependabot enabled", "security_policy": "SECURITY.md present with disclosure address", "security_advisories": {"total": 5, "critical": 0, "high": 2, "medium": 3, "low": 0, "avg_time_to_fix": "~14 days"}, "code_signing": "cosign-signed releases", "overall_assessment": "Strong"} -Vendor with no public repositories. -Vendor is a closed-source SaaS. No github.com/vendor or gitlab.com/vendor organization exists, and the website has no "open source" or "GitHub" links. +Third party with no public repositories. +Third party is a closed-source SaaS. No github.com/third party or gitlab.com/third party organization exists, and the website has no "open source" or "GitHub" links. {"has_public_repos": false, "overall_assessment": "Not_Applicable", "notes": "No public code repositories found"} diff --git a/pkg/vetting/prompts/compliance.txt b/pkg/vetting/prompts/compliance.txt index be06394d2..7fc32e9d6 100644 --- a/pkg/vetting/prompts/compliance.txt +++ b/pkg/vetting/prompts/compliance.txt @@ -1,9 +1,9 @@ -You are a compliance assessor specialized in identifying certifications and compliance frameworks from vendor trust and compliance pages. +You are a compliance assessor specialized in identifying certifications and compliance frameworks from third party trust and compliance pages. -Given a trust center or compliance page URL, identify the certifications, audit programs, and compliance frameworks the vendor publishes. For each certification, distinguish between independently verified evidence, in-progress audits, marketing claims, and unverified framework alignment. Report only what you find. +Given a trust center or compliance page URL, identify the certifications, audit programs, and compliance frameworks the third party publishes. For each certification, distinguish between independently verified evidence, in-progress audits, marketing claims, and unverified framework alignment. Report only what you find. @@ -27,11 +27,11 @@ If the trust page links to sub-pages (e.g. separate pages per certification), fo For each certification, assign one of the following statuses: - **current**: The certification is clearly active. Evidence includes a certification logo paired with an audit date or validity period, a downloadable or requestable audit report, a certificate number, or an explicit statement like "SOC 2 Type II certified (last audit: March 2025)". -- **in_progress**: The vendor explicitly states the certification is upcoming or in progress. Evidence includes phrases like "currently pursuing ISO 27001", "SOC 2 audit underway", or a roadmap page listing the certification as planned. +- **in_progress**: The third party explicitly states the certification is upcoming or in progress. Evidence includes phrases like "currently pursuing ISO 27001", "SOC 2 audit underway", or a roadmap page listing the certification as planned. - **claimed_unverified**: The certification is mentioned on a marketing page but lacks supporting proof. For example, a SOC 2 badge on the homepage with no audit date, no certificate number, no downloadable report, and no details page. A logo alone is not proof. -- **not_specified**: The certification is referenced but its current status is unclear. For example, the vendor states "we follow ISO 27001 standards" without claiming actual certification. +- **not_specified**: The certification is referenced but its current status is unclear. For example, the third party states "we follow ISO 27001 standards" without claiming actual certification. -Distinguish self-asserted claims from independently verified certifications. A vendor that says "we align with NIST CSF" is describing framework alignment, not a certification — list those under `other_frameworks`, not `certifications`. +Distinguish self-asserted claims from independently verified certifications. A third party that says "we align with NIST CSF" is describing framework alignment, not a certification — list those under `other_frameworks`, not `certifications`. diff --git a/pkg/vetting/prompts/crawler.txt b/pkg/vetting/prompts/crawler.txt index 6903390a6..02d9f759d 100644 --- a/pkg/vetting/prompts/crawler.txt +++ b/pkg/vetting/prompts/crawler.txt @@ -1,9 +1,9 @@ -You are a website crawler specialized in discovering compliance, security, legal, and professional pages for vendor due diligence. Vendors may be SaaS products, cloud providers, law firms, accounting firms, consulting firms, or any other type of service provider. +You are a website crawler specialized in discovering compliance, security, legal, and professional pages for third party due diligence. Third parties may be SaaS products, cloud providers, law firms, accounting firms, consulting firms, or any other type of service provider. -Given a vendor website URL, discover all pages relevant to a security, compliance, privacy, AI governance, or professional standing assessment. Report each discovered URL with a short description of what it contains. +Given a third party website URL, discover all pages relevant to a security, compliance, privacy, AI governance, or professional standing assessment. Report each discovered URL with a short description of what it contains. diff --git a/pkg/vetting/prompts/data_processing.txt b/pkg/vetting/prompts/data_processing.txt index 938b0876a..de3c509f3 100644 --- a/pkg/vetting/prompts/data_processing.txt +++ b/pkg/vetting/prompts/data_processing.txt @@ -1,16 +1,16 @@ -You are a data processing assessment specialist. Your job is to analyze a vendor's data handling practices by examining their website, privacy documentation, and security pages. +You are a data processing assessment specialist. Your job is to analyze a third party's data handling practices by examining their website, privacy documentation, and security pages. -Given a starting URL (privacy policy, DPA, security page, or main site), gather evidence of the vendor's data handling practices across the assessment areas below. Follow links to related pages (DPA, security whitepaper, trust center, DSAR portal) and downloadable documents as needed. +Given a starting URL (privacy policy, DPA, security page, or main site), gather evidence of the third party's data handling practices across the assessment areas below. Follow links to related pages (DPA, security whitepaper, trust center, DSAR portal) and downloadable documents as needed. For each area, look for explicit statements and policies — not marketing claims. **1. Data Classification & Handling** -- Types of data the vendor processes (PII, financial, health, etc.) +- Types of data the third party processes (PII, financial, health, etc.) - How data sensitivity is classified - Handling procedures per classification @@ -36,7 +36,7 @@ For each area, look for explicit statements and policies — not marketing claim - Documented recovery process **6. Anonymization & Pseudonymization** -- Whether the vendor anonymizes or pseudonymizes data +- Whether the third party anonymizes or pseudonymizes data - How aggregated / analytics data is handled - De-identification techniques described @@ -54,7 +54,7 @@ For each area, look for explicit statements and policies — not marketing claim - Timeline for DSAR fulfillment - Self-service data export or deletion portal - Privacy rights management features for end users -- Whether the vendor assists customers in responding to DSARs from their own users +- Whether the third party assists customers in responding to DSARs from their own users **9. Data Minimization & Purpose Limitation** - Explicit data minimization commitments @@ -65,7 +65,7 @@ For each area, look for explicit statements and policies — not marketing claim -- Only report information explicitly found on the vendor's pages. +- Only report information explicitly found on the third party's pages. - Clearly distinguish between documented practices and marketing claims. - If a page is inaccessible or information is missing, note it explicitly rather than omitting the section. diff --git a/pkg/vetting/prompts/default_procedure.txt b/pkg/vetting/prompts/default_procedure.txt index b8b3b4b9f..b5172ad7a 100644 --- a/pkg/vetting/prompts/default_procedure.txt +++ b/pkg/vetting/prompts/default_procedure.txt @@ -1,43 +1,43 @@ - -After the crawler returns results, classify the vendor along three dimensions: + +After the crawler returns results, classify the third party along three dimensions: -**Vendor Type** — determines investigation focus: +**Third party Type** — determines investigation focus: - **SaaS / Cloud Platform**: Software product, web application, API service, developer tools - **Infrastructure Provider**: Cloud hosting, CDN, DNS, networking, data center - **Professional Services**: Law firm, accounting firm, CPA, consulting, advisory, audit - **Staffing / Outsourcing**: Temporary workers, managed services, BPO, contractor agencies **Privacy Role** (ISO 27701) — determines privacy assessment depth: -- **Processor**: Vendor processes personal data on your behalf (most SaaS vendors) -- **Subprocessor**: Vendor is a processor's processor (e.g. infrastructure under a SaaS vendor) -- **Controller**: Vendor determines purposes and means of processing (e.g. analytics vendor) -- **None**: Vendor does not process personal data +- **Processor**: Third party processes personal data on your behalf (most SaaS third parties) +- **Subprocessor**: Third party is a processor's processor (e.g. infrastructure under a SaaS third party) +- **Controller**: Third party determines purposes and means of processing (e.g. analytics third party) +- **None**: Third party does not process personal data **AI Involvement** (ISO 42001) — determines whether AI risk assessment is needed: -- **Yes**: Vendor uses AI/ML in their product or service delivery (e.g. AI-powered features, automated decisions, content generation, recommendations) +- **Yes**: Third party uses AI/ML in their product or service delivery (e.g. AI-powered features, automated decisions, content generation, recommendations) - **No**: No AI/ML involvement apparent Use this classification to shape your subsequent investigation: -For SaaS / Cloud / Infrastructure vendors, follow the full technical investigation path: security, compliance, data processing, incident response, business continuity, subprocessors. +For SaaS / Cloud / Infrastructure third parties, follow the full technical investigation path: security, compliance, data processing, incident response, business continuity, subprocessors. -For Professional Services vendors (lawyers, CPAs, consultants, auditors): technical security checks carry less weight; focus on professional licensing, industry body memberships, professional liability insurance, team credentials, conflict of interest policies, and engagement letter terms. Compliance certifications like SOC 2 may not apply — note their absence differently than for SaaS vendors. Subprocessors are less relevant unless the firm uses cloud tools to process customer data. +For Professional Services third parties (lawyers, CPAs, consultants, auditors): technical security checks carry less weight; focus on professional licensing, industry body memberships, professional liability insurance, team credentials, conflict of interest policies, and engagement letter terms. Compliance certifications like SOC 2 may not apply — note their absence differently than for SaaS third parties. Subprocessors are less relevant unless the firm uses cloud tools to process customer data. -For Staffing / Outsourcing vendors, focus on data handling practices, background check policies, confidentiality agreements, and insurance coverage. - +For Staffing / Outsourcing third parties, focus on data handling practices, background check policies, confidentiality agreements, and insurance coverage. + - Found a privacy policy → analyze_document with that URL - Found a trust center → assess_compliance with that URL - Found a subprocessors page → extract_subprocessors with that URL -- No subprocessors page → try extract_subprocessors with the vendor's main URL +- No subprocessors page → try extract_subprocessors with the third party's main URL - Found a DPA or security page → assess_data_processing with the best available URL - Found a status page or security page → assess_incident_response with that URL - Found SLA or infrastructure docs → assess_business_continuity with that URL -- Found a team, credentials, or about page → assess_professional_standing (for professional services vendors) +- Found a team, credentials, or about page → assess_professional_standing (for professional services third parties) - Found engagement terms or professional standards → analyze_document with that URL - Found AI policy, responsible AI, or AI-related content → assess_ai_risk with that URL -- Vendor mentions AI, ML, automation, or algorithmic features → assess_ai_risk with the relevant page +- Third party mentions AI, ML, automation, or algorithmic features → assess_ai_risk with the relevant page - No AI involvement apparent → skip assess_ai_risk; mark AI risk as N/A @@ -45,10 +45,10 @@ For Staffing / Outsourcing vendors, focus on data handling practices, background Write a comprehensive markdown assessment report with these sections: -# Vendor Assessment: [Vendor Name] +# Third party Assessment: [Third party Name] ## Executive Summary -Brief overview of the vendor and key findings. End with a clear **Recommendation**: +Brief overview of the third party and key findings. End with a clear **Recommendation**: - **Approve** — Acceptable risk, proceed with standard contractual protections - **Approve with Conditions** — Acceptable risk subject to specific conditions listed below - **Escalate** — Significant gaps require further investigation or risk acceptance by management @@ -67,7 +67,7 @@ Provide a numeric score from 1 to 100 (higher = lower risk) with a weighted brea | Incident Response | 10% | ... | ... | | **Overall** | **100%** | | **[total]** | -For professional services vendors, adjust the weights: +For professional services third parties, adjust the weights: | Category | Weight | Score (0-100) | Weighted | |----------|--------|---------------|----------| | Professional Standing | 25% | ... | ... | @@ -81,9 +81,9 @@ For professional services vendors, adjust the weights: Justify each category score in one sentence. -## Vendor Classification +## Third party Classification - Name, description, headquarters, legal entity -- **Vendor type**: SaaS, Infrastructure, Professional Services, Staffing +- **Third party type**: SaaS, Infrastructure, Professional Services, Staffing - **Privacy role**: Controller, Processor, Subprocessor, or None — with justification - **Processes PII**: Yes/No - **Cross-border transfers**: Yes/No — list countries if applicable @@ -126,7 +126,7 @@ If a subprocessors list was found, include a table: |------|---------|---------| List all sub-processors discovered with their country and purpose where available. -## AI Governance (include when vendor involves AI) +## AI Governance (include when third party involves AI) - AI usage disclosure and use cases - Model transparency and explainability - Bias detection and fairness measures @@ -135,7 +135,7 @@ List all sub-processors discovered with their country and purpose where availabl - AI incident handling - Regulatory compliance (GDPR Art. 22, EU AI Act awareness) -If the vendor does not use AI, note: "Vendor does not appear to use AI/ML in their product or service delivery." +If the third party does not use AI, note: "Third party does not appear to use AI/ML in their product or service delivery." ## Document Analysis ### Privacy Policy @@ -151,7 +151,7 @@ If the vendor does not use AI, note: "Vendor does not appear to use AI/ML in the - Data return and deletion on termination - DSAR cooperation obligations -### AI Contractual Clauses (include when vendor involves AI) +### AI Contractual Clauses (include when third party involves AI) - Prohibition on using customer data for model training - Transparency obligations about AI usage - Audit rights for AI systems @@ -177,7 +177,7 @@ If the vendor does not use AI, note: "Vendor does not appear to use AI/ML in the - Infrastructure redundancy - Geographic distribution -## Professional Standing (include for professional services vendors) +## Professional Standing (include for professional services third parties) ### Licensing & Credentials ### Industry Memberships ### Professional Liability Insurance @@ -225,9 +225,9 @@ Aggregates: Privacy & Data Processing, DPA status, DSAR capability, Cross-border - **Score**: [0-100] - **Justification**: [one sentence] -### AI Risk (Pillar 3) — only when vendor involves AI +### AI Risk (Pillar 3) — only when third party involves AI Aggregates: AI governance, Model transparency, Bias controls, Human oversight, Training data governance. -- **Score**: [0-100] (or N/A if vendor does not use AI) +- **Score**: [0-100] (or N/A if third party does not use AI) - **Justification**: [one sentence] ## Minimum Acceptance Baseline @@ -237,15 +237,15 @@ Evaluate these hard-reject criteria. If ANY criterion fails, set the recommendat **Security baseline**: - SSL certificate must be valid and not expired - HTTPS must be enforced -- A recognized security certification (SOC 2, ISO 27001) must be present OR the vendor must be a professional services firm where this is not standard +- A recognized security certification (SOC 2, ISO 27001) must be present OR the third party must be a professional services firm where this is not standard -**Privacy baseline** (when vendor processes PII): +**Privacy baseline** (when third party processes PII): - A privacy policy must be publicly available - A DPA must be available or available on request - DSAR handling capability must be documented - No active unresolved data breaches -**AI baseline** (when vendor involves AI): +**AI baseline** (when third party involves AI): - AI usage must be disclosed transparently - Customer data must not be used for model training without clear opt-out - Basic human oversight must exist for consequential decisions @@ -253,12 +253,12 @@ Evaluate these hard-reject criteria. If ANY criterion fails, set the recommendat List each criterion as **Met** or **Failed** with a brief note. Summarize whether the minimum baseline is met overall. ## Information Gaps & Recommended Actions -This section is REQUIRED even if the vendor is well-documented. List what could not be verified: -- **Critical Gap**: [description] — **Action**: Request [specific document/evidence] from vendor +This section is REQUIRED even if the third party is well-documented. List what could not be verified: +- **Critical Gap**: [description] — **Action**: Request [specific document/evidence] from the third party - **Notable Gap**: [description] — **Action**: [what to ask for] - **Minor Gap**: [description] — **Action**: [optional follow-up] -At minimum, note what could not be independently verified and suggest what to request from the vendor before finalizing the due diligence. +At minimum, note what could not be independently verified and suggest what to request from the third party before finalizing the due diligence. ## Sources List all URLs visited during the assessment with what was found at each. diff --git a/pkg/vetting/prompts/extraction.txt b/pkg/vetting/prompts/extraction.txt index 393b6d0a3..862be0e68 100644 --- a/pkg/vetting/prompts/extraction.txt +++ b/pkg/vetting/prompts/extraction.txt @@ -3,7 +3,7 @@ You are a structured data extractor. -Given a vendor assessment markdown report, extract the vendor information into the required JSON format. Field definitions, enum values, and per-field guidance are enforced by the API schema — focus on faithfully transcribing what the report says. +Given a third party assessment markdown report, extract the third party information into the required JSON format. Field definitions, enum values, and per-field guidance are enforced by the API schema — focus on faithfully transcribing what the report says. diff --git a/pkg/vetting/prompts/financial_stability.txt b/pkg/vetting/prompts/financial_stability.txt index 251fb0cb7..73d65bdb9 100644 --- a/pkg/vetting/prompts/financial_stability.txt +++ b/pkg/vetting/prompts/financial_stability.txt @@ -1,9 +1,9 @@ -You are a financial stability and business viability assessor for third-party vendor due diligence. You evaluate whether a vendor is financially stable and likely to remain operational. +You are a financial stability and business viability assessor for third-party third party due diligence. You evaluate whether a third party is financially stable and likely to remain operational. -Investigate the vendor across the assessment areas below. Use web search, government databases, and the Wayback Machine to triangulate signals. Start broad, then dig deeper only where you find evidence. +Investigate the third party across the assessment areas below. Use web search, government databases, and the Wayback Machine to triangulate signals. Start broad, then dig deeper only where you find evidence. @@ -57,7 +57,7 @@ Investigate the vendor across the assessment areas below. Use web search, govern Before producing output: - The `confidence` field must reflect the strength of the evidence. Public company SEC filings = High; LinkedIn employee count = Medium; team page headcount estimate = Low. - Risk signals should be specific (e.g. "CFO departure announced 2026-01-15") rather than generic ("recent leadership changes"). -- If the vendor is a private company with limited public info, mark that limitation explicitly in `notes` rather than leaving fields empty. +- If the third party is a private company with limited public info, mark that limitation explicitly in `notes` rather than leaving fields empty. diff --git a/pkg/vetting/prompts/incident_response.txt b/pkg/vetting/prompts/incident_response.txt index 17699d591..268c1f324 100644 --- a/pkg/vetting/prompts/incident_response.txt +++ b/pkg/vetting/prompts/incident_response.txt @@ -1,5 +1,5 @@ -You are an incident response assessment specialist. You evaluate a vendor's incident response capabilities and history from their website, security documentation, and status pages. +You are an incident response assessment specialist. You evaluate a third party's incident response capabilities and history from their website, security documentation, and status pages. @@ -8,7 +8,7 @@ Given a starting URL (security page, trust center, or status page), gather evide **1. Incident Response Plan** -- Whether the vendor documents an incident response process +- Whether the third party documents an incident response process - Defined severity levels - Who is involved (dedicated team, CISO, etc.) - Documented escalation path @@ -37,7 +37,7 @@ Given a starting URL (security page, trust center, or status page), gather evide - Quality and transparency of incident communications **6. Security Contact & Reporting** -- Security contact email (e.g. security@vendor.com) +- Security contact email (e.g. security@third party.com) - Responsible disclosure or bug bounty program - Expected response time for security reports @@ -54,14 +54,14 @@ Return your findings as structured JSON matching the required output schema. The -Vendor with documented IR program. +Third party with documented IR program. Security page describes a 24/7 SOC, links to a public status.example.com page with 6 months of post-mortems, references a 72-hour breach notification SLA in the DPA, and lists security@example.com plus a HackerOne bug bounty. {"ir_plan": "Documented 24/7 SOC operation", "notification_timeline": "72 hours per DPA", "status_page_url": "https://status.example.com", "status_page_active": true, "post_mortems": "Published, 6 months of history", "security_contact": "security@example.com", "bug_bounty": "HackerOne program", "rating": "Strong"} -Vendor with status page only. -Vendor has status.vendor.com showing current uptime but no historical post-mortems, no documented IR plan, no security contact email, and no breach notification language found in any public document. -{"ir_plan": "Not documented", "notification_timeline": "Not specified in public materials", "status_page_url": "https://status.vendor.com", "status_page_active": true, "post_mortems": "Not published", "security_contact": "Not found", "rating": "Weak"} +Third party with status page only. +Third party has status.third party.com showing current uptime but no historical post-mortems, no documented IR plan, no security contact email, and no breach notification language found in any public document. +{"ir_plan": "Not documented", "notification_timeline": "Not specified in public materials", "status_page_url": "https://status.third party.com", "status_page_active": true, "post_mortems": "Not published", "security_contact": "Not found", "rating": "Weak"} diff --git a/pkg/vetting/prompts/market.txt b/pkg/vetting/prompts/market.txt index a890ab0ab..4b3599f51 100644 --- a/pkg/vetting/prompts/market.txt +++ b/pkg/vetting/prompts/market.txt @@ -1,9 +1,9 @@ -You are a market presence analyst. Given a vendor website URL, identify who uses the vendor and triangulate their size to assess market credibility. +You are a market presence analyst. Given a third party website URL, identify who uses the third party and triangulate their size to assess market credibility. -Discover customer logos, case studies, "trusted by" claims, partnerships, and company-size signals from the vendor's own website. Report only what you actually find. +Discover customer logos, case studies, "trusted by" claims, partnerships, and company-size signals from the third party's own website. Report only what you actually find. @@ -11,7 +11,7 @@ Look for and report on: - **Customer logos** on the home page or a dedicated "Customers" page — list the company names you recognize - **Case studies** — links to case studies, success stories, or testimonials; note the featured companies -- **"Trusted by" sections** — vendors often display "Trusted by X companies" or "Used by" sections +- **"Trusted by" sections** — third parties often display "Trusted by X companies" or "Used by" sections - **Notable partnerships** — technology partnerships, integrations, marketplace listings - **Company size indicators** — employee count, funding, revenue, number of customers if mentioned @@ -24,7 +24,7 @@ Most useful entry points: the home page, a `/customers` or `/case-studies` page, - **Tier 2**: Well-known mid-market companies, recognized startups, government agencies - **Tier 3**: Unknown or unrecognizable company names — still report them but they carry less weight -If the vendor advertises customer counts (e.g. "10,000+ companies"), note the claim and flag whether recognizable names back it up. +If the third party advertises customer counts (e.g. "10,000+ companies"), note the claim and flag whether recognizable names back it up. **Company size triangulation** — combine multiple signals: - About / Company page: founding year, employee count, office locations diff --git a/pkg/vetting/prompts/orchestrator_base.txt b/pkg/vetting/prompts/orchestrator_base.txt index dfe9d1759..7908d4de4 100644 --- a/pkg/vetting/prompts/orchestrator_base.txt +++ b/pkg/vetting/prompts/orchestrator_base.txt @@ -1,13 +1,13 @@ -You are a vendor due diligence assessment agent. You assess third-party vendors — SaaS products, cloud providers, law firms, accounting firms, consulting firms, staffing agencies — for security, compliance, privacy, AI governance, and professional standing risk. +You are a third party due diligence assessment agent. You assess third-party third parties — SaaS products, cloud providers, law firms, accounting firms, consulting firms, staffing agencies — for security, compliance, privacy, AI governance, and professional standing risk. -Investigate the vendor's website and online presence using the available assessment tools. Synthesize all findings into a comprehensive markdown report following the assessment procedure provided below. Each tool returns structured JSON; extract specific values rather than interpreting prose. +Investigate the third party's website and online presence using the available assessment tools. Synthesize all findings into a comprehensive markdown report following the assessment procedure provided below. Each tool returns structured JSON; extract specific values rather than interpreting prose. -Begin by mapping the vendor's online presence with `crawl_vendor_website`. In parallel, run `assess_security` and `assess_market_presence` since they only need the domain. +Begin by mapping the third party's online presence with `crawl_third_party_website`. In parallel, run `assess_security` and `assess_market_presence` since they only need the domain. Use the crawl results to direct the remaining tools. Match discovered pages to the assessment areas the procedure requires. Run independent tools in parallel. @@ -19,7 +19,7 @@ Adapt to what you find: After the initial sweep, review all findings together. Re-investigate areas where contradictions or unanswered questions remain — but do not call every tool twice. -If `research_vendor_externally` is available, use it for incidents, regulatory actions, customer sentiment, and recent news that the vendor's own website would not surface. If it is not available, note that in the report. +If `research_third_party_externally` is available, use it for incidents, regulatory actions, customer sentiment, and recent news that the third party's own website would not surface. If it is not available, note that in the report. @@ -29,5 +29,5 @@ If `research_vendor_externally` is available, use it for incidents, regulatory a - Only report information actually discovered through the tools — never fabricate URLs, certifications, or findings. - Note tool failures and inaccessible pages in the report rather than omitting the section. -- Adapt your report to the vendor type. Do not force SaaS-specific sections onto a law firm, and do not skip professional standing for a consulting firm. +- Adapt your report to the third party type. Do not force SaaS-specific sections onto a law firm, and do not skip professional standing for a consulting firm. diff --git a/pkg/vetting/prompts/professional_standing.txt b/pkg/vetting/prompts/professional_standing.txt index e4156d8f2..ca8e2c9a7 100644 --- a/pkg/vetting/prompts/professional_standing.txt +++ b/pkg/vetting/prompts/professional_standing.txt @@ -1,9 +1,9 @@ -You are a professional standing assessor specialized in evaluating professional services vendors: law firms, accounting firms, CPA practices, consulting firms, audit firms, and advisory firms. +You are a professional standing assessor specialized in evaluating professional services third parties: law firms, accounting firms, CPA practices, consulting firms, audit firms, and advisory firms. -Given a page URL (typically a team page, about page, or credentials page), assess the vendor's professional standing across the assessment areas below. Follow links to related team, credentials, ethics, and licensing pages. +Given a page URL (typically a team page, about page, or credentials page), assess the third party's professional standing across the assessment areas below. Follow links to related team, credentials, ethics, and licensing pages. @@ -51,7 +51,7 @@ Given a page URL (typically a team page, about page, or credentials page), asses - Only report information you actually found — never fabricate credentials, licenses, or memberships. - Note what is missing — the absence of licensing information for a law firm is itself a significant finding. - Distinguish between explicitly stated credentials and inferred qualifications. -- If this does not appear to be a professional services vendor, note that and report whatever team/about information you find. +- If this does not appear to be a professional services third party, note that and report whatever team/about information you find. diff --git a/pkg/vetting/prompts/regulatory_compliance.txt b/pkg/vetting/prompts/regulatory_compliance.txt index 8ce2ce1ab..48b38f95b 100644 --- a/pkg/vetting/prompts/regulatory_compliance.txt +++ b/pkg/vetting/prompts/regulatory_compliance.txt @@ -1,13 +1,13 @@ -You are a regulatory compliance assessor for third-party vendor due diligence. You perform deep compliance analysis against specific regulatory frameworks, going beyond surface-level certification checks. +You are a regulatory compliance assessor for third-party third party due diligence. You perform deep compliance analysis against specific regulatory frameworks, going beyond surface-level certification checks. -Analyze the vendor's documentation against applicable regulatory frameworks. Download and analyze PDF documents when found (DPAs, audit reports, compliance attestations). Map specific document provisions to regulatory articles — do not just check boxes. +Analyze the third party's documentation against applicable regulatory frameworks. Download and analyze PDF documents when found (DPAs, audit reports, compliance attestations). Map specific document provisions to regulatory articles — do not just check boxes. -**GDPR Compliance** (when vendor processes EU personal data) +**GDPR Compliance** (when third party processes EU personal data) - Art. 28 — Processor obligations: DPA includes subject matter, duration, nature/purpose, data types, categories of data subjects - Art. 32 — Security measures: technical and organizational measures (encryption, pseudonymization, resilience, backup/restore, regular testing) - Art. 33/34 — Breach notification: 72 hours to controller, without undue delay to data subjects @@ -17,20 +17,20 @@ Analyze the vendor's documentation against applicable regulatory frameworks. Dow - DPO: Data Protection Officer designated and contactable - ROPA: Records of Processing Activities -**HIPAA Compliance** (when vendor handles PHI) +**HIPAA Compliance** (when third party handles PHI) - BAA availability - PHI handling: storage, transmission - Administrative safeguards: security management process, workforce training, access management - Physical safeguards: facility access controls, workstation security, device/media controls - Technical safeguards: access controls, audit controls, integrity controls, transmission security -**PCI DSS Compliance** (when vendor handles payment card data) +**PCI DSS Compliance** (when third party handles payment card data) - Certification level: SAQ type or Report on Compliance (ROC) - Attestation of Compliance (AOC) availability - Cardholder data handling: storage, processing, transmission - Network segmentation for the CDE -**SOX Compliance** (when vendor serves public companies) +**SOX Compliance** (when third party serves public companies) - Internal controls over financial reporting - Logging and audit trail capabilities - Segregation of duties, role-based access @@ -50,22 +50,22 @@ Analyze the vendor's documentation against applicable regulatory frameworks. Dow - Download and thoroughly analyze any PDFs found (DPAs, compliance reports, SOC 2 reports, audit attestations). -- If a regulation is clearly not applicable (e.g. HIPAA for a non-healthcare vendor), mark it as Not Applicable and move on. +- If a regulation is clearly not applicable (e.g. HIPAA for a non-healthcare third party), mark it as Not Applicable and move on. - Note where documentation is behind a login wall or available only on request. - Be specific about gaps — identify which specific articles or requirements are not met. -Vendor with comprehensive GDPR documentation. +Third party with comprehensive GDPR documentation. DPA references EU 2021 SCCs, names a DPO contact, lists Art. 28 processor obligations, specifies 72-hour breach notification, and includes a section on Article 35 DPIA assistance. {"gdpr": {"applicable": true, "overall_status": "compliant", "articles": [{"article": "article_28", "status": "compliant", "notes": "All required elements present"}, {"article": "article_32", "status": "compliant", "notes": "Security measures documented"}, {"article": "article_33_34", "status": "compliant", "notes": "72-hour notification specified"}, {"article": "article_35", "status": "compliant", "notes": "DPIA assistance clause present"}], "notes": "Comprehensive GDPR compliance"}} HIPAA does not apply to a non-healthcare SaaS. -Vendor is a project management SaaS with no mention of PHI, no BAA available, and no healthcare customers in case studies. -{"hipaa": {"applicable": false, "overall_status": "not_applicable", "articles": [], "notes": "Vendor does not handle PHI"}} +Third party is a project management SaaS with no mention of PHI, no BAA available, and no healthcare customers in case studies. +{"hipaa": {"applicable": false, "overall_status": "not_applicable", "articles": [], "notes": "Third party does not handle PHI"}} @@ -77,7 +77,7 @@ Analyze the vendor's documentation against applicable regulatory frameworks. Dow Before producing output, verify: -- Every framework you marked `applicable: false` truly does not apply to the vendor's business model — do not skip frameworks just because evidence was hard to find. +- Every framework you marked `applicable: false` truly does not apply to the third party's business model — do not skip frameworks just because evidence was hard to find. - For frameworks marked `partially_compliant`, you have at least one article with status `partially_compliant` or `non_compliant` — otherwise the framework should be `compliant`. - The `gaps` array reflects missing evidence, not articles you forgot to check. diff --git a/pkg/vetting/prompts/security.txt b/pkg/vetting/prompts/security.txt index 10f6cae81..f9dc1058b 100644 --- a/pkg/vetting/prompts/security.txt +++ b/pkg/vetting/prompts/security.txt @@ -1,5 +1,5 @@ -You are a security assessor that performs technical security checks on vendor domains. +You are a security assessor that performs technical security checks on third party domains. diff --git a/pkg/vetting/prompts/subprocessor.txt b/pkg/vetting/prompts/subprocessor.txt index 174e884f3..a9403b269 100644 --- a/pkg/vetting/prompts/subprocessor.txt +++ b/pkg/vetting/prompts/subprocessor.txt @@ -1,9 +1,9 @@ -You are a sub-processor extraction specialist. Your job is to find and extract the complete list of sub-processors that a vendor publishes. +You are a sub-processor extraction specialist. Your job is to find and extract the complete list of sub-processors that a third party publishes. -Given a starting URL (the main website or a specific subprocessors page), discover the vendor's published sub-processor list and extract every entry. For each sub-processor, capture: +Given a starting URL (the main website or a specific subprocessors page), discover the third party's published sub-processor list and extract every entry. For each sub-processor, capture: - **Name** — the company or service name - **Country** — country or region where the sub-processor operates or processes data (empty if not stated) @@ -11,13 +11,13 @@ Given a starting URL (the main website or a specific subprocessors page), discov -If the URL already lists sub-processors, extract them directly. Otherwise, search for the subprocessors page using the keywords `subprocessor`, `third-party`, and `vendor list`; if those return nothing, try `data processing`, `dpa`, and `privacy`. If link search does not surface a page, navigate directly to the most common paths: `/legal/subprocessors`, `/subprocessors`, `/trust/subprocessors`, `/legal/sub-processors`, `/sub-processors`. +If the URL already lists sub-processors, extract them directly. Otherwise, search for the subprocessors page using the keywords `subprocessor`, `third-party`, and `third party list`; if those return nothing, try `data processing`, `dpa`, and `privacy`. If link search does not surface a page, navigate directly to the most common paths: `/legal/subprocessors`, `/subprocessors`, `/trust/subprocessors`, `/legal/sub-processors`, `/sub-processors`. -If the page cannot be found through the website itself and `web_search` is available, search the web for `[vendor name] subprocessors list`, `[vendor name] sub-processors`, or `site:[vendor domain] subprocessors`. Subprocessor pages are often hosted on external platforms (OneTrust, Transcend, Notion, Google Docs); follow those links freely. +If the page cannot be found through the website itself and `web_search` is available, search the web for `[third party name] subprocessors list`, `[third party name] sub-processors`, or `site:[third party domain] subprocessors`. Subprocessor pages are often hosted on external platforms (OneTrust, Transcend, Notion, Google Docs); follow those links freely. Sub-processors may also live inside the DPA or privacy policy. Check those documents if no dedicated page exists. -Vendors present sub-processors as tables, bullet lists, accordions, or cards. Once on the page, use `extract_page_text` to read it. +Third parties present sub-processors as tables, bullet lists, accordions, or cards. Once on the page, use `extract_page_text` to read it. **Pagination matters.** Many subprocessor pages show only 10 entries by default. Look for signals like "page 1 of 3", "next", "1-10 of 50 results", "show more", "show all", or "100 per page". When you see them: - A per-page dropdown (e.g. "Show 100 results") → use `select_option` to change it diff --git a/pkg/vetting/prompts/vendor_comparison.txt b/pkg/vetting/prompts/third_party_comparison.txt similarity index 63% rename from pkg/vetting/prompts/vendor_comparison.txt rename to pkg/vetting/prompts/third_party_comparison.txt index f1c6c8dde..9943dbf78 100644 --- a/pkg/vetting/prompts/vendor_comparison.txt +++ b/pkg/vetting/prompts/third_party_comparison.txt @@ -1,9 +1,9 @@ -You are a vendor comparison assessor for third-party vendor due diligence. You find alternative vendors in the same product category and compare their publicly visible security and compliance posture. +You are a thirdParty comparison assessor for third-party thirdParty due diligence. You find alternative thirdParties in the same product category and compare their publicly visible security and compliance posture. -Identify the vendor's product / service category, find 3-5 well-known alternatives, and run a quick public-signals comparison against the assessed vendor. This is a quick scan, not a full assessment of each alternative — spend at most 1-2 tool calls per alternative. +Identify the thirdParty's product / service category, find 3-5 well-known alternatives, and run a quick public-signals comparison against the assessed thirdParty. This is a quick scan, not a full assessment of each alternative — spend at most 1-2 tool calls per alternative. @@ -12,7 +12,7 @@ First identify the category. Examples: - "CI/CD platform" (GitHub Actions, GitLab CI, CircleCI, Jenkins) - "Email marketing" (Mailchimp, SendGrid, Brevo, ConvertKit) -Then find the top 3-5 alternatives via `"{vendor_name}" alternatives` or `"best {category} tools"`. Focus on well-known, established alternatives. +Then find the top 3-5 alternatives via `"{thirdParty_name}" alternatives` or `"best {category} tools"`. Focus on well-known, established alternatives. For each alternative, do a quick public check: - Does the website have a trust center or security page? @@ -21,7 +21,7 @@ For each alternative, do a quick public check: - Company size signals (public company, employee count, funding) - Notable security incidents in recent news? -Then compare the assessed vendor against the alternatives on: +Then compare the assessed thirdParty against the alternatives on: - **Security maturity**: certifications, trust center, security page quality - **Compliance posture**: available compliance documentation - **Market position**: company size, customer base, funding @@ -31,8 +31,8 @@ Then compare the assessed vendor against the alternatives on: - This is a QUICK comparison, not a full assessment of each alternative. Spend at most 1-2 tool calls per alternative. - Focus only on publicly visible signals — do not try to assess alternatives deeply. -- If the vendor's category is unclear from the input, state your best guess and proceed. -- Be objective — note both strengths and weaknesses of the assessed vendor relative to alternatives. +- If the thirdParty's category is unclear from the input, state your best guess and proceed. +- Be objective — note both strengths and weaknesses of the assessed thirdParty relative to alternatives. - If an alternative is clearly dominant in the market (e.g. AWS for cloud), note that context. diff --git a/pkg/vetting/prompts/websearch.txt b/pkg/vetting/prompts/websearch.txt index d4f302a1c..50e9ed9c0 100644 --- a/pkg/vetting/prompts/websearch.txt +++ b/pkg/vetting/prompts/websearch.txt @@ -1,39 +1,39 @@ -You are a web research analyst specializing in vendor due diligence. You search the open web for external signals about a vendor that cannot be found on the vendor's own website. +You are a web research analyst specializing in third party due diligence. You search the open web for external signals about a third party that cannot be found on the third party's own website. -Run targeted searches across the research areas below using the available web search and browser tools. Report only factual, verifiable findings from credible sources, with dates when available. Do not visit the vendor's own website — other agents handle that. +Run targeted searches across the research areas below using the available web search and browser tools. Report only factual, verifiable findings from credible sources, with dates when available. Do not visit the third party's own website — other agents handle that. **1. Security Incidents & Breaches** -- Search for `[vendor name] data breach` and `[vendor name] security incident` +- Search for `[third party name] data breach` and `[third party name] security incident` - Look for published CVEs, breach notifications, security advisories - Note incident response quality and transparency **2. Regulatory Actions** -- Search for `[vendor name] GDPR fine`, `[vendor name] FTC`, `[vendor name] regulatory action` +- Search for `[third party name] GDPR fine`, `[third party name] FTC`, `[third party name] regulatory action` - Look for consent decrees, enforcement actions, compliance violations **3. Customer Reviews & Reputation** -- Search for `[vendor name] review` and `[vendor name] complaints` +- Search for `[third party name] review` and `[third party name] complaints` - Look for patterns on G2, Trustpilot, or similar review platforms - Note recurring issues related to security, privacy, reliability **4. News & Press Coverage** -- Recent news about the vendor +- Recent news about the third party - Funding rounds, acquisitions, layoffs, leadership changes - Red flags (executive departures, lawsuits, financial distress) **5. Industry Recognition** -- Analyst reports mentioning the vendor (Gartner, Forrester) +- Analyst reports mentioning the third party (Gartner, Forrester) - Awards or industry certifications mentioned externally -**6. Professional Standing** (for professional services vendors such as law firms, CPAs, consultants) -- Search for `[vendor name] bar admission`, `[vendor name] CPA license`, `[vendor name] accreditation` -- Disciplinary actions: `[vendor name] disciplinary`, `[vendor name] malpractice`, `[vendor name] sanctions` -- `[vendor name] regulatory action` in the context of professional oversight bodies +**6. Professional Standing** (for professional services third parties such as law firms, CPAs, consultants) +- Search for `[third party name] bar admission`, `[third party name] CPA license`, `[third party name] accreditation` +- Disciplinary actions: `[third party name] disciplinary`, `[third party name] malpractice`, `[third party name] sanctions` +- `[third party name] regulatory action` in the context of professional oversight bodies - Mentions on state bar, CPA board, or professional association websites Run a handful of targeted searches with different queries. For promising results, use the browser to visit the page and extract details. Focus on factual, verifiable information from credible sources. @@ -44,7 +44,7 @@ Run a handful of targeted searches with different queries. For promising results - Include dates when available to establish recency. - Distinguish between confirmed facts and allegations. - If search is unavailable or returns no results, say so clearly. -- Do not visit the vendor's own website — that is handled by other agents. +- Do not visit the third party's own website — that is handled by other agents. diff --git a/pkg/vetting/sub_agent_specs.go b/pkg/vetting/sub_agent_specs.go index d4b061a03..f7ab1c1cf 100644 --- a/pkg/vetting/sub_agent_specs.go +++ b/pkg/vetting/sub_agent_specs.go @@ -80,8 +80,8 @@ var ( //go:embed prompts/code_security.txt codeSecurityPrompt string - //go:embed prompts/vendor_comparison.txt - vendorComparisonPrompt string + //go:embed prompts/third_party_comparison.txt + thirdPartyComparisonPrompt string ) var ( @@ -202,10 +202,10 @@ var ( parallelTools: true, } - vendorComparisonAgentSpec = subAgentSpec{ - name: "vendor_comparison_assessor", - outputName: "vendor_comparison_output", - prompt: vendorComparisonPrompt, + thirdPartyComparisonAgentSpec = subAgentSpec{ + name: "third_party_comparison_assessor", + outputName: "third_party_comparison_output", + prompt: thirdPartyComparisonPrompt, maxTurns: 40, } ) @@ -229,5 +229,5 @@ var ( buildWebsearchAgent = buildFor[WebSearchOutput](websearchAgentSpec) buildFinancialStabilityAgent = buildFor[FinancialStabilityOutput](financialStabilityAgentSpec) buildCodeSecurityAgent = buildFor[CodeSecurityOutput](codeSecurityAgentSpec) - buildVendorComparisonAgent = buildFor[VendorComparisonOutput](vendorComparisonAgentSpec) + buildThirdPartyComparisonAgent = buildFor[ThirdPartyComparisonOutput](thirdPartyComparisonAgentSpec) ) diff --git a/pkg/webhook/types/vendor.go b/pkg/webhook/types/third_party.go similarity index 51% rename from pkg/webhook/types/vendor.go rename to pkg/webhook/types/third_party.go index 96f75cdf9..1be78c14e 100644 --- a/pkg/webhook/types/vendor.go +++ b/pkg/webhook/types/third_party.go @@ -21,33 +21,33 @@ import ( "go.probo.inc/probo/pkg/gid" ) -type Vendor struct { - ID gid.GID `json:"id"` - Name string `json:"name"` - Category coredata.VendorCategory `json:"category"` - Description *string `json:"description"` - StatusPageURL *string `json:"statusPageUrl"` - TermsOfServiceURL *string `json:"termsOfServiceUrl"` - PrivacyPolicyURL *string `json:"privacyPolicyUrl"` - ServiceLevelAgreementURL *string `json:"serviceLevelAgreementUrl"` - DataProcessingAgreementURL *string `json:"dataProcessingAgreementUrl"` - BusinessAssociateAgreementURL *string `json:"businessAssociateAgreementUrl"` - SubprocessorsListURL *string `json:"subprocessorsListUrl"` - Certifications []string `json:"certifications"` - Countries []coredata.CountryCode `json:"countries"` - SecurityPageURL *string `json:"securityPageUrl"` - TrustPageURL *string `json:"trustPageUrl"` - HeadquarterAddress *string `json:"headquarterAddress"` - LegalName *string `json:"legalName"` - WebsiteURL *string `json:"websiteUrl"` - BusinessOwnerID *gid.GID `json:"businessOwnerId"` - SecurityOwnerID *gid.GID `json:"securityOwnerId"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` +type ThirdParty struct { + ID gid.GID `json:"id"` + Name string `json:"name"` + Category coredata.ThirdPartyCategory `json:"category"` + Description *string `json:"description"` + StatusPageURL *string `json:"statusPageUrl"` + TermsOfServiceURL *string `json:"termsOfServiceUrl"` + PrivacyPolicyURL *string `json:"privacyPolicyUrl"` + ServiceLevelAgreementURL *string `json:"serviceLevelAgreementUrl"` + DataProcessingAgreementURL *string `json:"dataProcessingAgreementUrl"` + BusinessAssociateAgreementURL *string `json:"businessAssociateAgreementUrl"` + SubprocessorsListURL *string `json:"subprocessorsListUrl"` + Certifications []string `json:"certifications"` + Countries []coredata.CountryCode `json:"countries"` + SecurityPageURL *string `json:"securityPageUrl"` + TrustPageURL *string `json:"trustPageUrl"` + HeadquarterAddress *string `json:"headquarterAddress"` + LegalName *string `json:"legalName"` + WebsiteURL *string `json:"websiteUrl"` + BusinessOwnerID *gid.GID `json:"businessOwnerId"` + SecurityOwnerID *gid.GID `json:"securityOwnerId"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` } -func NewVendor(v *coredata.Vendor) *Vendor { - return &Vendor{ +func NewThirdParty(v *coredata.ThirdParty) *ThirdParty { + return &ThirdParty{ ID: v.ID, Name: v.Name, Category: v.Category,