Add processing activity exports
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
65
pkg/coredata/snapshot_filter.go
Normal file
65
pkg/coredata/snapshot_filter.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// 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
|
||||
)`
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user