From c76764b69a5af7592b4634984407af80ffb3b6ce Mon Sep 17 00:00:00 2001 From: Sacha Al Himdani Date: Mon, 22 Dec 2025 17:36:50 +0100 Subject: [PATCH] Add processing activity exports Signed-off-by: Sacha Al Himdani --- .../ProcessingActivitiesPage.tsx | 208 +++- ...vitiesPageExportDPIAPDFMutation.graphql.ts | 96 ++ ...ActivitiesPageExportPDFMutation.graphql.ts | 96 ++ ...ivitiesPageExportTIAPDFMutation.graphql.ts | 96 ++ e2e/console/processing_activity_test.go | 389 +++++++ pkg/authz/permissions.go | 117 ++- .../data_protection_impact_assessment.go | 57 +- ...ata_protection_impact_assessment_filter.go | 6 + pkg/coredata/processing_activities.go | 70 ++ pkg/coredata/processing_activity_filter.go | 7 + pkg/coredata/snapshot.go | 9 +- pkg/coredata/snapshot_filter.go | 65 ++ pkg/coredata/transfer_impact_assessment.go | 57 +- .../transfer_impact_assessment_filter.go | 6 + pkg/coredata/vendor.go | 72 +- ...rotection_impact_assessments_template.html | 315 ++++++ pkg/docgen/generator.go | 213 +++- pkg/docgen/generator_test.go | 4 +- .../processing_activities_template.html | 535 ++++++++++ .../transfer_impact_assessments_template.html | 288 ++++++ ...ta_protection_impact_assessment_service.go | 133 ++- pkg/probo/processing_activity_service.go | 163 ++- pkg/probo/service.go | 15 +- pkg/probo/snapshot_service.go | 3 +- .../transfer_impact_assessment_service.go | 133 ++- pkg/server/api/console/v1/schema.graphql | 36 + pkg/server/api/console/v1/schema/schema.go | 953 +++++++++++++++--- pkg/server/api/console/v1/types/types.go | 27 + pkg/server/api/console/v1/v1_resolver.go | 78 ++ 29 files changed, 4021 insertions(+), 226 deletions(-) create mode 100644 apps/console/src/pages/organizations/processingActivities/__generated__/ProcessingActivitiesPageExportDPIAPDFMutation.graphql.ts create mode 100644 apps/console/src/pages/organizations/processingActivities/__generated__/ProcessingActivitiesPageExportPDFMutation.graphql.ts create mode 100644 apps/console/src/pages/organizations/processingActivities/__generated__/ProcessingActivitiesPageExportTIAPDFMutation.graphql.ts create mode 100644 pkg/coredata/snapshot_filter.go create mode 100644 pkg/docgen/data_protection_impact_assessments_template.html create mode 100644 pkg/docgen/processing_activities_template.html create mode 100644 pkg/docgen/transfer_impact_assessments_template.html diff --git a/apps/console/src/pages/organizations/processingActivities/ProcessingActivitiesPage.tsx b/apps/console/src/pages/organizations/processingActivities/ProcessingActivitiesPage.tsx index 8b4755a85..e04dc1f42 100644 --- a/apps/console/src/pages/organizations/processingActivities/ProcessingActivitiesPage.tsx +++ b/apps/console/src/pages/organizations/processingActivities/ProcessingActivitiesPage.tsx @@ -16,6 +16,10 @@ import { useConfirm, Tabs, TabItem, + IconArrowDown, + IconChevronDown, + Spinner, + Dropdown, } from "@probo/ui"; import { useTranslate } from "@probo/i18n"; import { usePageTitle } from "@probo/hooks"; @@ -32,7 +36,8 @@ import { useOrganizationId } from "/hooks/useOrganizationId"; import { useParams } from "react-router"; import { CreateProcessingActivityDialog } from "./dialogs/CreateProcessingActivityDialog"; import { deleteProcessingActivityMutation, ProcessingActivitiesConnectionKey, processingActivitiesQuery } from "../../../hooks/graph/ProcessingActivityGraph"; -import { sprintf, promisifyMutation } from "@probo/helpers"; +import { sprintf, promisifyMutation, downloadFile, toDateInput } from "@probo/helpers"; +import { useMutationWithToasts } from "/hooks/useMutationWithToasts"; import { SnapshotBanner } from "/components/SnapshotBanner"; import type { NodeOf } from "/types"; import type { @@ -174,6 +179,36 @@ const tiaListPageFragment = graphql` } `; +const exportProcessingActivitiesPDFMutation = graphql` + mutation ProcessingActivitiesPageExportPDFMutation( + $input: ExportProcessingActivitiesPDFInput! + ) { + exportProcessingActivitiesPDF(input: $input) { + data + } + } +`; + +const exportDataProtectionImpactAssessmentsPDFMutation = graphql` + mutation ProcessingActivitiesPageExportDPIAPDFMutation( + $input: ExportDataProtectionImpactAssessmentsPDFInput! + ) { + exportDataProtectionImpactAssessmentsPDF(input: $input) { + data + } + } +`; + +const exportTransferImpactAssessmentsPDFMutation = graphql` + mutation ProcessingActivitiesPageExportTIAPDFMutation( + $input: ExportTransferImpactAssessmentsPDFInput! + ) { + exportTransferImpactAssessmentsPDF(input: $input) { + data + } + } +`; + export default function ProcessingActivitiesPage({ queryRef }: ProcessingActivitiesPageProps) { const { __ } = useTranslate(); const organizationId = useOrganizationId(); @@ -233,12 +268,169 @@ export default function ProcessingActivitiesPage({ queryRef }: ProcessingActivit isAuthorized("ProcessingActivity", "deleteProcessingActivity") ); + const canExportPDF = isAuthorized("ProcessingActivity", "exportProcessingActivitiesPDF"); + const [exportPDF, isExportingPDF] = useMutationWithToasts<{ + response: { + exportProcessingActivitiesPDF?: { + data: string; + }; + }; + variables: { + input: { + organizationId: string; + filter: { snapshotId: string } | null; + }; + }; + }>( + exportProcessingActivitiesPDFMutation, + { + successMessage: __("PDF download started."), + errorMessage: __("Failed to generate PDF"), + } + ); + + const handleExportPDF = () => { + exportPDF({ + variables: { + input: { + organizationId: organizationId, + filter: snapshotId ? { snapshotId } : null, + }, + }, + onCompleted: (data) => { + if (data.exportProcessingActivitiesPDF?.data) { + downloadFile( + data.exportProcessingActivitiesPDF.data, + `processing-activities-${toDateInput(new Date().toISOString())}.pdf` + ); + } + }, + }); + }; + + const canExportDPIAPDF = isAuthorized("Organization", "exportDataProtectionImpactAssessmentsPDF"); + const [exportDPIAPDF, isExportingDPIAPDF] = useMutationWithToasts<{ + response: { + exportDataProtectionImpactAssessmentsPDF?: { + data: string; + }; + }; + variables: { + input: { + organizationId: string; + filter: { snapshotId: string } | null; + }; + }; + }>( + exportDataProtectionImpactAssessmentsPDFMutation, + { + successMessage: __("PDF download started."), + errorMessage: __("Failed to generate PDF"), + } + ); + + const handleExportDPIAPDF = () => { + exportDPIAPDF({ + variables: { + input: { + organizationId: organizationId, + filter: snapshotId ? { snapshotId } : null, + }, + }, + onCompleted: (data) => { + if (data.exportDataProtectionImpactAssessmentsPDF?.data) { + downloadFile( + data.exportDataProtectionImpactAssessmentsPDF.data, + `data-protection-impact-assessments-${toDateInput(new Date().toISOString())}.pdf` + ); + } + }, + }); + }; + + const canExportTIAPDF = isAuthorized("Organization", "exportTransferImpactAssessmentsPDF"); + const [exportTIAPDF, isExportingTIAPDF] = useMutationWithToasts<{ + response: { + exportTransferImpactAssessmentsPDF?: { + data: string; + }; + }; + variables: { + input: { + organizationId: string; + filter: { snapshotId: string } | null; + }; + }; + }>( + exportTransferImpactAssessmentsPDFMutation, + { + successMessage: __("PDF download started."), + errorMessage: __("Failed to generate PDF"), + } + ); + + const handleExportTIAPDF = () => { + exportTIAPDF({ + variables: { + input: { + organizationId: organizationId, + filter: snapshotId ? { snapshotId } : null, + }, + }, + onCompleted: (data) => { + if (data.exportTransferImpactAssessmentsPDF?.data) { + downloadFile( + data.exportTransferImpactAssessmentsPDF.data, + `transfer-impact-assessments-${toDateInput(new Date().toISOString())}.pdf` + ); + } + }, + }); + }; + return (
{isSnapshotMode && snapshotId && ( )} + {(canExportPDF || canExportDPIAPDF || canExportTIAPDF) && ( + + {__("Export")} + + } + > + {canExportPDF && ( + + {__("Processing Activities")} + + )} + {canExportDPIAPDF && ( + + {__("Data Protection Impact Assessments")} + + )} + {canExportTIAPDF && ( + + {__("Transfer Impact Assessments")} + + )} + + )} {!isSnapshotMode && activeTab === "activities" && ( isAuthorized("Organization", "createProcessingActivity") && ( - {__("Name")} - {__("Purpose")} - {__("Data Subject")} - {__("Lawful Basis")} - {__("Location")} - {__("International Transfers")} - {hasAnyAction && {__("Actions")}} + {__("Name")} + {__("Purpose")} + {__("Data Subject")} + {__("Lawful Basis")} + {__("Location")} + {__("International Transfers")} + {hasAnyAction && {__("Actions")}} diff --git a/apps/console/src/pages/organizations/processingActivities/__generated__/ProcessingActivitiesPageExportDPIAPDFMutation.graphql.ts b/apps/console/src/pages/organizations/processingActivities/__generated__/ProcessingActivitiesPageExportDPIAPDFMutation.graphql.ts new file mode 100644 index 000000000..ce85042bb --- /dev/null +++ b/apps/console/src/pages/organizations/processingActivities/__generated__/ProcessingActivitiesPageExportDPIAPDFMutation.graphql.ts @@ -0,0 +1,96 @@ +/** + * @generated SignedSource<<2f43f6874d8b6d515cf244934b7b19ea>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type ExportDataProtectionImpactAssessmentsPDFInput = { + filter?: DataProtectionImpactAssessmentFilter | null | undefined; + organizationId: string; +}; +export type DataProtectionImpactAssessmentFilter = { + snapshotId?: string | null | undefined; +}; +export type ProcessingActivitiesPageExportDPIAPDFMutation$variables = { + input: ExportDataProtectionImpactAssessmentsPDFInput; +}; +export type ProcessingActivitiesPageExportDPIAPDFMutation$data = { + readonly exportDataProtectionImpactAssessmentsPDF: { + readonly data: string; + }; +}; +export type ProcessingActivitiesPageExportDPIAPDFMutation = { + response: ProcessingActivitiesPageExportDPIAPDFMutation$data; + variables: ProcessingActivitiesPageExportDPIAPDFMutation$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "input" + } +], +v1 = [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "input", + "variableName": "input" + } + ], + "concreteType": "ExportDataProtectionImpactAssessmentsPDFPayload", + "kind": "LinkedField", + "name": "exportDataProtectionImpactAssessmentsPDF", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "data", + "storageKey": null + } + ], + "storageKey": null + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "ProcessingActivitiesPageExportDPIAPDFMutation", + "selections": (v1/*: any*/), + "type": "Mutation", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "ProcessingActivitiesPageExportDPIAPDFMutation", + "selections": (v1/*: any*/) + }, + "params": { + "cacheID": "851a1c0def7719185cf3a67954696231", + "id": null, + "metadata": {}, + "name": "ProcessingActivitiesPageExportDPIAPDFMutation", + "operationKind": "mutation", + "text": "mutation ProcessingActivitiesPageExportDPIAPDFMutation(\n $input: ExportDataProtectionImpactAssessmentsPDFInput!\n) {\n exportDataProtectionImpactAssessmentsPDF(input: $input) {\n data\n }\n}\n" + } +}; +})(); + +(node as any).hash = "3c2a96fb29c61791e7df39c7b8174c90"; + +export default node; diff --git a/apps/console/src/pages/organizations/processingActivities/__generated__/ProcessingActivitiesPageExportPDFMutation.graphql.ts b/apps/console/src/pages/organizations/processingActivities/__generated__/ProcessingActivitiesPageExportPDFMutation.graphql.ts new file mode 100644 index 000000000..a29c6936a --- /dev/null +++ b/apps/console/src/pages/organizations/processingActivities/__generated__/ProcessingActivitiesPageExportPDFMutation.graphql.ts @@ -0,0 +1,96 @@ +/** + * @generated SignedSource<<82ece1ab5e9585bfcce605e38fe9563a>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type ExportProcessingActivitiesPDFInput = { + filter?: ProcessingActivityFilter | null | undefined; + organizationId: string; +}; +export type ProcessingActivityFilter = { + snapshotId?: string | null | undefined; +}; +export type ProcessingActivitiesPageExportPDFMutation$variables = { + input: ExportProcessingActivitiesPDFInput; +}; +export type ProcessingActivitiesPageExportPDFMutation$data = { + readonly exportProcessingActivitiesPDF: { + readonly data: string; + }; +}; +export type ProcessingActivitiesPageExportPDFMutation = { + response: ProcessingActivitiesPageExportPDFMutation$data; + variables: ProcessingActivitiesPageExportPDFMutation$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "input" + } +], +v1 = [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "input", + "variableName": "input" + } + ], + "concreteType": "ExportProcessingActivitiesPDFPayload", + "kind": "LinkedField", + "name": "exportProcessingActivitiesPDF", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "data", + "storageKey": null + } + ], + "storageKey": null + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "ProcessingActivitiesPageExportPDFMutation", + "selections": (v1/*: any*/), + "type": "Mutation", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "ProcessingActivitiesPageExportPDFMutation", + "selections": (v1/*: any*/) + }, + "params": { + "cacheID": "85140bef5e55c8ae4fdb139965375f13", + "id": null, + "metadata": {}, + "name": "ProcessingActivitiesPageExportPDFMutation", + "operationKind": "mutation", + "text": "mutation ProcessingActivitiesPageExportPDFMutation(\n $input: ExportProcessingActivitiesPDFInput!\n) {\n exportProcessingActivitiesPDF(input: $input) {\n data\n }\n}\n" + } +}; +})(); + +(node as any).hash = "cc777bb4440f6aed36c2fb92c1bc7e0b"; + +export default node; diff --git a/apps/console/src/pages/organizations/processingActivities/__generated__/ProcessingActivitiesPageExportTIAPDFMutation.graphql.ts b/apps/console/src/pages/organizations/processingActivities/__generated__/ProcessingActivitiesPageExportTIAPDFMutation.graphql.ts new file mode 100644 index 000000000..d40e9f622 --- /dev/null +++ b/apps/console/src/pages/organizations/processingActivities/__generated__/ProcessingActivitiesPageExportTIAPDFMutation.graphql.ts @@ -0,0 +1,96 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type ExportTransferImpactAssessmentsPDFInput = { + filter?: TransferImpactAssessmentFilter | null | undefined; + organizationId: string; +}; +export type TransferImpactAssessmentFilter = { + snapshotId?: string | null | undefined; +}; +export type ProcessingActivitiesPageExportTIAPDFMutation$variables = { + input: ExportTransferImpactAssessmentsPDFInput; +}; +export type ProcessingActivitiesPageExportTIAPDFMutation$data = { + readonly exportTransferImpactAssessmentsPDF: { + readonly data: string; + }; +}; +export type ProcessingActivitiesPageExportTIAPDFMutation = { + response: ProcessingActivitiesPageExportTIAPDFMutation$data; + variables: ProcessingActivitiesPageExportTIAPDFMutation$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "input" + } +], +v1 = [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "input", + "variableName": "input" + } + ], + "concreteType": "ExportTransferImpactAssessmentsPDFPayload", + "kind": "LinkedField", + "name": "exportTransferImpactAssessmentsPDF", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "data", + "storageKey": null + } + ], + "storageKey": null + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "ProcessingActivitiesPageExportTIAPDFMutation", + "selections": (v1/*: any*/), + "type": "Mutation", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "ProcessingActivitiesPageExportTIAPDFMutation", + "selections": (v1/*: any*/) + }, + "params": { + "cacheID": "b41ab5f56ee5dc4436069c4e2a97f8d8", + "id": null, + "metadata": {}, + "name": "ProcessingActivitiesPageExportTIAPDFMutation", + "operationKind": "mutation", + "text": "mutation ProcessingActivitiesPageExportTIAPDFMutation(\n $input: ExportTransferImpactAssessmentsPDFInput!\n) {\n exportTransferImpactAssessmentsPDF(input: $input) {\n data\n }\n}\n" + } +}; +})(); + +(node as any).hash = "3fe289d5564d0617074825d8c58f88f8"; + +export default node; diff --git a/e2e/console/processing_activity_test.go b/e2e/console/processing_activity_test.go index 23d248833..5dbe1f477 100644 --- a/e2e/console/processing_activity_test.go +++ b/e2e/console/processing_activity_test.go @@ -2596,3 +2596,392 @@ func TestProcessingActivity_Snapshot_DPIA_TIA(t *testing.T) { } }) } + +func TestProcessingActivity_ExportPDF(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + + t.Run("export processing activities PDF", func(t *testing.T) { + _ = factory.NewProcessingActivity(owner). + WithName("PA Export Test 1"). + WithLawfulBasis("CONSENT"). + Create() + _ = factory.NewProcessingActivity(owner). + WithName("PA Export Test 2"). + WithLawfulBasis("LEGITIMATE_INTEREST"). + Create() + + query := ` + mutation ExportProcessingActivitiesPDF($input: ExportProcessingActivitiesPDFInput!) { + exportProcessingActivitiesPDF(input: $input) { + data + } + } + ` + + var result struct { + ExportProcessingActivitiesPDF struct { + Data string `json:"data"` + } `json:"exportProcessingActivitiesPDF"` + } + + err := owner.Execute(query, map[string]any{ + "input": map[string]any{ + "organizationId": owner.GetOrganizationID().String(), + "filter": nil, + }, + }, &result) + require.NoError(t, err) + assert.NotEmpty(t, result.ExportProcessingActivitiesPDF.Data) + assert.Contains(t, result.ExportProcessingActivitiesPDF.Data, "data:application/pdf;base64,") + }) + + t.Run("export processing activities PDF with snapshot filter", func(t *testing.T) { + _ = factory.NewProcessingActivity(owner). + WithName("PA Snapshot Export Test"). + Create() + + // Create snapshot + var snapshotResult struct { + CreateSnapshot struct { + SnapshotEdge struct { + Node struct { + ID string `json:"id"` + } `json:"node"` + } `json:"snapshotEdge"` + } `json:"createSnapshot"` + } + err := owner.Execute(` + mutation($input: CreateSnapshotInput!) { + createSnapshot(input: $input) { + snapshotEdge { + node { id } + } + } + } + `, map[string]any{ + "input": map[string]any{ + "organizationId": owner.GetOrganizationID().String(), + "name": fmt.Sprintf("PA Export Snapshot Test %d", time.Now().UnixNano()), + "type": "PROCESSING_ACTIVITIES", + }, + }, &snapshotResult) + require.NoError(t, err) + snapshotID := snapshotResult.CreateSnapshot.SnapshotEdge.Node.ID + + query := ` + mutation ExportProcessingActivitiesPDF($input: ExportProcessingActivitiesPDFInput!) { + exportProcessingActivitiesPDF(input: $input) { + data + } + } + ` + + var result struct { + ExportProcessingActivitiesPDF struct { + Data string `json:"data"` + } `json:"exportProcessingActivitiesPDF"` + } + + err = owner.Execute(query, map[string]any{ + "input": map[string]any{ + "organizationId": owner.GetOrganizationID().String(), + "filter": map[string]any{ + "snapshotId": snapshotID, + }, + }, + }, &result) + require.NoError(t, err) + assert.NotEmpty(t, result.ExportProcessingActivitiesPDF.Data) + assert.Contains(t, result.ExportProcessingActivitiesPDF.Data, "data:application/pdf;base64,") + }) + + t.Run("export fails with no processing activities", func(t *testing.T) { + newOwner := testutil.NewClient(t, testutil.RoleOwner) + + query := ` + mutation ExportProcessingActivitiesPDF($input: ExportProcessingActivitiesPDFInput!) { + exportProcessingActivitiesPDF(input: $input) { + data + } + } + ` + + _, err := newOwner.Do(query, map[string]any{ + "input": map[string]any{ + "organizationId": newOwner.GetOrganizationID().String(), + "filter": nil, + }, + }) + testutil.RequireErrorCode(t, err, "NOT_FOUND") + assert.Contains(t, err.Error(), "no processing activities found") + }) +} + +func TestDataProtectionImpactAssessment_ExportPDF(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + + t.Run("export DPIA PDF", func(t *testing.T) { + pa1ID := factory.NewProcessingActivity(owner). + WithName("DPIA Export Test PA 1"). + Create() + pa2ID := factory.NewProcessingActivity(owner). + WithName("DPIA Export Test PA 2"). + Create() + + _, err := owner.Do(` + mutation($input: CreateDataProtectionImpactAssessmentInput!) { + createDataProtectionImpactAssessment(input: $input) { + dataProtectionImpactAssessment { id } + } + } + `, map[string]any{ + "input": map[string]any{ + "processingActivityId": pa1ID, + "description": "DPIA 1 description", + "residualRisk": "LOW", + }, + }) + require.NoError(t, err) + + _, err = owner.Do(` + mutation($input: CreateDataProtectionImpactAssessmentInput!) { + createDataProtectionImpactAssessment(input: $input) { + dataProtectionImpactAssessment { id } + } + } + `, map[string]any{ + "input": map[string]any{ + "processingActivityId": pa2ID, + "description": "DPIA 2 description", + "residualRisk": "MEDIUM", + }, + }) + require.NoError(t, err) + + query := ` + mutation ExportDataProtectionImpactAssessmentsPDF($input: ExportDataProtectionImpactAssessmentsPDFInput!) { + exportDataProtectionImpactAssessmentsPDF(input: $input) { + data + } + } + ` + + var result struct { + ExportDataProtectionImpactAssessmentsPDF struct { + Data string `json:"data"` + } `json:"exportDataProtectionImpactAssessmentsPDF"` + } + + err = owner.Execute(query, map[string]any{ + "input": map[string]any{ + "organizationId": owner.GetOrganizationID().String(), + "filter": nil, + }, + }, &result) + require.NoError(t, err) + assert.NotEmpty(t, result.ExportDataProtectionImpactAssessmentsPDF.Data) + assert.Contains(t, result.ExportDataProtectionImpactAssessmentsPDF.Data, "data:application/pdf;base64,") + }) + + t.Run("export fails with no DPIAs", func(t *testing.T) { + newOwner := testutil.NewClient(t, testutil.RoleOwner) + + query := ` + mutation ExportDataProtectionImpactAssessmentsPDF($input: ExportDataProtectionImpactAssessmentsPDFInput!) { + exportDataProtectionImpactAssessmentsPDF(input: $input) { + data + } + } + ` + + _, err := newOwner.Do(query, map[string]any{ + "input": map[string]any{ + "organizationId": newOwner.GetOrganizationID().String(), + "filter": nil, + }, + }) + testutil.RequireErrorCode(t, err, "NOT_FOUND") + assert.Contains(t, err.Error(), "no data protection impact assessments found") + }) +} + +func TestTransferImpactAssessment_ExportPDF(t *testing.T) { + t.Parallel() + owner := testutil.NewClient(t, testutil.RoleOwner) + + t.Run("export TIA PDF", func(t *testing.T) { + pa1ID := factory.NewProcessingActivity(owner). + WithName("TIA Export Test PA 1"). + Create() + pa2ID := factory.NewProcessingActivity(owner). + WithName("TIA Export Test PA 2"). + Create() + + _, err := owner.Do(` + mutation($input: CreateTransferImpactAssessmentInput!) { + createTransferImpactAssessment(input: $input) { + transferImpactAssessment { id } + } + } + `, map[string]any{ + "input": map[string]any{ + "processingActivityId": pa1ID, + "dataSubjects": "TIA 1 subjects", + "transfer": "EU to US", + }, + }) + require.NoError(t, err) + + _, err = owner.Do(` + mutation($input: CreateTransferImpactAssessmentInput!) { + createTransferImpactAssessment(input: $input) { + transferImpactAssessment { id } + } + } + `, map[string]any{ + "input": map[string]any{ + "processingActivityId": pa2ID, + "dataSubjects": "TIA 2 subjects", + "transfer": "EU to UK", + }, + }) + require.NoError(t, err) + + query := ` + mutation ExportTransferImpactAssessmentsPDF($input: ExportTransferImpactAssessmentsPDFInput!) { + exportTransferImpactAssessmentsPDF(input: $input) { + data + } + } + ` + + var result struct { + ExportTransferImpactAssessmentsPDF struct { + Data string `json:"data"` + } `json:"exportTransferImpactAssessmentsPDF"` + } + + err = owner.Execute(query, map[string]any{ + "input": map[string]any{ + "organizationId": owner.GetOrganizationID().String(), + "filter": nil, + }, + }, &result) + require.NoError(t, err) + assert.NotEmpty(t, result.ExportTransferImpactAssessmentsPDF.Data) + assert.Contains(t, result.ExportTransferImpactAssessmentsPDF.Data, "data:application/pdf;base64,") + }) + + t.Run("export fails with no TIAs", func(t *testing.T) { + newOwner := testutil.NewClient(t, testutil.RoleOwner) + + query := ` + mutation ExportTransferImpactAssessmentsPDF($input: ExportTransferImpactAssessmentsPDFInput!) { + exportTransferImpactAssessmentsPDF(input: $input) { + data + } + } + ` + + _, err := newOwner.Do(query, map[string]any{ + "input": map[string]any{ + "organizationId": newOwner.GetOrganizationID().String(), + "filter": nil, + }, + }) + testutil.RequireErrorCode(t, err, "NOT_FOUND") + assert.Contains(t, err.Error(), "no transfer impact assessments found") + }) +} + +func TestProcessingActivity_ExportPDF_RBAC(t *testing.T) { + t.Parallel() + + t.Run("owner can export PDF", func(t *testing.T) { + owner := testutil.NewClient(t, testutil.RoleOwner) + _ = factory.NewProcessingActivity(owner).WithName("RBAC Export Test").Create() + + query := ` + mutation ExportProcessingActivitiesPDF($input: ExportProcessingActivitiesPDFInput!) { + exportProcessingActivitiesPDF(input: $input) { + data + } + } + ` + + var result struct { + ExportProcessingActivitiesPDF struct { + Data string `json:"data"` + } `json:"exportProcessingActivitiesPDF"` + } + + err := owner.Execute(query, map[string]any{ + "input": map[string]any{ + "organizationId": owner.GetOrganizationID().String(), + "filter": nil, + }, + }, &result) + require.NoError(t, err, "owner should be able to export PDF") + assert.NotEmpty(t, result.ExportProcessingActivitiesPDF.Data) + }) + + t.Run("admin can export PDF", func(t *testing.T) { + owner := testutil.NewClient(t, testutil.RoleOwner) + admin := testutil.NewClientInOrg(t, testutil.RoleAdmin, owner) + _ = factory.NewProcessingActivity(owner).WithName("RBAC Export Test").Create() + + query := ` + mutation ExportProcessingActivitiesPDF($input: ExportProcessingActivitiesPDFInput!) { + exportProcessingActivitiesPDF(input: $input) { + data + } + } + ` + + var result struct { + ExportProcessingActivitiesPDF struct { + Data string `json:"data"` + } `json:"exportProcessingActivitiesPDF"` + } + + err := admin.Execute(query, map[string]any{ + "input": map[string]any{ + "organizationId": admin.GetOrganizationID().String(), + "filter": nil, + }, + }, &result) + require.NoError(t, err, "admin should be able to export PDF") + assert.NotEmpty(t, result.ExportProcessingActivitiesPDF.Data) + }) + + t.Run("viewer can export PDF", func(t *testing.T) { + owner := testutil.NewClient(t, testutil.RoleOwner) + viewer := testutil.NewClientInOrg(t, testutil.RoleViewer, owner) + _ = factory.NewProcessingActivity(owner).WithName("RBAC Export Test").Create() + + query := ` + mutation ExportProcessingActivitiesPDF($input: ExportProcessingActivitiesPDFInput!) { + exportProcessingActivitiesPDF(input: $input) { + data + } + } + ` + + var result struct { + ExportProcessingActivitiesPDF struct { + Data string `json:"data"` + } `json:"exportProcessingActivitiesPDF"` + } + + err := viewer.Execute(query, map[string]any{ + "input": map[string]any{ + "organizationId": viewer.GetOrganizationID().String(), + "filter": nil, + }, + }, &result) + require.NoError(t, err, "viewer should be able to export PDF") + assert.NotEmpty(t, result.ExportProcessingActivitiesPDF.Data) + }) +} diff --git a/pkg/authz/permissions.go b/pkg/authz/permissions.go index 3a7942837..4c490437a 100644 --- a/pkg/authz/permissions.go +++ b/pkg/authz/permissions.go @@ -92,7 +92,7 @@ const ( ActionListComplianceReports Action = "listComplianceReports" ActionListContacts Action = "listContacts" ActionListContinualImprovements Action = "listContinualImprovements" - ActionListRightsRequests Action = "listRightsRequests" + ActionListRightsRequests Action = "listRightsRequests" ActionListControls Action = "listControls" ActionListData Action = "listData" ActionListDocuments Action = "listDocuments" @@ -235,39 +235,42 @@ const ( ActionDeleteVendorDataPrivacyAgreement Action = "deleteVendorDataPrivacyAgreement" ActionDeleteVendorService Action = "deleteVendorService" - ActionAcceptInvitation Action = "acceptInvitation" - ActionAssessVendor Action = "assessVendor" - ActionAssignTask Action = "assignTask" - ActionBulkDeleteDocuments Action = "bulkDeleteDocuments" - ActionBulkExportDocuments Action = "bulkExportDocuments" - ActionBulkPublishDocumentVersions Action = "bulkPublishDocumentVersions" - ActionBulkRequestSignatures Action = "bulkRequestSignatures" - ActionCancelSignatureRequest Action = "cancelSignatureRequest" - ActionSignDocument Action = "signDocument" - ActionConfirmEmail Action = "confirmEmail" - ActionDisableSAML Action = "disableSAML" - ActionEnableSAML Action = "enableSAML" - ActionExportDocumentVersionPDF Action = "exportDocumentVersionPDF" - ActionExportSignableVersionDocumentPDF Action = "exportSignableVersionDocumentPDF" - ActionExportFramework Action = "exportFramework" - ActionGenerateDocumentChangelog Action = "generateDocumentChangelog" - ActionGenerateFrameworkStateOfApplicability Action = "generateFrameworkStateOfApplicability" - ActionImportFramework Action = "importFramework" - ActionImportMeasure Action = "importMeasure" - ActionInitiateDomainVerification Action = "initiateDomainVerification" - ActionInviteUser Action = "inviteUser" - ActionPublishDocumentVersion Action = "publishDocumentVersion" - ActionRemoveMember Action = "removeMember" - ActionRequestSignature Action = "requestSignature" - ActionSendSigningNotifications Action = "sendSigningNotifications" - ActionUnassignTask Action = "unassignTask" - ActionUploadAuditReport Action = "uploadAuditReport" - ActionUploadMeasureEvidence Action = "uploadMeasureEvidence" - ActionUploadTrustCenterNDA Action = "uploadTrustCenterNDA" - ActionUploadVendorBusinessAssociateAgreement Action = "uploadVendorBusinessAssociateAgreement" - ActionUploadVendorComplianceReport Action = "uploadVendorComplianceReport" - ActionUploadVendorDataPrivacyAgreement Action = "uploadVendorDataPrivacyAgreement" - ActionVerifyDomain Action = "verifyDomain" + ActionAcceptInvitation Action = "acceptInvitation" + ActionAssessVendor Action = "assessVendor" + ActionAssignTask Action = "assignTask" + ActionBulkDeleteDocuments Action = "bulkDeleteDocuments" + ActionBulkExportDocuments Action = "bulkExportDocuments" + ActionBulkPublishDocumentVersions Action = "bulkPublishDocumentVersions" + ActionBulkRequestSignatures Action = "bulkRequestSignatures" + ActionCancelSignatureRequest Action = "cancelSignatureRequest" + ActionSignDocument Action = "signDocument" + ActionConfirmEmail Action = "confirmEmail" + ActionDisableSAML Action = "disableSAML" + ActionEnableSAML Action = "enableSAML" + ActionExportDocumentVersionPDF Action = "exportDocumentVersionPDF" + ActionExportSignableVersionDocumentPDF Action = "exportSignableVersionDocumentPDF" + ActionExportProcessingActivitiesPDF Action = "exportProcessingActivitiesPDF" + ActionExportDataProtectionImpactAssessmentsPDF Action = "exportDataProtectionImpactAssessmentsPDF" + ActionExportTransferImpactAssessmentsPDF Action = "exportTransferImpactAssessmentsPDF" + ActionExportFramework Action = "exportFramework" + ActionGenerateDocumentChangelog Action = "generateDocumentChangelog" + ActionGenerateFrameworkStateOfApplicability Action = "generateFrameworkStateOfApplicability" + ActionImportFramework Action = "importFramework" + ActionImportMeasure Action = "importMeasure" + ActionInitiateDomainVerification Action = "initiateDomainVerification" + ActionInviteUser Action = "inviteUser" + ActionPublishDocumentVersion Action = "publishDocumentVersion" + ActionRemoveMember Action = "removeMember" + ActionRequestSignature Action = "requestSignature" + ActionSendSigningNotifications Action = "sendSigningNotifications" + ActionUnassignTask Action = "unassignTask" + ActionUploadAuditReport Action = "uploadAuditReport" + ActionUploadMeasureEvidence Action = "uploadMeasureEvidence" + ActionUploadTrustCenterNDA Action = "uploadTrustCenterNDA" + ActionUploadVendorBusinessAssociateAgreement Action = "uploadVendorBusinessAssociateAgreement" + ActionUploadVendorComplianceReport Action = "uploadVendorComplianceReport" + ActionUploadVendorDataPrivacyAgreement Action = "uploadVendorDataPrivacyAgreement" + ActionVerifyDomain Action = "verifyDomain" ) var ( @@ -287,27 +290,30 @@ var Permissions = map[uint16]map[Action][]Role{ ActionListSignableDocuments: InternalRoles, - ActionListDocuments: NonEmployeeRoles, - ActionGetHorizontalLogoUrl: NonEmployeeRoles, - ActionPeoples: NonEmployeeRoles, - ActionTotalCount: NonEmployeeRoles, - ActionListFrameworks: NonEmployeeRoles, - ActionListControls: NonEmployeeRoles, - ActionListVendors: NonEmployeeRoles, - ActionListPeople: NonEmployeeRoles, - ActionListMeasures: NonEmployeeRoles, - ActionListRisks: NonEmployeeRoles, - ActionListAssets: NonEmployeeRoles, - ActionListData: NonEmployeeRoles, - ActionListAudits: NonEmployeeRoles, - ActionListNonconformities: NonEmployeeRoles, - ActionListObligations: NonEmployeeRoles, - ActionListContinualImprovements: NonEmployeeRoles, - ActionListRightsRequests: NonEmployeeRoles, - ActionListProcessingActivities: NonEmployeeRoles, - ActionListSnapshots: NonEmployeeRoles, - ActionConfirmEmail: NonEmployeeRoles, - ActionAcceptInvitation: NonEmployeeRoles, + ActionListDocuments: NonEmployeeRoles, + ActionGetHorizontalLogoUrl: NonEmployeeRoles, + ActionPeoples: NonEmployeeRoles, + ActionTotalCount: NonEmployeeRoles, + ActionListFrameworks: NonEmployeeRoles, + ActionListControls: NonEmployeeRoles, + ActionListVendors: NonEmployeeRoles, + ActionListPeople: NonEmployeeRoles, + ActionListMeasures: NonEmployeeRoles, + ActionListRisks: NonEmployeeRoles, + ActionListAssets: NonEmployeeRoles, + ActionListData: NonEmployeeRoles, + ActionListAudits: NonEmployeeRoles, + ActionListNonconformities: NonEmployeeRoles, + ActionListObligations: NonEmployeeRoles, + ActionListContinualImprovements: NonEmployeeRoles, + ActionListRightsRequests: NonEmployeeRoles, + ActionListProcessingActivities: NonEmployeeRoles, + ActionExportProcessingActivitiesPDF: NonEmployeeRoles, + ActionExportDataProtectionImpactAssessmentsPDF: NonEmployeeRoles, + ActionExportTransferImpactAssessmentsPDF: NonEmployeeRoles, + ActionListSnapshots: NonEmployeeRoles, + ActionConfirmEmail: NonEmployeeRoles, + ActionAcceptInvitation: NonEmployeeRoles, ActionListTrustCenterFiles: CoreRoles, ActionGetTrustCenter: CoreRoles, @@ -694,6 +700,7 @@ var Permissions = map[uint16]map[Action][]Role{ ActionGetDataProtectionOfficer: NonEmployeeRoles, ActionGetDataProtectionImpactAssessment: NonEmployeeRoles, ActionGetTransferImpactAssessment: NonEmployeeRoles, + ActionExportProcessingActivitiesPDF: NonEmployeeRoles, ActionUpdateProcessingActivity: EditRoles, ActionDeleteProcessingActivity: EditRoles, diff --git a/pkg/coredata/data_protection_impact_assessment.go b/pkg/coredata/data_protection_impact_assessment.go index aa29d13db..e46532430 100644 --- a/pkg/coredata/data_protection_impact_assessment.go +++ b/pkg/coredata/data_protection_impact_assessment.go @@ -35,6 +35,12 @@ func (e ErrDataProtectionImpactAssessmentNotFound) Error() string { return fmt.Sprintf("data protection impact assessment not found: %q", e.Identifier) } +type ErrNoDataProtectionImpactAssessmentsFound struct{} + +func (e ErrNoDataProtectionImpactAssessmentsFound) Error() string { + return "no data protection impact assessments found" +} + type ( DataProtectionImpactAssessment struct { ID gid.GID `db:"id"` @@ -151,6 +157,56 @@ WHERE return nil } +func (dpias *DataProtectionImpactAssessments) LoadAllByOrganizationID( + ctx context.Context, + conn pg.Conn, + scope Scoper, + organizationID gid.GID, + filter *DataProtectionImpactAssessmentFilter, +) error { + q := ` +SELECT + id, + snapshot_id, + source_id, + organization_id, + processing_activity_id, + description, + necessity_and_proportionality, + potential_risk, + mitigations, + residual_risk, + created_at, + updated_at +FROM + processing_activity_data_protection_impact_assessments +WHERE + %s + AND organization_id = @organization_id + AND %s +` + + q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment()) + + args := pgx.StrictNamedArgs{"organization_id": organizationID} + maps.Copy(args, scope.SQLArguments()) + maps.Copy(args, filter.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query data protection impact assessments: %w", err) + } + + results, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[DataProtectionImpactAssessment]) + if err != nil { + return fmt.Errorf("cannot collect data protection impact assessments: %w", err) + } + + *dpias = results + + return nil +} + func (dpia *DataProtectionImpactAssessment) LoadByID( ctx context.Context, conn pg.Conn, @@ -429,4 +485,3 @@ WHERE dpia.tenant_id = @tenant_id AND dpia.organization_id = @organization_id AN return nil } - diff --git a/pkg/coredata/data_protection_impact_assessment_filter.go b/pkg/coredata/data_protection_impact_assessment_filter.go index e22a6ef42..b1ff88b13 100644 --- a/pkg/coredata/data_protection_impact_assessment_filter.go +++ b/pkg/coredata/data_protection_impact_assessment_filter.go @@ -53,3 +53,9 @@ func (f *DataProtectionImpactAssessmentFilter) SQLFragment() string { } } +func (f *DataProtectionImpactAssessmentFilter) SnapshotID() *gid.GID { + if f.snapshotID == nil || *f.snapshotID == nil { + return nil + } + return *f.snapshotID +} diff --git a/pkg/coredata/processing_activities.go b/pkg/coredata/processing_activities.go index 7ee37f75d..64ef6f0a2 100644 --- a/pkg/coredata/processing_activities.go +++ b/pkg/coredata/processing_activities.go @@ -26,6 +26,12 @@ import ( "go.probo.inc/probo/pkg/page" ) +type ErrNoProcessingActivitiesFound struct{} + +func (e ErrNoProcessingActivitiesFound) Error() string { + return "no processing activities found" +} + type ( ProcessingActivity struct { ID gid.GID `db:"id"` @@ -231,6 +237,70 @@ WHERE return nil } +func (p *ProcessingActivities) LoadAllByOrganizationID( + ctx context.Context, + conn pg.Conn, + scope Scoper, + organizationID gid.GID, + filter *ProcessingActivityFilter, +) error { + q := ` +SELECT + id, + snapshot_id, + source_id, + organization_id, + name, + purpose, + data_subject_category, + personal_data_category, + special_or_criminal_data, + consent_evidence_link, + lawful_basis, + recipients, + location, + international_transfers, + transfer_safeguards, + retention_period, + security_measures, + data_protection_impact_assessment_needed, + transfer_impact_assessment_needed, + last_review_date, + next_review_date, + role, + data_protection_officer_id, + created_at, + updated_at +FROM + processing_activities +WHERE + %s + AND organization_id = @organization_id + AND %s +ORDER BY created_at DESC +` + + q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment()) + + args := pgx.StrictNamedArgs{"organization_id": organizationID} + maps.Copy(args, scope.SQLArguments()) + maps.Copy(args, filter.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query processing activities: %w", err) + } + + processingActivities, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ProcessingActivity]) + if err != nil { + return fmt.Errorf("cannot collect processing activities: %w", err) + } + + *p = processingActivities + + return nil +} + func (p *ProcessingActivity) Insert( ctx context.Context, conn pg.Conn, diff --git a/pkg/coredata/processing_activity_filter.go b/pkg/coredata/processing_activity_filter.go index df6b5c123..688d715e1 100644 --- a/pkg/coredata/processing_activity_filter.go +++ b/pkg/coredata/processing_activity_filter.go @@ -52,3 +52,10 @@ func (f *ProcessingActivityFilter) SQLFragment() string { return "snapshot_id = @filter_snapshot_id" } } + +func (f *ProcessingActivityFilter) SnapshotID() *gid.GID { + if f.snapshotID == nil || *f.snapshotID == nil { + return nil + } + return *f.snapshotID +} diff --git a/pkg/coredata/snapshot.go b/pkg/coredata/snapshot.go index eb8848ab1..37bf88b9e 100644 --- a/pkg/coredata/snapshot.go +++ b/pkg/coredata/snapshot.go @@ -20,10 +20,10 @@ import ( "maps" "time" - "go.probo.inc/probo/pkg/gid" - "go.probo.inc/probo/pkg/page" "github.com/jackc/pgx/v5" "go.gearno.de/kit/pg" + "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/page" ) type ( @@ -99,6 +99,7 @@ func (s *Snapshots) CountByOrganizationID( conn pg.Conn, scope Scoper, organizationID gid.GID, + filter *SnapshotFilter, ) (int, error) { q := ` SELECT @@ -108,12 +109,14 @@ FROM WHERE %s AND organization_id = @organization_id + AND %s ` - q = fmt.Sprintf(q, scope.SQLFragment()) + q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment()) args := pgx.StrictNamedArgs{"organization_id": organizationID} maps.Copy(args, scope.SQLArguments()) + maps.Copy(args, filter.SQLArguments()) row := conn.QueryRow(ctx, q, args) diff --git a/pkg/coredata/snapshot_filter.go b/pkg/coredata/snapshot_filter.go new file mode 100644 index 000000000..04afb99ce --- /dev/null +++ b/pkg/coredata/snapshot_filter.go @@ -0,0 +1,65 @@ +// Copyright (c) 2025 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 ( + "time" + + "github.com/jackc/pgx/v5" +) + +type ( + SnapshotFilter struct { + snapshotType *SnapshotsType + beforeDate *time.Time + } +) + +func NewSnapshotFilter(snapshotType *SnapshotsType) *SnapshotFilter { + return &SnapshotFilter{ + snapshotType: snapshotType, + } +} + +func (f *SnapshotFilter) WithBeforeDate(beforeDate *time.Time) *SnapshotFilter { + f.beforeDate = beforeDate + return f +} + +func (f *SnapshotFilter) SQLArguments() pgx.NamedArgs { + args := pgx.NamedArgs{ + "filter_snapshot_type": f.snapshotType, + "filter_before_date": f.beforeDate, + } + + return args +} + +func (f *SnapshotFilter) SQLFragment() string { + return ` +( + CASE + WHEN @filter_snapshot_type::snapshots_type IS NOT NULL THEN + type = @filter_snapshot_type::snapshots_type + ELSE TRUE + END + AND + CASE + WHEN @filter_before_date::timestamptz IS NOT NULL THEN + created_at <= @filter_before_date::timestamptz + ELSE TRUE + END +)` +} diff --git a/pkg/coredata/transfer_impact_assessment.go b/pkg/coredata/transfer_impact_assessment.go index 2d890fa86..f60aa6bb2 100644 --- a/pkg/coredata/transfer_impact_assessment.go +++ b/pkg/coredata/transfer_impact_assessment.go @@ -35,6 +35,12 @@ func (e ErrTransferImpactAssessmentNotFound) Error() string { return fmt.Sprintf("transfer impact assessment not found: %q", e.Identifier) } +type ErrNoTransferImpactAssessmentsFound struct{} + +func (e ErrNoTransferImpactAssessmentsFound) Error() string { + return "no transfer impact assessments found" +} + type ( TransferImpactAssessment struct { ID gid.GID `db:"id"` @@ -151,6 +157,56 @@ WHERE return nil } +func (tias *TransferImpactAssessments) LoadAllByOrganizationID( + ctx context.Context, + conn pg.Conn, + scope Scoper, + organizationID gid.GID, + filter *TransferImpactAssessmentFilter, +) error { + q := ` +SELECT + id, + snapshot_id, + source_id, + organization_id, + processing_activity_id, + data_subjects, + legal_mechanism, + transfer, + local_law_risk, + supplementary_measures, + created_at, + updated_at +FROM + processing_activity_transfer_impact_assessments +WHERE + %s + AND organization_id = @organization_id + AND %s +` + + q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment()) + + args := pgx.StrictNamedArgs{"organization_id": organizationID} + maps.Copy(args, scope.SQLArguments()) + maps.Copy(args, filter.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query transfer impact assessments: %w", err) + } + + results, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[TransferImpactAssessment]) + if err != nil { + return fmt.Errorf("cannot collect transfer impact assessments: %w", err) + } + + *tias = results + + return nil +} + func (tia *TransferImpactAssessment) LoadByID( ctx context.Context, conn pg.Conn, @@ -429,4 +485,3 @@ WHERE tia.tenant_id = @tenant_id AND tia.organization_id = @organization_id AND return nil } - diff --git a/pkg/coredata/transfer_impact_assessment_filter.go b/pkg/coredata/transfer_impact_assessment_filter.go index 23872839e..aa78d49b6 100644 --- a/pkg/coredata/transfer_impact_assessment_filter.go +++ b/pkg/coredata/transfer_impact_assessment_filter.go @@ -53,3 +53,9 @@ func (f *TransferImpactAssessmentFilter) SQLFragment() string { } } +func (f *TransferImpactAssessmentFilter) SnapshotID() *gid.GID { + if f.snapshotID == nil || *f.snapshotID == nil { + return nil + } + return *f.snapshotID +} diff --git a/pkg/coredata/vendor.go b/pkg/coredata/vendor.go index b948a4331..92464679e 100644 --- a/pkg/coredata/vendor.go +++ b/pkg/coredata/vendor.go @@ -21,10 +21,10 @@ import ( "maps" "time" - "go.probo.inc/probo/pkg/gid" - "go.probo.inc/probo/pkg/page" "github.com/jackc/pgx/v5" "go.gearno.de/kit/pg" + "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/page" ) type ( @@ -850,6 +850,74 @@ WHERE %s return nil } +func (v *Vendors) LoadAllByProcessingActivities( + ctx context.Context, + conn pg.Conn, + scope Scoper, + organizationID gid.GID, + filter *ProcessingActivityFilter, +) (map[gid.GID][]string, error) { + q := ` +WITH filtered_processing_activities AS ( + SELECT + pa.id + FROM + processing_activities pa + WHERE + pa.tenant_id = @tenant_id + AND pa.organization_id = @organization_id + AND %s +), +filtered_vendors AS ( + SELECT + v.id, + v.name + FROM + vendors v + WHERE + v.tenant_id = @tenant_id +) +SELECT + pav.processing_activity_id, + fv.name +FROM + processing_activity_vendors pav +INNER JOIN + filtered_vendors fv ON fv.id = pav.vendor_id +INNER JOIN + filtered_processing_activities fpa ON fpa.id = pav.processing_activity_id +WHERE + pav.tenant_id = @tenant_id +ORDER BY + pav.processing_activity_id, fv.name + ` + q = fmt.Sprintf(q, filter.SQLFragment()) + + args := pgx.StrictNamedArgs{ + "organization_id": organizationID, + } + maps.Copy(args, scope.SQLArguments()) + maps.Copy(args, filter.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return nil, fmt.Errorf("cannot query vendors: %w", err) + } + defer rows.Close() + + vendorMap := 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) + } + vendorMap[processingActivityID] = append(vendorMap[processingActivityID], vendorName) + } + + return vendorMap, nil +} + func (d Vendors) InsertDataSnapshots( ctx context.Context, conn pg.Conn, diff --git a/pkg/docgen/data_protection_impact_assessments_template.html b/pkg/docgen/data_protection_impact_assessments_template.html new file mode 100644 index 000000000..fb8557bc9 --- /dev/null +++ b/pkg/docgen/data_protection_impact_assessments_template.html @@ -0,0 +1,315 @@ + + + + + + Data Protection Impact Assessments Export + + + +
+
+ {{- if .CompanyHorizontalLogoBase64}} + {{imgTag .CompanyHorizontalLogoBase64 "Company Logo" "company-logo"}} + {{- else}} +
{{.CompanyName}}
+ {{- end}} +
+ +

Data Protection Impact Assessments

+ +
+ + + + + + + + + + + + + +
Classification + CONFIDENTIAL +
Version{{.Version}}
Published{{.PublishedAt.Format "January 2, 2006"}}
+
+ +
+
1. Purpose
+
+ This document contains Data Protection Impact Assessments (DPIAs) conducted for processing activities + that present a high risk to individuals' rights and freedoms. DPIAs are systematic assessments that + evaluate the necessity, proportionality, and risks associated with data processing operations, along + with the measures implemented to mitigate identified risks. +
+
+
+ + {{- range $index, $assessment := .Assessments}} +
+ {{if eq $index 0}} +

2. Records

+ {{end}} +

2.{{add $index 1}} {{$assessment.ProcessingActivityName}}

+ +
+
Description
+
{{if .Description}}{{.Description}}{{else}}Not specified{{end}}
+
+ +
+
Necessity and Proportionality
+
{{if .NecessityAndProportionality}}{{.NecessityAndProportionality}}{{else}}Not specified{{end}}
+
+ +
+
Potential Risk
+
{{if .PotentialRisk}}{{.PotentialRisk}}{{else}}Not specified{{end}}
+
+ +
+
Mitigations
+
{{if .Mitigations}}{{.Mitigations}}{{else}}Not specified{{end}}
+
+ +
+
Residual Risk
+
{{if .ResidualRisk}}{{.ResidualRisk | formatResidualRisk}}{{else}}Not specified{{end}}
+
+
+ {{- end}} + +
+

3. Annexes

+ +
+
3.1 Lexicon
+
+ +
+
Residual Risk
+
    +
  • + Low: + The residual risk after implementing mitigation measures is considered low. The processing activity poses minimal risk to individuals' rights and freedoms. +
  • +
  • + Medium: + The residual risk after implementing mitigation measures is considered medium. The processing activity poses a moderate risk to individuals' rights and freedoms, requiring ongoing monitoring. +
  • +
  • + High: + The residual risk after implementing mitigation measures is considered high. The processing activity poses significant risk to individuals' rights and freedoms, requiring enhanced safeguards and regular review. +
  • +
+
+
+ + diff --git a/pkg/docgen/generator.go b/pkg/docgen/generator.go index 371a14fa6..dbb4543b4 100644 --- a/pkg/docgen/generator.go +++ b/pkg/docgen/generator.go @@ -23,21 +23,36 @@ import ( "strings" "time" - "go.probo.inc/probo/pkg/coredata" "github.com/yuin/goldmark" "github.com/yuin/goldmark/extension" gmhtml "github.com/yuin/goldmark/renderer/html" + "go.probo.inc/probo/pkg/coredata" +) + +const ( + firstPageTOCItems = 21 + otherPageTOCItems = 28 ) var ( //go:embed template.html htmlTemplateContent string + //go:embed processing_activities_template.html + processingActivitiesTemplateContent string + + //go:embed data_protection_impact_assessments_template.html + dataProtectionImpactAssessmentsTemplateContent string + + //go:embed transfer_impact_assessments_template.html + transferImpactAssessmentsTemplateContent string + templateFuncs = template.FuncMap{ "now": func() time.Time { return time.Now() }, - "eq": func(a, b string) bool { return a == b }, + "eq": func(a, b any) bool { return a == b }, "string": func(v fmt.Stringer) string { return v.String() }, "lower": func(s string) string { return strings.ToLower(s) }, + "add": func(a, b int) int { return a + b }, "classificationString": func(c Classification) string { return string(c) }, "formatContent": func(content string) template.HTML { md := goldmark.New( @@ -56,9 +71,111 @@ var ( "imgTag": func(src, alt, class string) template.HTML { return template.HTML(fmt.Sprintf(`%s`, html.EscapeString(src), html.EscapeString(alt), html.EscapeString(class))) }, + "formatLawfulBasis": func(basis coredata.ProcessingActivityLawfulBasis) string { + switch basis { + case coredata.ProcessingActivityLawfulBasisConsent: + return "Consent" + case coredata.ProcessingActivityLawfulBasisContractualNecessity: + return "Contractual Necessity" + case coredata.ProcessingActivityLawfulBasisLegalObligation: + return "Legal Obligation" + case coredata.ProcessingActivityLawfulBasisLegitimateInterest: + return "Legitimate Interest" + case coredata.ProcessingActivityLawfulBasisPublicTask: + return "Public Task" + case coredata.ProcessingActivityLawfulBasisVitalInterests: + return "Vital Interests" + default: + return basis.String() + } + }, + "formatSpecialOrCriminalData": func(data coredata.ProcessingActivitySpecialOrCriminalDatum) string { + switch data { + case coredata.ProcessingActivitySpecialOrCriminalDatumYes: + return "Yes" + case coredata.ProcessingActivitySpecialOrCriminalDatumNo: + return "No" + case coredata.ProcessingActivitySpecialOrCriminalDatumPossible: + return "Possible" + default: + return data.String() + } + }, + "formatTransferSafeguard": func(safeguard *coredata.ProcessingActivityTransferSafeguard) string { + if safeguard == nil { + return "" + } + switch *safeguard { + case coredata.ProcessingActivityTransferSafeguardStandardContractualClauses: + return "Standard Contractual Clauses" + case coredata.ProcessingActivityTransferSafeguardBindingCorporateRules: + return "Binding Corporate Rules" + case coredata.ProcessingActivityTransferSafeguardAdequacyDecision: + return "Adequacy Decision" + case coredata.ProcessingActivityTransferSafeguardDerogations: + return "Derogations" + case coredata.ProcessingActivityTransferSafeguardCodesOfConduct: + return "Codes of Conduct" + case coredata.ProcessingActivityTransferSafeguardCertificationMechanisms: + return "Certification Mechanisms" + default: + return safeguard.String() + } + }, + "formatDPIANeeded": func(needed coredata.ProcessingActivityDataProtectionImpactAssessment) string { + switch needed { + case coredata.ProcessingActivityDataProtectionImpactAssessmentNeeded: + return "Yes" + case coredata.ProcessingActivityDataProtectionImpactAssessmentNotNeeded: + return "No" + default: + return needed.String() + } + }, + "formatTIANeeded": func(needed coredata.ProcessingActivityTransferImpactAssessment) string { + switch needed { + case coredata.ProcessingActivityTransferImpactAssessmentNeeded: + return "Yes" + case coredata.ProcessingActivityTransferImpactAssessmentNotNeeded: + return "No" + default: + return needed.String() + } + }, + "formatRole": func(role coredata.ProcessingActivityRole) string { + switch role { + case coredata.ProcessingActivityRoleController: + return "Controller" + case coredata.ProcessingActivityRoleProcessor: + return "Processor" + default: + return role.String() + } + }, + "formatResidualRisk": func(risk *coredata.DataProtectionImpactAssessmentResidualRisk) string { + if risk == nil { + return "" + } + switch *risk { + case coredata.DataProtectionImpactAssessmentResidualRiskLow: + return "Low" + case coredata.DataProtectionImpactAssessmentResidualRiskMedium: + return "Medium" + case coredata.DataProtectionImpactAssessmentResidualRiskHigh: + return "High" + default: + return risk.String() + } + }, } documentTemplate = template.Must(template.New("document").Funcs(templateFuncs).Parse(htmlTemplateContent)) + + processingActivitiesTemplate = template.Must(template.New("processingActivities").Funcs(templateFuncs).Parse(processingActivitiesTemplateContent)) + + dataProtectionImpactAssessmentsTemplate = template.Must(template.New("dataProtectionImpactAssessments").Funcs(templateFuncs).Parse(dataProtectionImpactAssessmentsTemplateContent)) + + transferImpactAssessmentsTemplate = template.Must(template.New("transferImpactAssessments").Funcs(templateFuncs).Parse(transferImpactAssessmentsTemplateContent)) ) type ( @@ -82,6 +199,71 @@ type ( State coredata.DocumentVersionSignatureState RequestedAt time.Time } + + ProcessingActivityTableData struct { + CompanyName string + CompanyHorizontalLogoBase64 string + Version int + PublishedAt time.Time + Activities []ProcessingActivityRowData + } + + ProcessingActivityRowData struct { + Name string + Purpose *string + DataSubjectCategory *string + PersonalDataCategory *string + SpecialOrCriminalData coredata.ProcessingActivitySpecialOrCriminalDatum + ConsentEvidenceLink *string + LawfulBasis coredata.ProcessingActivityLawfulBasis + Recipients *string + Location *string + InternationalTransfers bool + TransferSafeguards *coredata.ProcessingActivityTransferSafeguard + RetentionPeriod *string + SecurityMeasures *string + DataProtectionImpactAssessmentNeeded coredata.ProcessingActivityDataProtectionImpactAssessment + TransferImpactAssessmentNeeded coredata.ProcessingActivityTransferImpactAssessment + LastReviewDate *time.Time + NextReviewDate *time.Time + Role coredata.ProcessingActivityRole + DataProtectionOfficerFullName *string + Vendors string + } + + DataProtectionImpactAssessmentTableData struct { + CompanyName string + CompanyHorizontalLogoBase64 string + Version int + PublishedAt time.Time + Assessments []DataProtectionImpactAssessmentRowData + } + + DataProtectionImpactAssessmentRowData struct { + ProcessingActivityName string + Description *string + NecessityAndProportionality *string + PotentialRisk *string + Mitigations *string + ResidualRisk *coredata.DataProtectionImpactAssessmentResidualRisk + } + + TransferImpactAssessmentTableData struct { + CompanyName string + CompanyHorizontalLogoBase64 string + Version int + PublishedAt time.Time + Assessments []TransferImpactAssessmentRowData + } + + TransferImpactAssessmentRowData struct { + ProcessingActivityName string + DataSubjects *string + LegalMechanism *string + Transfer *string + LocalLawRisk *string + SupplementaryMeasures *string + } ) const ( @@ -99,3 +281,30 @@ func RenderHTML(data DocumentData) ([]byte, error) { return buf.Bytes(), nil } + +func RenderProcessingActivitiesTableHTML(data ProcessingActivityTableData) ([]byte, error) { + var buf bytes.Buffer + if err := processingActivitiesTemplate.Execute(&buf, data); err != nil { + return nil, fmt.Errorf("cannot execute processing activities template: %w", err) + } + + return buf.Bytes(), nil +} + +func RenderDataProtectionImpactAssessmentsTableHTML(data DataProtectionImpactAssessmentTableData) ([]byte, error) { + var buf bytes.Buffer + if err := dataProtectionImpactAssessmentsTemplate.Execute(&buf, data); err != nil { + return nil, fmt.Errorf("cannot execute data protection impact assessments template: %w", err) + } + + return buf.Bytes(), nil +} + +func RenderTransferImpactAssessmentsTableHTML(data TransferImpactAssessmentTableData) ([]byte, error) { + var buf bytes.Buffer + if err := transferImpactAssessmentsTemplate.Execute(&buf, data); err != nil { + return nil, fmt.Errorf("cannot execute transfer impact assessments template: %w", err) + } + + return buf.Bytes(), nil +} diff --git a/pkg/docgen/generator_test.go b/pkg/docgen/generator_test.go index b05f6458b..592f4a5f7 100644 --- a/pkg/docgen/generator_test.go +++ b/pkg/docgen/generator_test.go @@ -195,7 +195,7 @@ func TestTemplateFunctions(t *testing.T) { }) t.Run("eq function", func(t *testing.T) { - eqFunc := templateFuncs["eq"].(func(string, string) bool) + eqFunc := templateFuncs["eq"].(func(any, any) bool) assert.True(t, eqFunc("test", "test")) assert.False(t, eqFunc("test", "other")) }) @@ -266,7 +266,7 @@ func TestHTMLEscaping(t *testing.T) { resultStr := string(result) - // Verify dangerous content is escaped + // Verify dangerous content is escaped assert.NotContains(t, resultStr, "") assert.NotContains(t, resultStr, "tag") assert.Contains(t, resultStr, "<script>") diff --git a/pkg/docgen/processing_activities_template.html b/pkg/docgen/processing_activities_template.html new file mode 100644 index 000000000..7bd07f0e4 --- /dev/null +++ b/pkg/docgen/processing_activities_template.html @@ -0,0 +1,535 @@ + + + + + + Processing Activities Export + + + +
+
+ {{- if .CompanyHorizontalLogoBase64}} + {{imgTag .CompanyHorizontalLogoBase64 "Company Logo" "company-logo"}} + {{- else}} +
{{.CompanyName}}
+ {{- end}} +
+ +

Processing Activities

+ +
+ + + + + + + + + + + + + +
Classification + CONFIDENTIAL +
Version{{.Version}}
Published{{.PublishedAt.Format "January 2, 2006"}}
+
+ +
+
1. Purpose
+
+ This document provides a comprehensive overview of all processing activities within the organization. + It serves as a record of personal data processing operations, documenting the purposes, legal bases, + data categories, and associated safeguards for each activity. +
+
+
+ + {{- range $index, $activity := .Activities}} +
+ {{if eq $index 0}} +

2. Records

+ {{end}} +

2.{{add $index 1}} {{$activity.Name}}

+ +
+
2.{{add $index 1}}.1 General Information
+
+
Purpose
+
{{if .Purpose}}{{.Purpose}}{{else}}Not specified{{end}}
+
+
+
+
Role
+
{{.Role | formatRole}}
+
+
+
+ +
+
2.{{add $index 1}}.2 Data Categories
+
+
+
Data Subject Category
+
{{if .DataSubjectCategory}}{{.DataSubjectCategory}}{{else}}Not specified{{end}}
+
+
+
Personal Data Category
+
{{if .PersonalDataCategory}}{{.PersonalDataCategory}}{{else}}Not specified{{end}}
+
+
+
Special/Criminal Data
+
{{.SpecialOrCriminalData | formatSpecialOrCriminalData}}
+
+
+
+ +
+
2.{{add $index 1}}.3 Legal Basis
+
+
+
Lawful Basis
+
{{.LawfulBasis | formatLawfulBasis}}
+
+
+
Consent Evidence Link
+
{{if .ConsentEvidenceLink}}{{.ConsentEvidenceLink}}{{else}}Not specified{{end}}
+
+
+
+ +
+
2.{{add $index 1}}.4 Data Sharing & Transfers
+
+
+
Recipients
+
{{if .Recipients}}{{.Recipients}}{{else}}Not specified{{end}}
+
+
+
Location
+
{{if .Location}}{{.Location}}{{else}}Not specified{{end}}
+
+
+
International Transfers
+
{{if .InternationalTransfers}}Yes{{else}}No{{end}}
+
+
+
Transfer Safeguards
+
{{if .TransferSafeguards}}{{.TransferSafeguards | formatTransferSafeguard}}{{else}}Not specified{{end}}
+
+
+
+ +
+
2.{{add $index 1}}.5 Retention & Security
+
+
+
Retention Period
+
{{if .RetentionPeriod}}{{.RetentionPeriod}}{{else}}Not specified{{end}}
+
+
+
+
Security Measures
+
{{if .SecurityMeasures}}{{.SecurityMeasures}}{{else}}Not specified{{end}}
+
+
+ +
+
2.{{add $index 1}}.6 Assessments & Reviews
+
+
+
DPIA Needed
+
{{.DataProtectionImpactAssessmentNeeded | formatDPIANeeded}}
+
+
+
TIA Needed
+
{{.TransferImpactAssessmentNeeded | formatTIANeeded}}
+
+
+
Last Review Date
+
{{if .LastReviewDate}}{{.LastReviewDate.Format "January 2, 2006"}}{{else}}Not specified{{end}}
+
+
+
Next Review Date
+
{{if .NextReviewDate}}{{.NextReviewDate.Format "January 2, 2006"}}{{else}}Not specified{{end}}
+
+
+
+ +
+
2.{{add $index 1}}.7 Responsible Parties
+
+
+
Data Protection Officer
+
{{if .DataProtectionOfficerFullName}}{{.DataProtectionOfficerFullName}}{{else}}Not assigned{{end}}
+
+
+
Vendors
+
{{if .Vendors}}{{.Vendors}}{{else}}None{{end}}
+
+
+
+
+ {{- end}} + +
+

3. Annexes

+ +
+
3.1 Lexicon
+
+ +
+
Role
+
    +
  • + Controller: + The entity that determines the purposes and means of processing personal data. +
  • +
  • + Processor: + The entity that processes personal data on behalf of the controller. +
  • +
+
+ +
+
Lawful Basis
+
    +
  • + Consent: + The data subject has given consent to the processing of their personal data. +
  • +
  • + Contractual Necessity: + Processing is necessary for the performance of a contract to which the data subject is party. +
  • +
  • + Legal Obligation: + Processing is necessary for compliance with a legal obligation to which the controller is subject. +
  • +
  • + Legitimate Interest: + Processing is necessary for the purposes of the legitimate interests pursued by the controller or a third party. +
  • +
  • + Public Task: + Processing is necessary for the performance of a task carried out in the public interest or in the exercise of official authority. +
  • +
  • + Vital Interests: + Processing is necessary to protect the vital interests of the data subject or of another natural person. +
  • +
+
+ +
+
Special/Criminal Data
+
    +
  • + Yes: + The processing activity involves special categories of personal data or data relating to criminal convictions and offences. +
  • +
  • + No: + The processing activity does not involve special categories of personal data or data relating to criminal convictions and offences. +
  • +
  • + Possible: + The processing activity may involve special categories of personal data or data relating to criminal convictions and offences. +
  • +
+
+ +
+
Transfer Safeguards
+
    +
  • + Standard Contractual Clauses: + Standard contractual clauses approved by the European Commission are used to ensure adequate protection for international transfers. +
  • +
  • + Binding Corporate Rules: + Binding corporate rules approved by a supervisory authority are used to ensure adequate protection for international transfers. +
  • +
  • + Adequacy Decision: + The European Commission has determined that the third country ensures an adequate level of protection. +
  • +
  • + Derogations: + A derogation under Article 49 of the GDPR is used for the international transfer. +
  • +
  • + Codes of Conduct: + An approved code of conduct together with binding and enforceable commitments is used to ensure adequate protection. +
  • +
  • + Certification Mechanisms: + An approved certification mechanism together with binding and enforceable commitments is used to ensure adequate protection. +
  • +
+
+ +
+
DPIA Needed
+
    +
  • + Yes: + A Data Protection Impact Assessment is required for this processing activity. +
  • +
  • + No: + A Data Protection Impact Assessment is not required for this processing activity. +
  • +
+
+ +
+
TIA Needed
+
    +
  • + Yes: + A Transfer Impact Assessment is required for this processing activity. +
  • +
  • + No: + A Transfer Impact Assessment is not required for this processing activity. +
  • +
+
+
+ + diff --git a/pkg/docgen/transfer_impact_assessments_template.html b/pkg/docgen/transfer_impact_assessments_template.html new file mode 100644 index 000000000..233db42f4 --- /dev/null +++ b/pkg/docgen/transfer_impact_assessments_template.html @@ -0,0 +1,288 @@ + + + + + + Transfer Impact Assessments Export + + + +
+
+ {{- if .CompanyHorizontalLogoBase64}} + {{imgTag .CompanyHorizontalLogoBase64 "Company Logo" "company-logo"}} + {{- else}} +
{{.CompanyName}}
+ {{- end}} +
+ +

Transfer Impact Assessments

+ +
+ + + + + + + + + + + + + +
Classification + CONFIDENTIAL +
Version{{.Version}}
Published{{.PublishedAt.Format "January 2, 2006"}}
+
+ +
+
1. Purpose
+
+ This document contains Transfer Impact Assessments (TIAs) conducted for processing activities involving + international transfers of personal data to countries outside the European Economic Area (EEA). TIAs + evaluate the legal mechanisms used for transfers, assess risks related to local laws in destination + countries, and document supplementary measures implemented to ensure an adequate level of data protection. +
+
+
+ + {{- range $index, $assessment := .Assessments}} +
+ {{if eq $index 0}} +

2. Records

+ {{end}} +

2.{{add $index 1}} {{$assessment.ProcessingActivityName}}

+ +
+
Data Subjects
+
{{if .DataSubjects}}{{.DataSubjects}}{{else}}Not specified{{end}}
+
+ +
+
Transfer
+
{{if .Transfer}}{{.Transfer}}{{else}}Not specified{{end}}
+
+ +
+
Legal Mechanism
+
{{if .LegalMechanism}}{{.LegalMechanism}}{{else}}Not specified{{end}}
+
+ +
+
Local Law Risk
+
{{if .LocalLawRisk}}{{.LocalLawRisk}}{{else}}Not specified{{end}}
+
+ +
+
Supplementary Measures
+
{{if .SupplementaryMeasures}}{{.SupplementaryMeasures}}{{else}}Not specified{{end}}
+
+
+ {{- end}} + + diff --git a/pkg/probo/data_protection_impact_assessment_service.go b/pkg/probo/data_protection_impact_assessment_service.go index e9efee3f2..e57810ca2 100644 --- a/pkg/probo/data_protection_impact_assessment_service.go +++ b/pkg/probo/data_protection_impact_assessment_service.go @@ -17,17 +17,21 @@ package probo import ( "context" "fmt" + "io" "time" "go.gearno.de/kit/pg" "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/docgen" "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/html2pdf" "go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/validator" ) type DataProtectionImpactAssessmentService struct { - svc *TenantService + svc *TenantService + html2pdfConverter *html2pdf.Converter } type ( @@ -298,3 +302,130 @@ func (s *DataProtectionImpactAssessmentService) Delete( return err } +func (s *DataProtectionImpactAssessmentService) ExportPDF( + ctx context.Context, + organizationID gid.GID, + filter *coredata.DataProtectionImpactAssessmentFilter, +) ([]byte, error) { + var tableData docgen.DataProtectionImpactAssessmentTableData + + err := s.svc.pg.WithTx( + ctx, + func(conn pg.Conn) error { + var assessments coredata.DataProtectionImpactAssessments + if err := assessments.LoadAllByOrganizationID(ctx, conn, s.svc.scope, organizationID, filter); err != nil { + return fmt.Errorf("cannot load data protection impact assessments: %w", err) + } + + if len(assessments) == 0 { + return &coredata.ErrNoDataProtectionImpactAssessmentsFound{} + } + + organization := &coredata.Organization{} + if err := organization.LoadByID(ctx, conn, s.svc.scope, organizationID); err != nil { + return fmt.Errorf("cannot load organization: %w", err) + } + + horizontalLogoBase64 := "" + if organization.HorizontalLogoFileID != nil { + fileRecord := &coredata.File{} + fileErr := s.svc.pg.WithConn(ctx, func(conn pg.Conn) error { + return fileRecord.LoadByID(ctx, conn, s.svc.scope, *organization.HorizontalLogoFileID) + }) + if fileErr == nil { + base64Data, mimeType, logoErr := s.svc.fileManager.GetFileBase64(ctx, fileRecord) + if logoErr == nil { + horizontalLogoBase64 = fmt.Sprintf("data:%s;base64,%s", mimeType, base64Data) + } + } + } + + var snapshots coredata.Snapshots + snapshotType := coredata.SnapshotsTypeProcessingActivities + + var version int + var publishedAt time.Time + + if snapshotID := filter.SnapshotID(); snapshotID != nil { + snapshot := &coredata.Snapshot{} + if err := snapshot.LoadByID(ctx, conn, s.svc.scope, *snapshotID); err != nil { + return fmt.Errorf("cannot load snapshot: %w", err) + } + publishedAt = snapshot.CreatedAt + snapshotFilter := coredata.NewSnapshotFilter(&snapshotType).WithBeforeDate(&snapshot.CreatedAt) + snapshotCount, err := snapshots.CountByOrganizationID(ctx, conn, s.svc.scope, organizationID, snapshotFilter) + if err != nil { + return fmt.Errorf("cannot count processing activities snapshots: %w", err) + } + version = snapshotCount + } else { + publishedAt = time.Now() + snapshotFilter := coredata.NewSnapshotFilter(&snapshotType) + snapshotCount, err := snapshots.CountByOrganizationID(ctx, conn, s.svc.scope, organizationID, snapshotFilter) + if err != nil { + return fmt.Errorf("cannot count processing activities snapshots: %w", err) + } + version = snapshotCount + 1 + } + + assessmentRows := make([]docgen.DataProtectionImpactAssessmentRowData, len(assessments)) + for i, assessment := range assessments { + processingActivity := &coredata.ProcessingActivity{} + if err := processingActivity.LoadByID(ctx, conn, s.svc.scope, assessment.ProcessingActivityID); err != nil { + return fmt.Errorf("cannot load processing activity: %w", err) + } + + assessmentRows[i] = docgen.DataProtectionImpactAssessmentRowData{ + ProcessingActivityName: processingActivity.Name, + Description: assessment.Description, + NecessityAndProportionality: assessment.NecessityAndProportionality, + PotentialRisk: assessment.PotentialRisk, + Mitigations: assessment.Mitigations, + ResidualRisk: assessment.ResidualRisk, + } + } + + tableData = docgen.DataProtectionImpactAssessmentTableData{ + CompanyName: organization.Name, + CompanyHorizontalLogoBase64: horizontalLogoBase64, + Version: version, + PublishedAt: publishedAt, + Assessments: assessmentRows, + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + htmlContent, err := docgen.RenderDataProtectionImpactAssessmentsTableHTML(tableData) + if err != nil { + return nil, fmt.Errorf("cannot render HTML: %w", err) + } + + cfg := html2pdf.RenderConfig{ + PageFormat: html2pdf.PageFormatA4, + Orientation: html2pdf.OrientationPortrait, + MarginTop: html2pdf.NewMarginInches(0.98), + MarginBottom: html2pdf.NewMarginInches(0.98), + MarginLeft: html2pdf.NewMarginInches(0.98), + MarginRight: html2pdf.NewMarginInches(0.98), + PrintBackground: true, + Scale: 1.0, + } + + pdfReader, err := s.html2pdfConverter.GeneratePDF(ctx, htmlContent, cfg) + if err != nil { + return nil, fmt.Errorf("cannot generate PDF: %w", err) + } + + pdfData, err := io.ReadAll(pdfReader) + if err != nil { + return nil, fmt.Errorf("cannot read PDF data: %w", err) + } + + return pdfData, nil +} diff --git a/pkg/probo/processing_activity_service.go b/pkg/probo/processing_activity_service.go index dfdf480c0..584ac1914 100644 --- a/pkg/probo/processing_activity_service.go +++ b/pkg/probo/processing_activity_service.go @@ -17,17 +17,22 @@ package probo import ( "context" "fmt" + "io" + "strings" "time" "go.gearno.de/kit/pg" "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/docgen" "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/html2pdf" "go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/validator" ) type ProcessingActivityService struct { - svc *TenantService + svc *TenantService + html2pdfConverter *html2pdf.Converter } type ( @@ -382,3 +387,159 @@ func (s ProcessingActivityService) CountForOrganizationID( return count, nil } + +func (s *ProcessingActivityService) ExportPDF( + ctx context.Context, + organizationID gid.GID, + filter *coredata.ProcessingActivityFilter, +) ([]byte, error) { + var tableData docgen.ProcessingActivityTableData + + err := s.svc.pg.WithTx( + ctx, + func(conn pg.Conn) error { + var processingActivities coredata.ProcessingActivities + if err := processingActivities.LoadAllByOrganizationID(ctx, conn, s.svc.scope, organizationID, filter); err != nil { + return fmt.Errorf("cannot load processing activities: %w", err) + } + + if len(processingActivities) == 0 { + return &coredata.ErrNoProcessingActivitiesFound{} + } + + organization := &coredata.Organization{} + if err := organization.LoadByID(ctx, conn, s.svc.scope, organizationID); err != nil { + return fmt.Errorf("cannot load organization: %w", err) + } + + horizontalLogoBase64 := "" + if organization.HorizontalLogoFileID != nil { + fileRecord := &coredata.File{} + fileErr := s.svc.pg.WithConn(ctx, func(conn pg.Conn) error { + return fileRecord.LoadByID(ctx, conn, s.svc.scope, *organization.HorizontalLogoFileID) + }) + if fileErr == nil { + base64Data, mimeType, logoErr := s.svc.fileManager.GetFileBase64(ctx, fileRecord) + if logoErr == nil { + horizontalLogoBase64 = fmt.Sprintf("data:%s;base64,%s", mimeType, base64Data) + } + } + } + + var vendors coredata.Vendors + vendorMap, err := vendors.LoadAllByProcessingActivities(ctx, conn, s.svc.scope, organizationID, filter) + if err != nil { + return fmt.Errorf("cannot load vendors: %w", err) + } + + var snapshots coredata.Snapshots + snapshotType := coredata.SnapshotsTypeProcessingActivities + + var version int + var publishedAt time.Time + + if snapshotID := filter.SnapshotID(); snapshotID != nil { + snapshot := &coredata.Snapshot{} + if err := snapshot.LoadByID(ctx, conn, s.svc.scope, *snapshotID); err != nil { + return fmt.Errorf("cannot load snapshot: %w", err) + } + publishedAt = snapshot.CreatedAt + snapshotFilter := coredata.NewSnapshotFilter(&snapshotType).WithBeforeDate(&snapshot.CreatedAt) + snapshotCount, err := snapshots.CountByOrganizationID(ctx, conn, s.svc.scope, organizationID, snapshotFilter) + if err != nil { + return fmt.Errorf("cannot count processing activities snapshots: %w", err) + } + version = snapshotCount + } else { + publishedAt = time.Now() + snapshotFilter := coredata.NewSnapshotFilter(&snapshotType) + snapshotCount, err := snapshots.CountByOrganizationID(ctx, conn, s.svc.scope, organizationID, snapshotFilter) + if err != nil { + return fmt.Errorf("cannot count processing activities snapshots: %w", err) + } + version = snapshotCount + 1 + } + + activities := make([]docgen.ProcessingActivityRowData, len(processingActivities)) + for i, pa := range processingActivities { + dpoFullName := (*string)(nil) + if pa.DataProtectionOfficerID != nil { + dpo := &coredata.People{} + if err := dpo.LoadByID(ctx, conn, s.svc.scope, *pa.DataProtectionOfficerID); err == nil { + dpoFullName = &dpo.FullName + } + } + + vendorsList := "" + if vendorNames, ok := vendorMap[pa.ID]; ok && len(vendorNames) > 0 { + vendorsList = strings.Join(vendorNames, ", ") + } + + activities[i] = docgen.ProcessingActivityRowData{ + Name: pa.Name, + Purpose: pa.Purpose, + DataSubjectCategory: pa.DataSubjectCategory, + PersonalDataCategory: pa.PersonalDataCategory, + SpecialOrCriminalData: pa.SpecialOrCriminalData, + ConsentEvidenceLink: pa.ConsentEvidenceLink, + LawfulBasis: pa.LawfulBasis, + Recipients: pa.Recipients, + Location: pa.Location, + InternationalTransfers: pa.InternationalTransfers, + TransferSafeguards: pa.TransferSafeguard, + RetentionPeriod: pa.RetentionPeriod, + SecurityMeasures: pa.SecurityMeasures, + DataProtectionImpactAssessmentNeeded: pa.DataProtectionImpactAssessmentNeeded, + TransferImpactAssessmentNeeded: pa.TransferImpactAssessmentNeeded, + LastReviewDate: pa.LastReviewDate, + NextReviewDate: pa.NextReviewDate, + Role: pa.Role, + DataProtectionOfficerFullName: dpoFullName, + Vendors: vendorsList, + } + } + + tableData = docgen.ProcessingActivityTableData{ + CompanyName: organization.Name, + CompanyHorizontalLogoBase64: horizontalLogoBase64, + Version: version, + PublishedAt: publishedAt, + Activities: activities, + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + htmlContent, err := docgen.RenderProcessingActivitiesTableHTML(tableData) + if err != nil { + return nil, fmt.Errorf("cannot generate HTML: %w", err) + } + + cfg := html2pdf.RenderConfig{ + PageFormat: html2pdf.PageFormatA4, + Orientation: html2pdf.OrientationPortrait, + MarginTop: html2pdf.NewMarginInches(0.98), + MarginBottom: html2pdf.NewMarginInches(0.98), + MarginLeft: html2pdf.NewMarginInches(0.98), + MarginRight: html2pdf.NewMarginInches(0.98), + PrintBackground: true, + Scale: 1.0, + } + + pdfReader, err := s.html2pdfConverter.GeneratePDF(ctx, htmlContent, cfg) + if err != nil { + return nil, fmt.Errorf("cannot generate PDF: %w", err) + } + + pdfData, err := io.ReadAll(pdfReader) + if err != nil { + return nil, fmt.Errorf("cannot read PDF data: %w", err) + } + + return pdfData, nil +} diff --git a/pkg/probo/service.go b/pkg/probo/service.go index caa326409..4fa8327db 100644 --- a/pkg/probo/service.go +++ b/pkg/probo/service.go @@ -252,9 +252,18 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService { tenantService.Snapshots = &SnapshotService{svc: tenantService} tenantService.ContinualImprovements = &ContinualImprovementService{svc: tenantService} tenantService.RightsRequests = &RightsRequestService{svc: tenantService} - tenantService.ProcessingActivities = &ProcessingActivityService{svc: tenantService} - tenantService.DataProtectionImpactAssessments = &DataProtectionImpactAssessmentService{svc: tenantService} - tenantService.TransferImpactAssessments = &TransferImpactAssessmentService{svc: tenantService} + tenantService.ProcessingActivities = &ProcessingActivityService{ + svc: tenantService, + html2pdfConverter: s.html2pdfConverter, + } + tenantService.DataProtectionImpactAssessments = &DataProtectionImpactAssessmentService{ + svc: tenantService, + html2pdfConverter: s.html2pdfConverter, + } + tenantService.TransferImpactAssessments = &TransferImpactAssessmentService{ + svc: tenantService, + html2pdfConverter: s.html2pdfConverter, + } tenantService.Files = &FileService{svc: tenantService} tenantService.CustomDomains = &CustomDomainService{ svc: tenantService, diff --git a/pkg/probo/snapshot_service.go b/pkg/probo/snapshot_service.go index 28d9013e3..0742f572f 100644 --- a/pkg/probo/snapshot_service.go +++ b/pkg/probo/snapshot_service.go @@ -192,7 +192,8 @@ func (s *SnapshotService) CountForOrganizationID( ctx, func(conn pg.Conn) (err error) { snapshots := coredata.Snapshots{} - count, err = snapshots.CountByOrganizationID(ctx, conn, s.svc.scope, organizationID) + filter := coredata.NewSnapshotFilter(nil) + count, err = snapshots.CountByOrganizationID(ctx, conn, s.svc.scope, organizationID, filter) if err != nil { return fmt.Errorf("cannot count snapshots: %w", err) } diff --git a/pkg/probo/transfer_impact_assessment_service.go b/pkg/probo/transfer_impact_assessment_service.go index 8668b1c7a..ed3563700 100644 --- a/pkg/probo/transfer_impact_assessment_service.go +++ b/pkg/probo/transfer_impact_assessment_service.go @@ -17,17 +17,21 @@ package probo import ( "context" "fmt" + "io" "time" "go.gearno.de/kit/pg" "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/docgen" "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/html2pdf" "go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/validator" ) type TransferImpactAssessmentService struct { - svc *TenantService + svc *TenantService + html2pdfConverter *html2pdf.Converter } type ( @@ -298,3 +302,130 @@ func (s *TransferImpactAssessmentService) Delete( return err } +func (s *TransferImpactAssessmentService) ExportPDF( + ctx context.Context, + organizationID gid.GID, + filter *coredata.TransferImpactAssessmentFilter, +) ([]byte, error) { + var tableData docgen.TransferImpactAssessmentTableData + + err := s.svc.pg.WithTx( + ctx, + func(conn pg.Conn) error { + var assessments coredata.TransferImpactAssessments + if err := assessments.LoadAllByOrganizationID(ctx, conn, s.svc.scope, organizationID, filter); err != nil { + return fmt.Errorf("cannot load transfer impact assessments: %w", err) + } + + if len(assessments) == 0 { + return &coredata.ErrNoTransferImpactAssessmentsFound{} + } + + organization := &coredata.Organization{} + if err := organization.LoadByID(ctx, conn, s.svc.scope, organizationID); err != nil { + return fmt.Errorf("cannot load organization: %w", err) + } + + horizontalLogoBase64 := "" + if organization.HorizontalLogoFileID != nil { + fileRecord := &coredata.File{} + fileErr := s.svc.pg.WithConn(ctx, func(conn pg.Conn) error { + return fileRecord.LoadByID(ctx, conn, s.svc.scope, *organization.HorizontalLogoFileID) + }) + if fileErr == nil { + base64Data, mimeType, logoErr := s.svc.fileManager.GetFileBase64(ctx, fileRecord) + if logoErr == nil { + horizontalLogoBase64 = fmt.Sprintf("data:%s;base64,%s", mimeType, base64Data) + } + } + } + + var snapshots coredata.Snapshots + snapshotType := coredata.SnapshotsTypeProcessingActivities + + var version int + var publishedAt time.Time + + if snapshotID := filter.SnapshotID(); snapshotID != nil { + snapshot := &coredata.Snapshot{} + if err := snapshot.LoadByID(ctx, conn, s.svc.scope, *snapshotID); err != nil { + return fmt.Errorf("cannot load snapshot: %w", err) + } + publishedAt = snapshot.CreatedAt + snapshotFilter := coredata.NewSnapshotFilter(&snapshotType).WithBeforeDate(&snapshot.CreatedAt) + snapshotCount, err := snapshots.CountByOrganizationID(ctx, conn, s.svc.scope, organizationID, snapshotFilter) + if err != nil { + return fmt.Errorf("cannot count processing activities snapshots: %w", err) + } + version = snapshotCount + } else { + publishedAt = time.Now() + snapshotFilter := coredata.NewSnapshotFilter(&snapshotType) + snapshotCount, err := snapshots.CountByOrganizationID(ctx, conn, s.svc.scope, organizationID, snapshotFilter) + if err != nil { + return fmt.Errorf("cannot count processing activities snapshots: %w", err) + } + version = snapshotCount + 1 + } + + assessmentRows := make([]docgen.TransferImpactAssessmentRowData, len(assessments)) + for i, assessment := range assessments { + processingActivity := &coredata.ProcessingActivity{} + if err := processingActivity.LoadByID(ctx, conn, s.svc.scope, assessment.ProcessingActivityID); err != nil { + return fmt.Errorf("cannot load processing activity: %w", err) + } + + assessmentRows[i] = docgen.TransferImpactAssessmentRowData{ + ProcessingActivityName: processingActivity.Name, + DataSubjects: assessment.DataSubjects, + LegalMechanism: assessment.LegalMechanism, + Transfer: assessment.Transfer, + LocalLawRisk: assessment.LocalLawRisk, + SupplementaryMeasures: assessment.SupplementaryMeasures, + } + } + + tableData = docgen.TransferImpactAssessmentTableData{ + CompanyName: organization.Name, + CompanyHorizontalLogoBase64: horizontalLogoBase64, + Version: version, + PublishedAt: publishedAt, + Assessments: assessmentRows, + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + htmlContent, err := docgen.RenderTransferImpactAssessmentsTableHTML(tableData) + if err != nil { + return nil, fmt.Errorf("cannot render HTML: %w", err) + } + + cfg := html2pdf.RenderConfig{ + PageFormat: html2pdf.PageFormatA4, + Orientation: html2pdf.OrientationPortrait, + MarginTop: html2pdf.NewMarginInches(0.98), + MarginBottom: html2pdf.NewMarginInches(0.98), + MarginLeft: html2pdf.NewMarginInches(0.98), + MarginRight: html2pdf.NewMarginInches(0.98), + PrintBackground: true, + Scale: 1.0, + } + + pdfReader, err := s.html2pdfConverter.GeneratePDF(ctx, htmlContent, cfg) + if err != nil { + return nil, fmt.Errorf("cannot generate PDF: %w", err) + } + + pdfData, err := io.ReadAll(pdfReader) + if err != nil { + return nil, fmt.Errorf("cannot read PDF data: %w", err) + } + + return pdfData, nil +} diff --git a/pkg/server/api/console/v1/schema.graphql b/pkg/server/api/console/v1/schema.graphql index 289b40c70..4f12e87a2 100644 --- a/pkg/server/api/console/v1/schema.graphql +++ b/pkg/server/api/console/v1/schema.graphql @@ -3310,6 +3310,15 @@ type Mutation { exportSignableVersionDocumentPDF( input: ExportSignableDocumentVersionPDFInput! ): ExportSignableDocumentVersionPDFPayload! + exportProcessingActivitiesPDF( + input: ExportProcessingActivitiesPDFInput! + ): ExportProcessingActivitiesPDFPayload! + exportDataProtectionImpactAssessmentsPDF( + input: ExportDataProtectionImpactAssessmentsPDFInput! + ): ExportDataProtectionImpactAssessmentsPDFPayload! + exportTransferImpactAssessmentsPDF( + input: ExportTransferImpactAssessmentsPDFInput! + ): ExportTransferImpactAssessmentsPDFPayload! createVendorRiskAssessment( input: CreateVendorRiskAssessmentInput! ): CreateVendorRiskAssessmentPayload! @@ -3941,6 +3950,21 @@ input ExportSignableDocumentVersionPDFInput { documentVersionId: ID! } +input ExportProcessingActivitiesPDFInput { + organizationId: ID! + filter: ProcessingActivityFilter +} + +input ExportDataProtectionImpactAssessmentsPDFInput { + organizationId: ID! + filter: DataProtectionImpactAssessmentFilter +} + +input ExportTransferImpactAssessmentsPDFInput { + organizationId: ID! + filter: TransferImpactAssessmentFilter +} + input DeleteDocumentInput { documentId: ID! } @@ -4600,6 +4624,18 @@ type ExportSignableDocumentVersionPDFPayload { data: String! } +type ExportProcessingActivitiesPDFPayload { + data: String! +} + +type ExportDataProtectionImpactAssessmentsPDFPayload { + data: String! +} + +type ExportTransferImpactAssessmentsPDFPayload { + data: String! +} + type UpdateDocumentPayload { document: Document! } diff --git a/pkg/server/api/console/v1/schema/schema.go b/pkg/server/api/console/v1/schema/schema.go index a1369b395..b4435ab08 100644 --- a/pkg/server/api/console/v1/schema/schema.go +++ b/pkg/server/api/console/v1/schema/schema.go @@ -779,6 +779,10 @@ type ComplexityRoot struct { Node func(childComplexity int) int } + ExportDataProtectionImpactAssessmentsPDFPayload struct { + Data func(childComplexity int) int + } + ExportDocumentVersionPDFPayload struct { Data func(childComplexity int) int } @@ -787,10 +791,18 @@ type ComplexityRoot struct { ExportJobID func(childComplexity int) int } + ExportProcessingActivitiesPDFPayload struct { + Data func(childComplexity int) int + } + ExportSignableDocumentVersionPDFPayload struct { Data func(childComplexity int) int } + ExportTransferImpactAssessmentsPDFPayload struct { + Data func(childComplexity int) int + } + File struct { CreatedAt func(childComplexity int) int DownloadURL func(childComplexity int) int @@ -951,151 +963,154 @@ type ComplexityRoot struct { } Mutation struct { - AcceptInvitation func(childComplexity int, input types.AcceptInvitationInput) int - AssessVendor func(childComplexity int, input types.AssessVendorInput) int - BulkDeleteDocuments func(childComplexity int, input types.BulkDeleteDocumentsInput) int - BulkExportDocuments func(childComplexity int, input types.BulkExportDocumentsInput) int - BulkPublishDocumentVersions func(childComplexity int, input types.BulkPublishDocumentVersionsInput) int - BulkRequestSignatures func(childComplexity int, input types.BulkRequestSignaturesInput) int - CancelSignatureRequest func(childComplexity int, input types.CancelSignatureRequestInput) int - ConfirmEmail func(childComplexity int, input types.ConfirmEmailInput) int - CreateAsset func(childComplexity int, input types.CreateAssetInput) int - CreateAudit func(childComplexity int, input types.CreateAuditInput) int - CreateContinualImprovement func(childComplexity int, input types.CreateContinualImprovementInput) int - CreateControl func(childComplexity int, input types.CreateControlInput) int - CreateControlAuditMapping func(childComplexity int, input types.CreateControlAuditMappingInput) int - CreateControlDocumentMapping func(childComplexity int, input types.CreateControlDocumentMappingInput) int - CreateControlMeasureMapping func(childComplexity int, input types.CreateControlMeasureMappingInput) int - CreateControlSnapshotMapping func(childComplexity int, input types.CreateControlSnapshotMappingInput) int - CreateCustomDomain func(childComplexity int, input types.CreateCustomDomainInput) int - CreateDataProtectionImpactAssessment func(childComplexity int, input types.CreateDataProtectionImpactAssessmentInput) int - CreateDatum func(childComplexity int, input types.CreateDatumInput) int - CreateDocument func(childComplexity int, input types.CreateDocumentInput) int - CreateDraftDocumentVersion func(childComplexity int, input types.CreateDraftDocumentVersionInput) int - CreateFramework func(childComplexity int, input types.CreateFrameworkInput) int - CreateMeasure func(childComplexity int, input types.CreateMeasureInput) int - CreateMeeting func(childComplexity int, input types.CreateMeetingInput) int - CreateNonconformity func(childComplexity int, input types.CreateNonconformityInput) int - CreateObligation func(childComplexity int, input types.CreateObligationInput) int - CreateOrganization func(childComplexity int, input types.CreateOrganizationInput) int - CreatePeople func(childComplexity int, input types.CreatePeopleInput) int - CreateProcessingActivity func(childComplexity int, input types.CreateProcessingActivityInput) int - CreateRightsRequest func(childComplexity int, input types.CreateRightsRequestInput) int - CreateRisk func(childComplexity int, input types.CreateRiskInput) int - CreateRiskDocumentMapping func(childComplexity int, input types.CreateRiskDocumentMappingInput) int - CreateRiskMeasureMapping func(childComplexity int, input types.CreateRiskMeasureMappingInput) int - CreateRiskObligationMapping func(childComplexity int, input types.CreateRiskObligationMappingInput) int - CreateSAMLConfiguration func(childComplexity int, input types.CreateSAMLConfigurationInput) int - CreateSnapshot func(childComplexity int, input types.CreateSnapshotInput) int - CreateTask func(childComplexity int, input types.CreateTaskInput) int - CreateTransferImpactAssessment func(childComplexity int, input types.CreateTransferImpactAssessmentInput) int - CreateTrustCenterAccess func(childComplexity int, input types.CreateTrustCenterAccessInput) int - CreateTrustCenterFile func(childComplexity int, input types.CreateTrustCenterFileInput) int - CreateTrustCenterReference func(childComplexity int, input types.CreateTrustCenterReferenceInput) int - CreateVendor func(childComplexity int, input types.CreateVendorInput) int - CreateVendorContact func(childComplexity int, input types.CreateVendorContactInput) int - CreateVendorRiskAssessment func(childComplexity int, input types.CreateVendorRiskAssessmentInput) int - CreateVendorService func(childComplexity int, input types.CreateVendorServiceInput) int - DeleteAsset func(childComplexity int, input types.DeleteAssetInput) int - DeleteAudit func(childComplexity int, input types.DeleteAuditInput) int - DeleteAuditReport func(childComplexity int, input types.DeleteAuditReportInput) int - DeleteContinualImprovement func(childComplexity int, input types.DeleteContinualImprovementInput) int - DeleteControl func(childComplexity int, input types.DeleteControlInput) int - DeleteControlAuditMapping func(childComplexity int, input types.DeleteControlAuditMappingInput) int - DeleteControlDocumentMapping func(childComplexity int, input types.DeleteControlDocumentMappingInput) int - DeleteControlMeasureMapping func(childComplexity int, input types.DeleteControlMeasureMappingInput) int - DeleteControlSnapshotMapping func(childComplexity int, input types.DeleteControlSnapshotMappingInput) int - DeleteCustomDomain func(childComplexity int, input types.DeleteCustomDomainInput) int - DeleteDataProtectionImpactAssessment func(childComplexity int, input types.DeleteDataProtectionImpactAssessmentInput) int - DeleteDatum func(childComplexity int, input types.DeleteDatumInput) int - DeleteDocument func(childComplexity int, input types.DeleteDocumentInput) int - DeleteDraftDocumentVersion func(childComplexity int, input types.DeleteDraftDocumentVersionInput) int - DeleteEvidence func(childComplexity int, input types.DeleteEvidenceInput) int - DeleteFramework func(childComplexity int, input types.DeleteFrameworkInput) int - DeleteInvitation func(childComplexity int, input types.DeleteInvitationInput) int - DeleteMeasure func(childComplexity int, input types.DeleteMeasureInput) int - DeleteMeeting func(childComplexity int, input types.DeleteMeetingInput) int - DeleteNonconformity func(childComplexity int, input types.DeleteNonconformityInput) int - DeleteObligation func(childComplexity int, input types.DeleteObligationInput) int - DeleteOrganization func(childComplexity int, input types.DeleteOrganizationInput) int - DeleteOrganizationHorizontalLogo func(childComplexity int, input types.DeleteOrganizationHorizontalLogoInput) int - DeletePeople func(childComplexity int, input types.DeletePeopleInput) int - DeleteProcessingActivity func(childComplexity int, input types.DeleteProcessingActivityInput) int - DeleteRightsRequest func(childComplexity int, input types.DeleteRightsRequestInput) int - DeleteRisk func(childComplexity int, input types.DeleteRiskInput) int - DeleteRiskDocumentMapping func(childComplexity int, input types.DeleteRiskDocumentMappingInput) int - DeleteRiskMeasureMapping func(childComplexity int, input types.DeleteRiskMeasureMappingInput) int - DeleteRiskObligationMapping func(childComplexity int, input types.DeleteRiskObligationMappingInput) int - DeleteSAMLConfiguration func(childComplexity int, input types.DeleteSAMLConfigurationInput) int - DeleteSnapshot func(childComplexity int, input types.DeleteSnapshotInput) int - DeleteTask func(childComplexity int, input types.DeleteTaskInput) int - DeleteTransferImpactAssessment func(childComplexity int, input types.DeleteTransferImpactAssessmentInput) int - DeleteTrustCenterAccess func(childComplexity int, input types.DeleteTrustCenterAccessInput) int - DeleteTrustCenterFile func(childComplexity int, input types.DeleteTrustCenterFileInput) int - DeleteTrustCenterNda func(childComplexity int, input types.DeleteTrustCenterNDAInput) int - DeleteTrustCenterReference func(childComplexity int, input types.DeleteTrustCenterReferenceInput) int - DeleteVendor func(childComplexity int, input types.DeleteVendorInput) int - DeleteVendorBusinessAssociateAgreement func(childComplexity int, input types.DeleteVendorBusinessAssociateAgreementInput) int - DeleteVendorComplianceReport func(childComplexity int, input types.DeleteVendorComplianceReportInput) int - DeleteVendorContact func(childComplexity int, input types.DeleteVendorContactInput) int - DeleteVendorDataPrivacyAgreement func(childComplexity int, input types.DeleteVendorDataPrivacyAgreementInput) int - DeleteVendorService func(childComplexity int, input types.DeleteVendorServiceInput) int - DisableSaml func(childComplexity int, input types.DisableSAMLInput) int - EnableSaml func(childComplexity int, input types.EnableSAMLInput) int - ExportDocumentVersionPDF func(childComplexity int, input types.ExportDocumentVersionPDFInput) int - ExportFramework func(childComplexity int, input types.ExportFrameworkInput) int - ExportSignableVersionDocumentPDF func(childComplexity int, input types.ExportSignableDocumentVersionPDFInput) int - GenerateDocumentChangelog func(childComplexity int, input types.GenerateDocumentChangelogInput) int - GenerateFrameworkStateOfApplicability func(childComplexity int, input types.GenerateFrameworkStateOfApplicabilityInput) int - GetTrustCenterFile func(childComplexity int, input types.GetTrustCenterFileInput) int - ImportFramework func(childComplexity int, input types.ImportFrameworkInput) int - ImportMeasure func(childComplexity int, input types.ImportMeasureInput) int - InitiateDomainVerification func(childComplexity int, input types.InitiateDomainVerificationInput) int - InviteUser func(childComplexity int, input types.InviteUserInput) int - PublishDocumentVersion func(childComplexity int, input types.PublishDocumentVersionInput) int - RemoveMember func(childComplexity int, input types.RemoveMemberInput) int - RequestSignature func(childComplexity int, input types.RequestSignatureInput) int - SendSigningNotifications func(childComplexity int, input types.SendSigningNotificationsInput) int - SignDocument func(childComplexity int, input types.SignDocumentInput) int - UpdateAsset func(childComplexity int, input types.UpdateAssetInput) int - UpdateAudit func(childComplexity int, input types.UpdateAuditInput) int - UpdateContinualImprovement func(childComplexity int, input types.UpdateContinualImprovementInput) int - UpdateControl func(childComplexity int, input types.UpdateControlInput) int - UpdateDataProtectionImpactAssessment func(childComplexity int, input types.UpdateDataProtectionImpactAssessmentInput) int - UpdateDatum func(childComplexity int, input types.UpdateDatumInput) int - UpdateDocument func(childComplexity int, input types.UpdateDocumentInput) int - UpdateDocumentVersion func(childComplexity int, input types.UpdateDocumentVersionInput) int - UpdateFramework func(childComplexity int, input types.UpdateFrameworkInput) int - UpdateMeasure func(childComplexity int, input types.UpdateMeasureInput) int - UpdateMeeting func(childComplexity int, input types.UpdateMeetingInput) int - UpdateMembership func(childComplexity int, input types.UpdateMembershipInput) int - UpdateNonconformity func(childComplexity int, input types.UpdateNonconformityInput) int - UpdateObligation func(childComplexity int, input types.UpdateObligationInput) int - UpdateOrganization func(childComplexity int, input types.UpdateOrganizationInput) int - UpdateOrganizationContext func(childComplexity int, input types.UpdateOrganizationContextInput) int - UpdatePeople func(childComplexity int, input types.UpdatePeopleInput) int - UpdateProcessingActivity func(childComplexity int, input types.UpdateProcessingActivityInput) int - UpdateRightsRequest func(childComplexity int, input types.UpdateRightsRequestInput) int - UpdateRisk func(childComplexity int, input types.UpdateRiskInput) int - UpdateSAMLConfiguration func(childComplexity int, input types.UpdateSAMLConfigurationInput) int - UpdateTask func(childComplexity int, input types.UpdateTaskInput) int - UpdateTransferImpactAssessment func(childComplexity int, input types.UpdateTransferImpactAssessmentInput) int - UpdateTrustCenter func(childComplexity int, input types.UpdateTrustCenterInput) int - UpdateTrustCenterAccess func(childComplexity int, input types.UpdateTrustCenterAccessInput) int - UpdateTrustCenterFile func(childComplexity int, input types.UpdateTrustCenterFileInput) int - UpdateTrustCenterReference func(childComplexity int, input types.UpdateTrustCenterReferenceInput) int - UpdateVendor func(childComplexity int, input types.UpdateVendorInput) int - UpdateVendorBusinessAssociateAgreement func(childComplexity int, input types.UpdateVendorBusinessAssociateAgreementInput) int - UpdateVendorContact func(childComplexity int, input types.UpdateVendorContactInput) int - UpdateVendorDataPrivacyAgreement func(childComplexity int, input types.UpdateVendorDataPrivacyAgreementInput) int - UpdateVendorService func(childComplexity int, input types.UpdateVendorServiceInput) int - UploadAuditReport func(childComplexity int, input types.UploadAuditReportInput) int - UploadMeasureEvidence func(childComplexity int, input types.UploadMeasureEvidenceInput) int - UploadTrustCenterNda func(childComplexity int, input types.UploadTrustCenterNDAInput) int - UploadVendorBusinessAssociateAgreement func(childComplexity int, input types.UploadVendorBusinessAssociateAgreementInput) int - UploadVendorComplianceReport func(childComplexity int, input types.UploadVendorComplianceReportInput) int - UploadVendorDataPrivacyAgreement func(childComplexity int, input types.UploadVendorDataPrivacyAgreementInput) int - VerifyDomain func(childComplexity int, input types.VerifyDomainInput) int + AcceptInvitation func(childComplexity int, input types.AcceptInvitationInput) int + AssessVendor func(childComplexity int, input types.AssessVendorInput) int + BulkDeleteDocuments func(childComplexity int, input types.BulkDeleteDocumentsInput) int + BulkExportDocuments func(childComplexity int, input types.BulkExportDocumentsInput) int + BulkPublishDocumentVersions func(childComplexity int, input types.BulkPublishDocumentVersionsInput) int + BulkRequestSignatures func(childComplexity int, input types.BulkRequestSignaturesInput) int + CancelSignatureRequest func(childComplexity int, input types.CancelSignatureRequestInput) int + ConfirmEmail func(childComplexity int, input types.ConfirmEmailInput) int + CreateAsset func(childComplexity int, input types.CreateAssetInput) int + CreateAudit func(childComplexity int, input types.CreateAuditInput) int + CreateContinualImprovement func(childComplexity int, input types.CreateContinualImprovementInput) int + CreateControl func(childComplexity int, input types.CreateControlInput) int + CreateControlAuditMapping func(childComplexity int, input types.CreateControlAuditMappingInput) int + CreateControlDocumentMapping func(childComplexity int, input types.CreateControlDocumentMappingInput) int + CreateControlMeasureMapping func(childComplexity int, input types.CreateControlMeasureMappingInput) int + CreateControlSnapshotMapping func(childComplexity int, input types.CreateControlSnapshotMappingInput) int + CreateCustomDomain func(childComplexity int, input types.CreateCustomDomainInput) int + CreateDataProtectionImpactAssessment func(childComplexity int, input types.CreateDataProtectionImpactAssessmentInput) int + CreateDatum func(childComplexity int, input types.CreateDatumInput) int + CreateDocument func(childComplexity int, input types.CreateDocumentInput) int + CreateDraftDocumentVersion func(childComplexity int, input types.CreateDraftDocumentVersionInput) int + CreateFramework func(childComplexity int, input types.CreateFrameworkInput) int + CreateMeasure func(childComplexity int, input types.CreateMeasureInput) int + CreateMeeting func(childComplexity int, input types.CreateMeetingInput) int + CreateNonconformity func(childComplexity int, input types.CreateNonconformityInput) int + CreateObligation func(childComplexity int, input types.CreateObligationInput) int + CreateOrganization func(childComplexity int, input types.CreateOrganizationInput) int + CreatePeople func(childComplexity int, input types.CreatePeopleInput) int + CreateProcessingActivity func(childComplexity int, input types.CreateProcessingActivityInput) int + CreateRightsRequest func(childComplexity int, input types.CreateRightsRequestInput) int + CreateRisk func(childComplexity int, input types.CreateRiskInput) int + CreateRiskDocumentMapping func(childComplexity int, input types.CreateRiskDocumentMappingInput) int + CreateRiskMeasureMapping func(childComplexity int, input types.CreateRiskMeasureMappingInput) int + CreateRiskObligationMapping func(childComplexity int, input types.CreateRiskObligationMappingInput) int + CreateSAMLConfiguration func(childComplexity int, input types.CreateSAMLConfigurationInput) int + CreateSnapshot func(childComplexity int, input types.CreateSnapshotInput) int + CreateTask func(childComplexity int, input types.CreateTaskInput) int + CreateTransferImpactAssessment func(childComplexity int, input types.CreateTransferImpactAssessmentInput) int + CreateTrustCenterAccess func(childComplexity int, input types.CreateTrustCenterAccessInput) int + CreateTrustCenterFile func(childComplexity int, input types.CreateTrustCenterFileInput) int + CreateTrustCenterReference func(childComplexity int, input types.CreateTrustCenterReferenceInput) int + CreateVendor func(childComplexity int, input types.CreateVendorInput) int + CreateVendorContact func(childComplexity int, input types.CreateVendorContactInput) int + CreateVendorRiskAssessment func(childComplexity int, input types.CreateVendorRiskAssessmentInput) int + CreateVendorService func(childComplexity int, input types.CreateVendorServiceInput) int + DeleteAsset func(childComplexity int, input types.DeleteAssetInput) int + DeleteAudit func(childComplexity int, input types.DeleteAuditInput) int + DeleteAuditReport func(childComplexity int, input types.DeleteAuditReportInput) int + DeleteContinualImprovement func(childComplexity int, input types.DeleteContinualImprovementInput) int + DeleteControl func(childComplexity int, input types.DeleteControlInput) int + DeleteControlAuditMapping func(childComplexity int, input types.DeleteControlAuditMappingInput) int + DeleteControlDocumentMapping func(childComplexity int, input types.DeleteControlDocumentMappingInput) int + DeleteControlMeasureMapping func(childComplexity int, input types.DeleteControlMeasureMappingInput) int + DeleteControlSnapshotMapping func(childComplexity int, input types.DeleteControlSnapshotMappingInput) int + DeleteCustomDomain func(childComplexity int, input types.DeleteCustomDomainInput) int + DeleteDataProtectionImpactAssessment func(childComplexity int, input types.DeleteDataProtectionImpactAssessmentInput) int + DeleteDatum func(childComplexity int, input types.DeleteDatumInput) int + DeleteDocument func(childComplexity int, input types.DeleteDocumentInput) int + DeleteDraftDocumentVersion func(childComplexity int, input types.DeleteDraftDocumentVersionInput) int + DeleteEvidence func(childComplexity int, input types.DeleteEvidenceInput) int + DeleteFramework func(childComplexity int, input types.DeleteFrameworkInput) int + DeleteInvitation func(childComplexity int, input types.DeleteInvitationInput) int + DeleteMeasure func(childComplexity int, input types.DeleteMeasureInput) int + DeleteMeeting func(childComplexity int, input types.DeleteMeetingInput) int + DeleteNonconformity func(childComplexity int, input types.DeleteNonconformityInput) int + DeleteObligation func(childComplexity int, input types.DeleteObligationInput) int + DeleteOrganization func(childComplexity int, input types.DeleteOrganizationInput) int + DeleteOrganizationHorizontalLogo func(childComplexity int, input types.DeleteOrganizationHorizontalLogoInput) int + DeletePeople func(childComplexity int, input types.DeletePeopleInput) int + DeleteProcessingActivity func(childComplexity int, input types.DeleteProcessingActivityInput) int + DeleteRightsRequest func(childComplexity int, input types.DeleteRightsRequestInput) int + DeleteRisk func(childComplexity int, input types.DeleteRiskInput) int + DeleteRiskDocumentMapping func(childComplexity int, input types.DeleteRiskDocumentMappingInput) int + DeleteRiskMeasureMapping func(childComplexity int, input types.DeleteRiskMeasureMappingInput) int + DeleteRiskObligationMapping func(childComplexity int, input types.DeleteRiskObligationMappingInput) int + DeleteSAMLConfiguration func(childComplexity int, input types.DeleteSAMLConfigurationInput) int + DeleteSnapshot func(childComplexity int, input types.DeleteSnapshotInput) int + DeleteTask func(childComplexity int, input types.DeleteTaskInput) int + DeleteTransferImpactAssessment func(childComplexity int, input types.DeleteTransferImpactAssessmentInput) int + DeleteTrustCenterAccess func(childComplexity int, input types.DeleteTrustCenterAccessInput) int + DeleteTrustCenterFile func(childComplexity int, input types.DeleteTrustCenterFileInput) int + DeleteTrustCenterNda func(childComplexity int, input types.DeleteTrustCenterNDAInput) int + DeleteTrustCenterReference func(childComplexity int, input types.DeleteTrustCenterReferenceInput) int + DeleteVendor func(childComplexity int, input types.DeleteVendorInput) int + DeleteVendorBusinessAssociateAgreement func(childComplexity int, input types.DeleteVendorBusinessAssociateAgreementInput) int + DeleteVendorComplianceReport func(childComplexity int, input types.DeleteVendorComplianceReportInput) int + DeleteVendorContact func(childComplexity int, input types.DeleteVendorContactInput) int + DeleteVendorDataPrivacyAgreement func(childComplexity int, input types.DeleteVendorDataPrivacyAgreementInput) int + DeleteVendorService func(childComplexity int, input types.DeleteVendorServiceInput) int + DisableSaml func(childComplexity int, input types.DisableSAMLInput) int + EnableSaml func(childComplexity int, input types.EnableSAMLInput) int + ExportDataProtectionImpactAssessmentsPDF func(childComplexity int, input types.ExportDataProtectionImpactAssessmentsPDFInput) int + ExportDocumentVersionPDF func(childComplexity int, input types.ExportDocumentVersionPDFInput) int + ExportFramework func(childComplexity int, input types.ExportFrameworkInput) int + ExportProcessingActivitiesPDF func(childComplexity int, input types.ExportProcessingActivitiesPDFInput) int + ExportSignableVersionDocumentPDF func(childComplexity int, input types.ExportSignableDocumentVersionPDFInput) int + ExportTransferImpactAssessmentsPDF func(childComplexity int, input types.ExportTransferImpactAssessmentsPDFInput) int + GenerateDocumentChangelog func(childComplexity int, input types.GenerateDocumentChangelogInput) int + GenerateFrameworkStateOfApplicability func(childComplexity int, input types.GenerateFrameworkStateOfApplicabilityInput) int + GetTrustCenterFile func(childComplexity int, input types.GetTrustCenterFileInput) int + ImportFramework func(childComplexity int, input types.ImportFrameworkInput) int + ImportMeasure func(childComplexity int, input types.ImportMeasureInput) int + InitiateDomainVerification func(childComplexity int, input types.InitiateDomainVerificationInput) int + InviteUser func(childComplexity int, input types.InviteUserInput) int + PublishDocumentVersion func(childComplexity int, input types.PublishDocumentVersionInput) int + RemoveMember func(childComplexity int, input types.RemoveMemberInput) int + RequestSignature func(childComplexity int, input types.RequestSignatureInput) int + SendSigningNotifications func(childComplexity int, input types.SendSigningNotificationsInput) int + SignDocument func(childComplexity int, input types.SignDocumentInput) int + UpdateAsset func(childComplexity int, input types.UpdateAssetInput) int + UpdateAudit func(childComplexity int, input types.UpdateAuditInput) int + UpdateContinualImprovement func(childComplexity int, input types.UpdateContinualImprovementInput) int + UpdateControl func(childComplexity int, input types.UpdateControlInput) int + UpdateDataProtectionImpactAssessment func(childComplexity int, input types.UpdateDataProtectionImpactAssessmentInput) int + UpdateDatum func(childComplexity int, input types.UpdateDatumInput) int + UpdateDocument func(childComplexity int, input types.UpdateDocumentInput) int + UpdateDocumentVersion func(childComplexity int, input types.UpdateDocumentVersionInput) int + UpdateFramework func(childComplexity int, input types.UpdateFrameworkInput) int + UpdateMeasure func(childComplexity int, input types.UpdateMeasureInput) int + UpdateMeeting func(childComplexity int, input types.UpdateMeetingInput) int + UpdateMembership func(childComplexity int, input types.UpdateMembershipInput) int + UpdateNonconformity func(childComplexity int, input types.UpdateNonconformityInput) int + UpdateObligation func(childComplexity int, input types.UpdateObligationInput) int + UpdateOrganization func(childComplexity int, input types.UpdateOrganizationInput) int + UpdateOrganizationContext func(childComplexity int, input types.UpdateOrganizationContextInput) int + UpdatePeople func(childComplexity int, input types.UpdatePeopleInput) int + UpdateProcessingActivity func(childComplexity int, input types.UpdateProcessingActivityInput) int + UpdateRightsRequest func(childComplexity int, input types.UpdateRightsRequestInput) int + UpdateRisk func(childComplexity int, input types.UpdateRiskInput) int + UpdateSAMLConfiguration func(childComplexity int, input types.UpdateSAMLConfigurationInput) int + UpdateTask func(childComplexity int, input types.UpdateTaskInput) int + UpdateTransferImpactAssessment func(childComplexity int, input types.UpdateTransferImpactAssessmentInput) int + UpdateTrustCenter func(childComplexity int, input types.UpdateTrustCenterInput) int + UpdateTrustCenterAccess func(childComplexity int, input types.UpdateTrustCenterAccessInput) int + UpdateTrustCenterFile func(childComplexity int, input types.UpdateTrustCenterFileInput) int + UpdateTrustCenterReference func(childComplexity int, input types.UpdateTrustCenterReferenceInput) int + UpdateVendor func(childComplexity int, input types.UpdateVendorInput) int + UpdateVendorBusinessAssociateAgreement func(childComplexity int, input types.UpdateVendorBusinessAssociateAgreementInput) int + UpdateVendorContact func(childComplexity int, input types.UpdateVendorContactInput) int + UpdateVendorDataPrivacyAgreement func(childComplexity int, input types.UpdateVendorDataPrivacyAgreementInput) int + UpdateVendorService func(childComplexity int, input types.UpdateVendorServiceInput) int + UploadAuditReport func(childComplexity int, input types.UploadAuditReportInput) int + UploadMeasureEvidence func(childComplexity int, input types.UploadMeasureEvidenceInput) int + UploadTrustCenterNda func(childComplexity int, input types.UploadTrustCenterNDAInput) int + UploadVendorBusinessAssociateAgreement func(childComplexity int, input types.UploadVendorBusinessAssociateAgreementInput) int + UploadVendorComplianceReport func(childComplexity int, input types.UploadVendorComplianceReportInput) int + UploadVendorDataPrivacyAgreement func(childComplexity int, input types.UploadVendorDataPrivacyAgreementInput) int + VerifyDomain func(childComplexity int, input types.VerifyDomainInput) int } Nonconformity struct { @@ -2200,6 +2215,9 @@ type MutationResolver interface { SignDocument(ctx context.Context, input types.SignDocumentInput) (*types.SignDocumentPayload, error) ExportDocumentVersionPDF(ctx context.Context, input types.ExportDocumentVersionPDFInput) (*types.ExportDocumentVersionPDFPayload, error) ExportSignableVersionDocumentPDF(ctx context.Context, input types.ExportSignableDocumentVersionPDFInput) (*types.ExportSignableDocumentVersionPDFPayload, error) + ExportProcessingActivitiesPDF(ctx context.Context, input types.ExportProcessingActivitiesPDFInput) (*types.ExportProcessingActivitiesPDFPayload, error) + ExportDataProtectionImpactAssessmentsPDF(ctx context.Context, input types.ExportDataProtectionImpactAssessmentsPDFInput) (*types.ExportDataProtectionImpactAssessmentsPDFPayload, error) + ExportTransferImpactAssessmentsPDF(ctx context.Context, input types.ExportTransferImpactAssessmentsPDFInput) (*types.ExportTransferImpactAssessmentsPDFPayload, error) CreateVendorRiskAssessment(ctx context.Context, input types.CreateVendorRiskAssessmentInput) (*types.CreateVendorRiskAssessmentPayload, error) AssessVendor(ctx context.Context, input types.AssessVendorInput) (*types.AssessVendorPayload, error) CreateAsset(ctx context.Context, input types.CreateAssetInput) (*types.CreateAssetPayload, error) @@ -4350,6 +4368,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.EvidenceEdge.Node(childComplexity), true + case "ExportDataProtectionImpactAssessmentsPDFPayload.data": + if e.complexity.ExportDataProtectionImpactAssessmentsPDFPayload.Data == nil { + break + } + + return e.complexity.ExportDataProtectionImpactAssessmentsPDFPayload.Data(childComplexity), true + case "ExportDocumentVersionPDFPayload.data": if e.complexity.ExportDocumentVersionPDFPayload.Data == nil { break @@ -4364,6 +4389,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.ExportFrameworkPayload.ExportJobID(childComplexity), true + case "ExportProcessingActivitiesPDFPayload.data": + if e.complexity.ExportProcessingActivitiesPDFPayload.Data == nil { + break + } + + return e.complexity.ExportProcessingActivitiesPDFPayload.Data(childComplexity), true + case "ExportSignableDocumentVersionPDFPayload.data": if e.complexity.ExportSignableDocumentVersionPDFPayload.Data == nil { break @@ -4371,6 +4403,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.ExportSignableDocumentVersionPDFPayload.Data(childComplexity), true + case "ExportTransferImpactAssessmentsPDFPayload.data": + if e.complexity.ExportTransferImpactAssessmentsPDFPayload.Data == nil { + break + } + + return e.complexity.ExportTransferImpactAssessmentsPDFPayload.Data(childComplexity), true + case "File.createdAt": if e.complexity.File.CreatedAt == nil { break @@ -5943,6 +5982,17 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin } return e.complexity.Mutation.EnableSaml(childComplexity, args["input"].(types.EnableSAMLInput)), true + case "Mutation.exportDataProtectionImpactAssessmentsPDF": + if e.complexity.Mutation.ExportDataProtectionImpactAssessmentsPDF == nil { + break + } + + args, err := ec.field_Mutation_exportDataProtectionImpactAssessmentsPDF_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.ExportDataProtectionImpactAssessmentsPDF(childComplexity, args["input"].(types.ExportDataProtectionImpactAssessmentsPDFInput)), true case "Mutation.exportDocumentVersionPDF": if e.complexity.Mutation.ExportDocumentVersionPDF == nil { break @@ -5965,6 +6015,17 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin } return e.complexity.Mutation.ExportFramework(childComplexity, args["input"].(types.ExportFrameworkInput)), true + case "Mutation.exportProcessingActivitiesPDF": + if e.complexity.Mutation.ExportProcessingActivitiesPDF == nil { + break + } + + args, err := ec.field_Mutation_exportProcessingActivitiesPDF_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.ExportProcessingActivitiesPDF(childComplexity, args["input"].(types.ExportProcessingActivitiesPDFInput)), true case "Mutation.exportSignableVersionDocumentPDF": if e.complexity.Mutation.ExportSignableVersionDocumentPDF == nil { break @@ -5976,6 +6037,17 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin } return e.complexity.Mutation.ExportSignableVersionDocumentPDF(childComplexity, args["input"].(types.ExportSignableDocumentVersionPDFInput)), true + case "Mutation.exportTransferImpactAssessmentsPDF": + if e.complexity.Mutation.ExportTransferImpactAssessmentsPDF == nil { + break + } + + args, err := ec.field_Mutation_exportTransferImpactAssessmentsPDF_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.ExportTransferImpactAssessmentsPDF(childComplexity, args["input"].(types.ExportTransferImpactAssessmentsPDFInput)), true case "Mutation.generateDocumentChangelog": if e.complexity.Mutation.GenerateDocumentChangelog == nil { break @@ -10030,9 +10102,12 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputDocumentVersionSignatureOrder, ec.unmarshalInputEnableSAMLInput, ec.unmarshalInputEvidenceOrder, + ec.unmarshalInputExportDataProtectionImpactAssessmentsPDFInput, ec.unmarshalInputExportDocumentVersionPDFInput, ec.unmarshalInputExportFrameworkInput, + ec.unmarshalInputExportProcessingActivitiesPDFInput, ec.unmarshalInputExportSignableDocumentVersionPDFInput, + ec.unmarshalInputExportTransferImpactAssessmentsPDFInput, ec.unmarshalInputFrameworkOrder, ec.unmarshalInputFulfillEvidenceInput, ec.unmarshalInputGenerateDocumentChangelogInput, @@ -13530,6 +13605,15 @@ type Mutation { exportSignableVersionDocumentPDF( input: ExportSignableDocumentVersionPDFInput! ): ExportSignableDocumentVersionPDFPayload! + exportProcessingActivitiesPDF( + input: ExportProcessingActivitiesPDFInput! + ): ExportProcessingActivitiesPDFPayload! + exportDataProtectionImpactAssessmentsPDF( + input: ExportDataProtectionImpactAssessmentsPDFInput! + ): ExportDataProtectionImpactAssessmentsPDFPayload! + exportTransferImpactAssessmentsPDF( + input: ExportTransferImpactAssessmentsPDFInput! + ): ExportTransferImpactAssessmentsPDFPayload! createVendorRiskAssessment( input: CreateVendorRiskAssessmentInput! ): CreateVendorRiskAssessmentPayload! @@ -14161,6 +14245,21 @@ input ExportSignableDocumentVersionPDFInput { documentVersionId: ID! } +input ExportProcessingActivitiesPDFInput { + organizationId: ID! + filter: ProcessingActivityFilter +} + +input ExportDataProtectionImpactAssessmentsPDFInput { + organizationId: ID! + filter: DataProtectionImpactAssessmentFilter +} + +input ExportTransferImpactAssessmentsPDFInput { + organizationId: ID! + filter: TransferImpactAssessmentFilter +} + input DeleteDocumentInput { documentId: ID! } @@ -14820,6 +14919,18 @@ type ExportSignableDocumentVersionPDFPayload { data: String! } +type ExportProcessingActivitiesPDFPayload { + data: String! +} + +type ExportDataProtectionImpactAssessmentsPDFPayload { + data: String! +} + +type ExportTransferImpactAssessmentsPDFPayload { + data: String! +} + type UpdateDocumentPayload { document: Document! } @@ -17125,6 +17236,17 @@ func (ec *executionContext) field_Mutation_enableSAML_args(ctx context.Context, return args, nil } +func (ec *executionContext) field_Mutation_exportDataProtectionImpactAssessmentsPDF_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", ec.unmarshalNExportDataProtectionImpactAssessmentsPDFInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐExportDataProtectionImpactAssessmentsPDFInput) + if err != nil { + return nil, err + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_exportDocumentVersionPDF_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -17147,6 +17269,17 @@ func (ec *executionContext) field_Mutation_exportFramework_args(ctx context.Cont return args, nil } +func (ec *executionContext) field_Mutation_exportProcessingActivitiesPDF_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", ec.unmarshalNExportProcessingActivitiesPDFInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐExportProcessingActivitiesPDFInput) + if err != nil { + return nil, err + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_exportSignableVersionDocumentPDF_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -17158,6 +17291,17 @@ func (ec *executionContext) field_Mutation_exportSignableVersionDocumentPDF_args return args, nil } +func (ec *executionContext) field_Mutation_exportTransferImpactAssessmentsPDF_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", ec.unmarshalNExportTransferImpactAssessmentsPDFInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐExportTransferImpactAssessmentsPDFInput) + if err != nil { + return nil, err + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_generateDocumentChangelog_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -29640,6 +29784,35 @@ func (ec *executionContext) fieldContext_EvidenceEdge_node(_ context.Context, fi return fc, nil } +func (ec *executionContext) _ExportDataProtectionImpactAssessmentsPDFPayload_data(ctx context.Context, field graphql.CollectedField, obj *types.ExportDataProtectionImpactAssessmentsPDFPayload) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ExportDataProtectionImpactAssessmentsPDFPayload_data, + func(ctx context.Context) (any, error) { + return obj.Data, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ExportDataProtectionImpactAssessmentsPDFPayload_data(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ExportDataProtectionImpactAssessmentsPDFPayload", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _ExportDocumentVersionPDFPayload_data(ctx context.Context, field graphql.CollectedField, obj *types.ExportDocumentVersionPDFPayload) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -29698,6 +29871,35 @@ func (ec *executionContext) fieldContext_ExportFrameworkPayload_exportJobId(_ co return fc, nil } +func (ec *executionContext) _ExportProcessingActivitiesPDFPayload_data(ctx context.Context, field graphql.CollectedField, obj *types.ExportProcessingActivitiesPDFPayload) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ExportProcessingActivitiesPDFPayload_data, + func(ctx context.Context) (any, error) { + return obj.Data, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ExportProcessingActivitiesPDFPayload_data(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ExportProcessingActivitiesPDFPayload", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _ExportSignableDocumentVersionPDFPayload_data(ctx context.Context, field graphql.CollectedField, obj *types.ExportSignableDocumentVersionPDFPayload) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -29727,6 +29929,35 @@ func (ec *executionContext) fieldContext_ExportSignableDocumentVersionPDFPayload return fc, nil } +func (ec *executionContext) _ExportTransferImpactAssessmentsPDFPayload_data(ctx context.Context, field graphql.CollectedField, obj *types.ExportTransferImpactAssessmentsPDFPayload) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ExportTransferImpactAssessmentsPDFPayload_data, + func(ctx context.Context) (any, error) { + return obj.Data, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ExportTransferImpactAssessmentsPDFPayload_data(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ExportTransferImpactAssessmentsPDFPayload", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _File_id(ctx context.Context, field graphql.CollectedField, obj *types.File) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -37412,6 +37643,141 @@ func (ec *executionContext) fieldContext_Mutation_exportSignableVersionDocumentP return fc, nil } +func (ec *executionContext) _Mutation_exportProcessingActivitiesPDF(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_Mutation_exportProcessingActivitiesPDF, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.resolvers.Mutation().ExportProcessingActivitiesPDF(ctx, fc.Args["input"].(types.ExportProcessingActivitiesPDFInput)) + }, + nil, + ec.marshalNExportProcessingActivitiesPDFPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐExportProcessingActivitiesPDFPayload, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_Mutation_exportProcessingActivitiesPDF(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "data": + return ec.fieldContext_ExportProcessingActivitiesPDFPayload_data(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ExportProcessingActivitiesPDFPayload", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_exportProcessingActivitiesPDF_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_exportDataProtectionImpactAssessmentsPDF(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_Mutation_exportDataProtectionImpactAssessmentsPDF, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.resolvers.Mutation().ExportDataProtectionImpactAssessmentsPDF(ctx, fc.Args["input"].(types.ExportDataProtectionImpactAssessmentsPDFInput)) + }, + nil, + ec.marshalNExportDataProtectionImpactAssessmentsPDFPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐExportDataProtectionImpactAssessmentsPDFPayload, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_Mutation_exportDataProtectionImpactAssessmentsPDF(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "data": + return ec.fieldContext_ExportDataProtectionImpactAssessmentsPDFPayload_data(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ExportDataProtectionImpactAssessmentsPDFPayload", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_exportDataProtectionImpactAssessmentsPDF_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_exportTransferImpactAssessmentsPDF(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_Mutation_exportTransferImpactAssessmentsPDF, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.resolvers.Mutation().ExportTransferImpactAssessmentsPDF(ctx, fc.Args["input"].(types.ExportTransferImpactAssessmentsPDFInput)) + }, + nil, + ec.marshalNExportTransferImpactAssessmentsPDFPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐExportTransferImpactAssessmentsPDFPayload, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_Mutation_exportTransferImpactAssessmentsPDF(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "data": + return ec.fieldContext_ExportTransferImpactAssessmentsPDFPayload_data(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ExportTransferImpactAssessmentsPDFPayload", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_exportTransferImpactAssessmentsPDF_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Mutation_createVendorRiskAssessment(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -65022,6 +65388,40 @@ func (ec *executionContext) unmarshalInputEvidenceOrder(ctx context.Context, obj return it, nil } +func (ec *executionContext) unmarshalInputExportDataProtectionImpactAssessmentsPDFInput(ctx context.Context, obj any) (types.ExportDataProtectionImpactAssessmentsPDFInput, error) { + var it types.ExportDataProtectionImpactAssessmentsPDFInput + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"organizationId", "filter"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "organizationId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("organizationId")) + data, err := ec.unmarshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.OrganizationID = data + case "filter": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) + data, err := ec.unmarshalODataProtectionImpactAssessmentFilter2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDataProtectionImpactAssessmentFilter(ctx, v) + if err != nil { + return it, err + } + it.Filter = data + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputExportDocumentVersionPDFInput(ctx context.Context, obj any) (types.ExportDocumentVersionPDFInput, error) { var it types.ExportDocumentVersionPDFInput asMap := map[string]any{} @@ -65097,6 +65497,40 @@ func (ec *executionContext) unmarshalInputExportFrameworkInput(ctx context.Conte return it, nil } +func (ec *executionContext) unmarshalInputExportProcessingActivitiesPDFInput(ctx context.Context, obj any) (types.ExportProcessingActivitiesPDFInput, error) { + var it types.ExportProcessingActivitiesPDFInput + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"organizationId", "filter"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "organizationId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("organizationId")) + data, err := ec.unmarshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.OrganizationID = data + case "filter": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) + data, err := ec.unmarshalOProcessingActivityFilter2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐProcessingActivityFilter(ctx, v) + if err != nil { + return it, err + } + it.Filter = data + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputExportSignableDocumentVersionPDFInput(ctx context.Context, obj any) (types.ExportSignableDocumentVersionPDFInput, error) { var it types.ExportSignableDocumentVersionPDFInput asMap := map[string]any{} @@ -65124,6 +65558,40 @@ func (ec *executionContext) unmarshalInputExportSignableDocumentVersionPDFInput( return it, nil } +func (ec *executionContext) unmarshalInputExportTransferImpactAssessmentsPDFInput(ctx context.Context, obj any) (types.ExportTransferImpactAssessmentsPDFInput, error) { + var it types.ExportTransferImpactAssessmentsPDFInput + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"organizationId", "filter"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "organizationId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("organizationId")) + data, err := ec.unmarshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.OrganizationID = data + case "filter": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) + data, err := ec.unmarshalOTransferImpactAssessmentFilter2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐTransferImpactAssessmentFilter(ctx, v) + if err != nil { + return it, err + } + it.Filter = data + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputFrameworkOrder(ctx context.Context, obj any) (types.FrameworkOrderBy, error) { var it types.FrameworkOrderBy asMap := map[string]any{} @@ -76415,6 +76883,45 @@ func (ec *executionContext) _EvidenceEdge(ctx context.Context, sel ast.Selection return out } +var exportDataProtectionImpactAssessmentsPDFPayloadImplementors = []string{"ExportDataProtectionImpactAssessmentsPDFPayload"} + +func (ec *executionContext) _ExportDataProtectionImpactAssessmentsPDFPayload(ctx context.Context, sel ast.SelectionSet, obj *types.ExportDataProtectionImpactAssessmentsPDFPayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, exportDataProtectionImpactAssessmentsPDFPayloadImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ExportDataProtectionImpactAssessmentsPDFPayload") + case "data": + out.Values[i] = ec._ExportDataProtectionImpactAssessmentsPDFPayload_data(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var exportDocumentVersionPDFPayloadImplementors = []string{"ExportDocumentVersionPDFPayload"} func (ec *executionContext) _ExportDocumentVersionPDFPayload(ctx context.Context, sel ast.SelectionSet, obj *types.ExportDocumentVersionPDFPayload) graphql.Marshaler { @@ -76493,6 +77000,45 @@ func (ec *executionContext) _ExportFrameworkPayload(ctx context.Context, sel ast return out } +var exportProcessingActivitiesPDFPayloadImplementors = []string{"ExportProcessingActivitiesPDFPayload"} + +func (ec *executionContext) _ExportProcessingActivitiesPDFPayload(ctx context.Context, sel ast.SelectionSet, obj *types.ExportProcessingActivitiesPDFPayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, exportProcessingActivitiesPDFPayloadImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ExportProcessingActivitiesPDFPayload") + case "data": + out.Values[i] = ec._ExportProcessingActivitiesPDFPayload_data(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var exportSignableDocumentVersionPDFPayloadImplementors = []string{"ExportSignableDocumentVersionPDFPayload"} func (ec *executionContext) _ExportSignableDocumentVersionPDFPayload(ctx context.Context, sel ast.SelectionSet, obj *types.ExportSignableDocumentVersionPDFPayload) graphql.Marshaler { @@ -76532,6 +77078,45 @@ func (ec *executionContext) _ExportSignableDocumentVersionPDFPayload(ctx context return out } +var exportTransferImpactAssessmentsPDFPayloadImplementors = []string{"ExportTransferImpactAssessmentsPDFPayload"} + +func (ec *executionContext) _ExportTransferImpactAssessmentsPDFPayload(ctx context.Context, sel ast.SelectionSet, obj *types.ExportTransferImpactAssessmentsPDFPayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, exportTransferImpactAssessmentsPDFPayloadImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ExportTransferImpactAssessmentsPDFPayload") + case "data": + out.Values[i] = ec._ExportTransferImpactAssessmentsPDFPayload_data(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var fileImplementors = []string{"File"} func (ec *executionContext) _File(ctx context.Context, sel ast.SelectionSet, obj *types.File) graphql.Marshaler { @@ -79042,6 +79627,27 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { out.Invalids++ } + case "exportProcessingActivitiesPDF": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_exportProcessingActivitiesPDF(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "exportDataProtectionImpactAssessmentsPDF": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_exportDataProtectionImpactAssessmentsPDF(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "exportTransferImpactAssessmentsPDF": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_exportTransferImpactAssessmentsPDF(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } case "createVendorRiskAssessment": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Mutation_createVendorRiskAssessment(ctx, field) @@ -93526,6 +94132,25 @@ var ( } ) +func (ec *executionContext) unmarshalNExportDataProtectionImpactAssessmentsPDFInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐExportDataProtectionImpactAssessmentsPDFInput(ctx context.Context, v any) (types.ExportDataProtectionImpactAssessmentsPDFInput, error) { + res, err := ec.unmarshalInputExportDataProtectionImpactAssessmentsPDFInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNExportDataProtectionImpactAssessmentsPDFPayload2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐExportDataProtectionImpactAssessmentsPDFPayload(ctx context.Context, sel ast.SelectionSet, v types.ExportDataProtectionImpactAssessmentsPDFPayload) graphql.Marshaler { + return ec._ExportDataProtectionImpactAssessmentsPDFPayload(ctx, sel, &v) +} + +func (ec *executionContext) marshalNExportDataProtectionImpactAssessmentsPDFPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐExportDataProtectionImpactAssessmentsPDFPayload(ctx context.Context, sel ast.SelectionSet, v *types.ExportDataProtectionImpactAssessmentsPDFPayload) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._ExportDataProtectionImpactAssessmentsPDFPayload(ctx, sel, v) +} + func (ec *executionContext) unmarshalNExportDocumentVersionPDFInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐExportDocumentVersionPDFInput(ctx context.Context, v any) (types.ExportDocumentVersionPDFInput, error) { res, err := ec.unmarshalInputExportDocumentVersionPDFInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) @@ -93564,6 +94189,25 @@ func (ec *executionContext) marshalNExportFrameworkPayload2ᚖgoᚗproboᚗinc return ec._ExportFrameworkPayload(ctx, sel, v) } +func (ec *executionContext) unmarshalNExportProcessingActivitiesPDFInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐExportProcessingActivitiesPDFInput(ctx context.Context, v any) (types.ExportProcessingActivitiesPDFInput, error) { + res, err := ec.unmarshalInputExportProcessingActivitiesPDFInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNExportProcessingActivitiesPDFPayload2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐExportProcessingActivitiesPDFPayload(ctx context.Context, sel ast.SelectionSet, v types.ExportProcessingActivitiesPDFPayload) graphql.Marshaler { + return ec._ExportProcessingActivitiesPDFPayload(ctx, sel, &v) +} + +func (ec *executionContext) marshalNExportProcessingActivitiesPDFPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐExportProcessingActivitiesPDFPayload(ctx context.Context, sel ast.SelectionSet, v *types.ExportProcessingActivitiesPDFPayload) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._ExportProcessingActivitiesPDFPayload(ctx, sel, v) +} + func (ec *executionContext) unmarshalNExportSignableDocumentVersionPDFInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐExportSignableDocumentVersionPDFInput(ctx context.Context, v any) (types.ExportSignableDocumentVersionPDFInput, error) { res, err := ec.unmarshalInputExportSignableDocumentVersionPDFInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) @@ -93583,6 +94227,25 @@ func (ec *executionContext) marshalNExportSignableDocumentVersionPDFPayload2ᚖg return ec._ExportSignableDocumentVersionPDFPayload(ctx, sel, v) } +func (ec *executionContext) unmarshalNExportTransferImpactAssessmentsPDFInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐExportTransferImpactAssessmentsPDFInput(ctx context.Context, v any) (types.ExportTransferImpactAssessmentsPDFInput, error) { + res, err := ec.unmarshalInputExportTransferImpactAssessmentsPDFInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNExportTransferImpactAssessmentsPDFPayload2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐExportTransferImpactAssessmentsPDFPayload(ctx context.Context, sel ast.SelectionSet, v types.ExportTransferImpactAssessmentsPDFPayload) graphql.Marshaler { + return ec._ExportTransferImpactAssessmentsPDFPayload(ctx, sel, &v) +} + +func (ec *executionContext) marshalNExportTransferImpactAssessmentsPDFPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐExportTransferImpactAssessmentsPDFPayload(ctx context.Context, sel ast.SelectionSet, v *types.ExportTransferImpactAssessmentsPDFPayload) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._ExportTransferImpactAssessmentsPDFPayload(ctx, sel, v) +} + func (ec *executionContext) marshalNFramework2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐFramework(ctx context.Context, sel ast.SelectionSet, v types.Framework) graphql.Marshaler { return ec._Framework(ctx, sel, &v) } diff --git a/pkg/server/api/console/v1/types/types.go b/pkg/server/api/console/v1/types/types.go index 0b79864a8..2f92dd76b 100644 --- a/pkg/server/api/console/v1/types/types.go +++ b/pkg/server/api/console/v1/types/types.go @@ -1220,6 +1220,15 @@ type EvidenceEdge struct { Node *Evidence `json:"node"` } +type ExportDataProtectionImpactAssessmentsPDFInput struct { + OrganizationID gid.GID `json:"organizationId"` + Filter *DataProtectionImpactAssessmentFilter `json:"filter,omitempty"` +} + +type ExportDataProtectionImpactAssessmentsPDFPayload struct { + Data string `json:"data"` +} + type ExportDocumentVersionPDFInput struct { DocumentVersionID gid.GID `json:"documentVersionId"` WithWatermark bool `json:"withWatermark"` @@ -1239,6 +1248,15 @@ type ExportFrameworkPayload struct { ExportJobID gid.GID `json:"exportJobId"` } +type ExportProcessingActivitiesPDFInput struct { + OrganizationID gid.GID `json:"organizationId"` + Filter *ProcessingActivityFilter `json:"filter,omitempty"` +} + +type ExportProcessingActivitiesPDFPayload struct { + Data string `json:"data"` +} + type ExportSignableDocumentVersionPDFInput struct { DocumentVersionID gid.GID `json:"documentVersionId"` } @@ -1247,6 +1265,15 @@ type ExportSignableDocumentVersionPDFPayload struct { Data string `json:"data"` } +type ExportTransferImpactAssessmentsPDFInput struct { + OrganizationID gid.GID `json:"organizationId"` + Filter *TransferImpactAssessmentFilter `json:"filter,omitempty"` +} + +type ExportTransferImpactAssessmentsPDFPayload struct { + Data string `json:"data"` +} + type File struct { ID gid.GID `json:"id"` MimeType string `json:"mimeType"` diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go index 5d4a38ce9..71f3ba268 100644 --- a/pkg/server/api/console/v1/v1_resolver.go +++ b/pkg/server/api/console/v1/v1_resolver.go @@ -3745,6 +3745,84 @@ func (r *mutationResolver) ExportSignableVersionDocumentPDF(ctx context.Context, }, nil } +// ExportProcessingActivitiesPDF is the resolver for the exportProcessingActivitiesPDF field. +func (r *mutationResolver) ExportProcessingActivitiesPDF(ctx context.Context, input types.ExportProcessingActivitiesPDFInput) (*types.ExportProcessingActivitiesPDFPayload, error) { + r.MustBeAuthorized(ctx, input.OrganizationID, authz.ActionExportProcessingActivitiesPDF) + + prb := r.ProboService(ctx, input.OrganizationID.TenantID()) + + var snapshotIDPtr *gid.GID + if input.Filter != nil { + snapshotIDPtr = input.Filter.SnapshotID + } + processingActivityFilter := coredata.NewProcessingActivityFilter(&snapshotIDPtr) + + pdf, err := prb.ProcessingActivities.ExportPDF(ctx, input.OrganizationID, processingActivityFilter) + if err != nil { + var errNotFound *coredata.ErrNoProcessingActivitiesFound + if errors.As(err, &errNotFound) { + return nil, gqlutils.NotFound(errNotFound) + } + panic(fmt.Errorf("cannot export processing activities PDF: %w", err)) + } + + return &types.ExportProcessingActivitiesPDFPayload{ + Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)), + }, nil +} + +// ExportDataProtectionImpactAssessmentsPDF is the resolver for the exportDataProtectionImpactAssessmentsPDF field. +func (r *mutationResolver) ExportDataProtectionImpactAssessmentsPDF(ctx context.Context, input types.ExportDataProtectionImpactAssessmentsPDFInput) (*types.ExportDataProtectionImpactAssessmentsPDFPayload, error) { + r.MustBeAuthorized(ctx, input.OrganizationID, authz.ActionExportDataProtectionImpactAssessmentsPDF) + + prb := r.ProboService(ctx, input.OrganizationID.TenantID()) + + var snapshotIDPtr *gid.GID + if input.Filter != nil { + snapshotIDPtr = input.Filter.SnapshotID + } + dpiaFilter := coredata.NewDataProtectionImpactAssessmentFilter(&snapshotIDPtr) + + pdf, err := prb.DataProtectionImpactAssessments.ExportPDF(ctx, input.OrganizationID, dpiaFilter) + if err != nil { + var errNotFound *coredata.ErrNoDataProtectionImpactAssessmentsFound + if errors.As(err, &errNotFound) { + return nil, gqlutils.NotFound(errNotFound) + } + panic(fmt.Errorf("cannot export data protection impact assessments PDF: %w", err)) + } + + return &types.ExportDataProtectionImpactAssessmentsPDFPayload{ + Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)), + }, nil +} + +// ExportTransferImpactAssessmentsPDF is the resolver for the exportTransferImpactAssessmentsPDF field. +func (r *mutationResolver) ExportTransferImpactAssessmentsPDF(ctx context.Context, input types.ExportTransferImpactAssessmentsPDFInput) (*types.ExportTransferImpactAssessmentsPDFPayload, error) { + r.MustBeAuthorized(ctx, input.OrganizationID, authz.ActionExportTransferImpactAssessmentsPDF) + + prb := r.ProboService(ctx, input.OrganizationID.TenantID()) + + var snapshotIDPtr *gid.GID + if input.Filter != nil { + snapshotIDPtr = input.Filter.SnapshotID + } + tiaFilter := coredata.NewTransferImpactAssessmentFilter(&snapshotIDPtr) + + pdf, err := prb.TransferImpactAssessments.ExportPDF(ctx, input.OrganizationID, tiaFilter) + if err != nil { + var errNotFound *coredata.ErrNoTransferImpactAssessmentsFound + if errors.As(err, &errNotFound) { + return nil, gqlutils.NotFound(errNotFound) + } + panic(fmt.Errorf("cannot export transfer impact assessments PDF: %w", err)) + } + + return &types.ExportTransferImpactAssessmentsPDFPayload{ + Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)), + }, nil +} + // CreateVendorRiskAssessment is the resolver for the createVendorRiskAssessment field. func (r *mutationResolver) CreateVendorRiskAssessment(ctx context.Context, input types.CreateVendorRiskAssessmentInput) (*types.CreateVendorRiskAssessmentPayload, error) { r.MustBeAuthorized(ctx, input.VendorID, authz.ActionCreateVendorRiskAssessment)