Add vendor snapshots

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2025-09-01 10:24:17 +02:00
parent d7ffed9d7f
commit 078102db74
32 changed files with 1381 additions and 426 deletions

View File

@@ -135,42 +135,3 @@ VALUES (
_, err := conn.Exec(ctx, q, args)
return err
}
func (f File) Delete(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
DELETE FROM files WHERE %s AND id = @file_id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"file_id": f.ID}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
return err
}
func (f File) SoftDelete(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
UPDATE files SET deleted_at = @deleted_at, updated_at = @updated_at WHERE %s AND id = @file_id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"file_id": f.ID,
"updated_at": time.Now(),
"deleted_at": time.Now(),
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
return err
}

View File

@@ -0,0 +1,84 @@
ALTER TABLE vendor_business_associate_agreements ADD COLUMN snapshot_id TEXT;
ALTER TABLE vendor_business_associate_agreements ADD COLUMN source_id TEXT;
ALTER TABLE vendor_business_associate_agreements DROP CONSTRAINT vendor_business_associate_agreements_file_id_unique;
ALTER TABLE vendor_business_associate_agreements DROP CONSTRAINT vendor_business_associate_agreements_file_id_fkey;
ALTER TABLE vendor_business_associate_agreements ADD CONSTRAINT vendor_business_associate_agreements_file_id_fkey
FOREIGN KEY (file_id)
REFERENCES files(id)
ON UPDATE CASCADE
ON DELETE RESTRICT;
ALTER TABLE vendor_business_associate_agreements ADD CONSTRAINT vendor_business_associate_agreements_snapshot_id_fkey
FOREIGN KEY (snapshot_id)
REFERENCES snapshots(id)
ON UPDATE CASCADE
ON DELETE CASCADE;
ALTER TABLE vendor_business_associate_agreements ADD CONSTRAINT vendor_business_associate_agreements_source_id_snapshot_id_key
UNIQUE (source_id, snapshot_id);
ALTER TABLE vendor_contacts ADD COLUMN snapshot_id TEXT;
ALTER TABLE vendor_contacts ADD COLUMN source_id TEXT;
ALTER TABLE vendor_contacts ADD CONSTRAINT vendor_contacts_snapshot_id_fkey
FOREIGN KEY (snapshot_id)
REFERENCES snapshots(id)
ON UPDATE CASCADE
ON DELETE CASCADE;
ALTER TABLE vendor_contacts ADD CONSTRAINT vendor_contacts_source_id_snapshot_id_key
UNIQUE (source_id, snapshot_id);
ALTER TABLE vendor_data_privacy_agreements ADD COLUMN snapshot_id TEXT;
ALTER TABLE vendor_data_privacy_agreements ADD COLUMN source_id TEXT;
ALTER TABLE vendor_data_privacy_agreements DROP CONSTRAINT vendor_data_privacy_agreements_file_id_unique;
ALTER TABLE vendor_data_privacy_agreements DROP CONSTRAINT vendor_data_privacy_agreements_file_id_fkey;
ALTER TABLE vendor_data_privacy_agreements ADD CONSTRAINT vendor_data_privacy_agreements_file_id_fkey
FOREIGN KEY (file_id)
REFERENCES files(id)
ON UPDATE CASCADE
ON DELETE RESTRICT;
ALTER TABLE vendor_data_privacy_agreements ADD CONSTRAINT vendor_data_privacy_agreements_snapshot_id_fkey
FOREIGN KEY (snapshot_id)
REFERENCES snapshots(id)
ON UPDATE CASCADE
ON DELETE CASCADE;
ALTER TABLE vendor_data_privacy_agreements ADD CONSTRAINT vendor_data_privacy_agreements_source_id_snapshot_id_key
UNIQUE (source_id, snapshot_id);
ALTER TABLE vendor_risk_assessments ADD COLUMN snapshot_id TEXT;
ALTER TABLE vendor_risk_assessments ADD COLUMN source_id TEXT;
ALTER TABLE vendor_risk_assessments ADD CONSTRAINT vendor_risk_assessments_snapshot_id_fkey
FOREIGN KEY (snapshot_id)
REFERENCES snapshots(id)
ON UPDATE CASCADE
ON DELETE CASCADE;
ALTER TABLE vendor_risk_assessments ADD CONSTRAINT vendor_risk_assessments_source_id_snapshot_id_key
UNIQUE (source_id, snapshot_id);
ALTER TABLE vendor_services ADD COLUMN snapshot_id TEXT;
ALTER TABLE vendor_services ADD COLUMN source_id TEXT;
ALTER TABLE vendor_services ADD CONSTRAINT vendor_services_snapshot_id_fkey
FOREIGN KEY (snapshot_id)
REFERENCES snapshots(id)
ON UPDATE CASCADE
ON DELETE CASCADE;
ALTER TABLE vendor_services ADD CONSTRAINT vendor_services_source_id_snapshot_id_key
UNIQUE (source_id, snapshot_id);
ALTER TABLE vendor_compliance_reports ADD COLUMN snapshot_id TEXT;
ALTER TABLE vendor_compliance_reports ADD COLUMN source_id TEXT;
ALTER TABLE vendor_compliance_reports ADD CONSTRAINT vendor_compliance_reports_snapshot_id_fkey
FOREIGN KEY (snapshot_id)
REFERENCES snapshots(id)
ON UPDATE CASCADE
ON DELETE CASCADE;

View File

@@ -40,6 +40,8 @@ func GetSnapshottable(snapshotType SnapshotsType) (Snapshottable, error) {
return ContinualImprovementRegistries{}, nil
case SnapshotsTypeProcessingActivityRegistries:
return ProcessingActivityRegistries{}, nil
case SnapshotsTypeVendors:
return Vendors{}, nil
default:
return nil, fmt.Errorf("unsupported snapshot type: %s", snapshotType)
}

View File

@@ -57,6 +57,10 @@ type (
}
Vendors []*Vendor
VendorSnapshotter interface {
InsertVendorSnapshots(ctx context.Context, conn pg.Conn, scope Scoper, organizationID, snapshotID gid.GID) error
}
)
func (v Vendor) CursorKey(orderBy VendorOrderField) page.CursorKey {
@@ -914,3 +918,106 @@ FROM source_vendors v
return nil
}
func (v Vendors) Snapshot(ctx context.Context, conn pg.Conn, scope Scoper, organizationID, snapshotID gid.GID) error {
for _, snapshotter := range []VendorSnapshotter{
Vendors{},
VendorServices{},
VendorContacts{},
VendorRiskAssessments{},
VendorComplianceReports{},
VendorBusinessAssociateAgreements{},
VendorDataPrivacyAgreements{},
} {
if err := snapshotter.InsertVendorSnapshots(ctx, conn, scope, organizationID, snapshotID); err != nil {
return fmt.Errorf("cannot create vendor snapshots: (%T) %w", snapshotter, err)
}
}
return nil
}
func (v Vendors) InsertVendorSnapshots(
ctx context.Context,
conn pg.Conn,
scope Scoper,
organizationID gid.GID,
snapshotID gid.GID,
) error {
query := `
INSERT INTO vendors (
tenant_id,
id,
snapshot_id,
source_id,
organization_id,
name,
description,
category,
headquarter_address,
legal_name,
website_url,
privacy_policy_url,
service_level_agreement_url,
data_processing_agreement_url,
business_associate_agreement_url,
subprocessors_list_url,
certifications,
business_owner_id,
security_owner_id,
status_page_url,
terms_of_service_url,
security_page_url,
trust_page_url,
show_on_trust_center,
created_at,
updated_at
)
SELECT
@tenant_id,
generate_gid(decode_base64_unpadded(@tenant_id), @vendor_entity_type),
@snapshot_id,
v.id,
v.organization_id,
v.name,
v.description,
v.category,
v.headquarter_address,
v.legal_name,
v.website_url,
v.privacy_policy_url,
v.service_level_agreement_url,
v.data_processing_agreement_url,
v.business_associate_agreement_url,
v.subprocessors_list_url,
v.certifications,
v.business_owner_id,
v.security_owner_id,
v.status_page_url,
v.terms_of_service_url,
v.security_page_url,
v.trust_page_url,
v.show_on_trust_center,
v.created_at,
v.updated_at
FROM vendors v
WHERE %s AND organization_id = @organization_id AND snapshot_id IS NULL
`
query = fmt.Sprintf(query, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"snapshot_id": snapshotID,
"organization_id": organizationID,
"vendor_entity_type": VendorEntityType,
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, query, args)
if err != nil {
return fmt.Errorf("cannot insert vendor snapshots: %w", err)
}
return nil
}

View File

@@ -34,6 +34,8 @@ type (
ValidFrom *time.Time `db:"valid_from"`
ValidUntil *time.Time `db:"valid_until"`
FileID gid.GID `db:"file_id"`
SnapshotID *gid.GID `db:"snapshot_id"`
SourceID *gid.GID `db:"source_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
@@ -66,6 +68,8 @@ SELECT
valid_from,
valid_until,
file_id,
snapshot_id,
source_id,
created_at,
updated_at
FROM
@@ -110,6 +114,8 @@ SELECT
valid_from,
valid_until,
file_id,
snapshot_id,
source_id,
created_at,
updated_at
FROM
@@ -156,6 +162,7 @@ SET
WHERE
%s
AND id = @id
AND snapshot_id IS NULL
`
q = fmt.Sprintf(q, scope.SQLFragment())
@@ -192,6 +199,8 @@ INSERT INTO
valid_from,
valid_until,
file_id,
snapshot_id,
source_id,
created_at,
updated_at
)
@@ -203,6 +212,8 @@ VALUES (
@valid_from,
@valid_until,
@file_id,
@snapshot_id,
@source_id,
@created_at,
@updated_at
)
@@ -211,6 +222,8 @@ ON CONFLICT (organization_id, vendor_id) DO UPDATE SET
valid_from = EXCLUDED.valid_from,
valid_until = EXCLUDED.valid_until,
file_id = EXCLUDED.file_id,
snapshot_id = EXCLUDED.snapshot_id,
source_id = EXCLUDED.source_id,
updated_at = EXCLUDED.updated_at
`
args := pgx.StrictNamedArgs{
@@ -221,6 +234,8 @@ ON CONFLICT (organization_id, vendor_id) DO UPDATE SET
"valid_from": vbaa.ValidFrom,
"valid_until": vbaa.ValidUntil,
"file_id": vbaa.FileID,
"snapshot_id": vbaa.SnapshotID,
"source_id": vbaa.SourceID,
"created_at": vbaa.CreatedAt,
"updated_at": vbaa.UpdatedAt,
}
@@ -241,6 +256,7 @@ FROM
WHERE
%s
AND id = @id
AND snapshot_id IS NULL
`
q = fmt.Sprintf(q, scope.SQLFragment())
@@ -265,6 +281,7 @@ FROM
WHERE
%s
AND vendor_id = @vendor_id
AND snapshot_id IS NULL
`
q = fmt.Sprintf(q, scope.SQLFragment())
@@ -275,3 +292,65 @@ WHERE
_, err := conn.Exec(ctx, q, args)
return err
}
func (v VendorBusinessAssociateAgreements) InsertVendorSnapshots(
ctx context.Context,
conn pg.Conn,
scope Scoper,
organizationID gid.GID,
snapshotID gid.GID,
) error {
query := `
WITH
snapshot_vendors AS (
SELECT id, source_id
FROM vendors
WHERE organization_id = @organization_id AND snapshot_id = @snapshot_id
)
INSERT INTO vendor_business_associate_agreements (
tenant_id,
id,
snapshot_id,
source_id,
organization_id,
vendor_id,
valid_from,
valid_until,
file_id,
created_at,
updated_at
)
SELECT
@tenant_id,
generate_gid(decode_base64_unpadded(@tenant_id), @vendor_business_associate_agreement_entity_type),
@snapshot_id,
vbaa.id,
vbaa.organization_id,
sv.id,
vbaa.valid_from,
vbaa.valid_until,
vbaa.file_id,
vbaa.created_at,
vbaa.updated_at
FROM vendor_business_associate_agreements vbaa
INNER JOIN snapshot_vendors sv ON sv.source_id = vbaa.vendor_id
WHERE %s AND vbaa.snapshot_id IS NULL
`
query = fmt.Sprintf(query, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"snapshot_id": snapshotID,
"organization_id": organizationID,
"vendor_business_associate_agreement_entity_type": VendorBusinessAssociateAgreementEntityType,
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, query, args)
if err != nil {
return fmt.Errorf("cannot insert vendor business associate agreement snapshots: %w", err)
}
return nil
}

View File

@@ -35,6 +35,8 @@ type (
ReportName string
FileKey string
FileSize int
SnapshotID *gid.GID
SourceID *gid.GID
CreatedAt time.Time
UpdatedAt time.Time
}
@@ -69,6 +71,8 @@ SELECT
report_name,
file_key,
file_size,
snapshot_id,
source_id,
created_at,
updated_at
FROM
@@ -115,6 +119,8 @@ SELECT
report_name,
file_key,
file_size,
snapshot_id,
source_id,
created_at,
updated_at
FROM
@@ -206,6 +212,7 @@ FROM
WHERE
%s
AND id = @id
AND snapshot_id IS NULL
`
q = fmt.Sprintf(q, scope.SQLFragment())
@@ -216,3 +223,67 @@ WHERE
_, err := conn.Exec(ctx, q, args)
return err
}
func (vcrs VendorComplianceReports) InsertVendorSnapshots(
ctx context.Context,
conn pg.Conn,
scope Scoper,
organizationID gid.GID,
snapshotID gid.GID,
) error {
query := `
WITH
snapshot_vendors AS (
SELECT id, source_id
FROM vendors
WHERE organization_id = @organization_id AND snapshot_id = @snapshot_id
)
INSERT INTO vendor_compliance_reports (
tenant_id,
id,
snapshot_id,
source_id,
vendor_id,
report_date,
valid_until,
report_name,
file_key,
file_size,
created_at,
updated_at
)
SELECT
@tenant_id,
generate_gid(decode_base64_unpadded(@tenant_id), @vendor_compliance_report_entity_type),
@snapshot_id,
vcr.id,
sv.id,
vcr.report_date,
vcr.valid_until,
vcr.report_name,
vcr.file_key,
vcr.file_size,
vcr.created_at,
vcr.updated_at
FROM vendor_compliance_reports vcr
INNER JOIN snapshot_vendors sv ON sv.source_id = vcr.vendor_id
WHERE %s AND vcr.snapshot_id IS NULL
`
query = fmt.Sprintf(query, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"snapshot_id": snapshotID,
"organization_id": organizationID,
"vendor_compliance_report_entity_type": VendorComplianceReportEntityType,
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, query, args)
if err != nil {
return fmt.Errorf("cannot insert vendor compliance report snapshots: %w", err)
}
return nil
}

View File

@@ -29,14 +29,16 @@ import (
type (
VendorContact struct {
ID gid.GID `db:"id"`
VendorID gid.GID `db:"vendor_id"`
FullName *string `db:"full_name"`
Email *string `db:"email"`
Phone *string `db:"phone"`
Role *string `db:"role"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
ID gid.GID `db:"id"`
VendorID gid.GID `db:"vendor_id"`
FullName *string `db:"full_name"`
Email *string `db:"email"`
Phone *string `db:"phone"`
Role *string `db:"role"`
SnapshotID *gid.GID `db:"snapshot_id"`
SourceID *gid.GID `db:"source_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
VendorContacts []*VendorContact
@@ -77,6 +79,8 @@ SELECT
email,
phone,
role,
snapshot_id,
source_id,
created_at,
updated_at
FROM
@@ -127,6 +131,8 @@ SELECT
email,
phone,
role,
snapshot_id,
source_id,
created_at,
updated_at
FROM
@@ -228,6 +234,7 @@ SET
WHERE
%s
AND id = @vendor_contact_id
AND snapshot_id IS NULL
`
q = fmt.Sprintf(q, scope.SQLFragment())
@@ -261,6 +268,7 @@ DELETE FROM
WHERE
%s
AND id = @vendor_contact_id
AND snapshot_id IS NULL
`
q = fmt.Sprintf(q, scope.SQLFragment())
@@ -275,3 +283,65 @@ WHERE
return nil
}
func (vc VendorContacts) InsertVendorSnapshots(
ctx context.Context,
conn pg.Conn,
scope Scoper,
organizationID gid.GID,
snapshotID gid.GID,
) error {
query := `
WITH
snapshot_vendors AS (
SELECT id, source_id
FROM vendors
WHERE organization_id = @organization_id AND snapshot_id = @snapshot_id
)
INSERT INTO vendor_contacts (
tenant_id,
id,
snapshot_id,
source_id,
vendor_id,
full_name,
email,
phone,
role,
created_at,
updated_at
)
SELECT
@tenant_id,
generate_gid(decode_base64_unpadded(@tenant_id), @vendor_contact_entity_type),
@snapshot_id,
vc.id,
sv.id,
vc.full_name,
vc.email,
vc.phone,
vc.role,
vc.created_at,
vc.updated_at
FROM vendor_contacts vc
INNER JOIN snapshot_vendors sv ON sv.source_id = vc.vendor_id
WHERE %s AND vc.snapshot_id IS NULL
`
query = fmt.Sprintf(query, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"snapshot_id": snapshotID,
"organization_id": organizationID,
"vendor_contact_entity_type": VendorContactEntityType,
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, query, args)
if err != nil {
return fmt.Errorf("cannot insert vendor contact snapshots: %w", err)
}
return nil
}

View File

@@ -28,15 +28,16 @@ import (
type (
VendorDataPrivacyAgreement struct {
ID gid.GID `db:"id"`
TenantID gid.TenantID `db:"tenant_id"`
OrganizationID gid.GID `db:"organization_id"`
VendorID gid.GID `db:"vendor_id"`
ValidFrom *time.Time `db:"valid_from"`
ValidUntil *time.Time `db:"valid_until"`
FileID gid.GID `db:"file_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
VendorID gid.GID `db:"vendor_id"`
ValidFrom *time.Time `db:"valid_from"`
ValidUntil *time.Time `db:"valid_until"`
FileID gid.GID `db:"file_id"`
SnapshotID *gid.GID `db:"snapshot_id"`
SourceID *gid.GID `db:"source_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
VendorDataPrivacyAgreements []*VendorDataPrivacyAgreement
@@ -62,12 +63,13 @@ func (vdpa *VendorDataPrivacyAgreement) LoadByVendorID(
q := `
SELECT
id,
tenant_id,
organization_id,
vendor_id,
valid_from,
valid_until,
file_id,
snapshot_id,
source_id,
created_at,
updated_at
FROM
@@ -107,12 +109,13 @@ func (vdpa *VendorDataPrivacyAgreement) LoadByID(
q := `
SELECT
id,
tenant_id,
organization_id,
vendor_id,
valid_from,
valid_until,
file_id,
snapshot_id,
source_id,
created_at,
updated_at
FROM
@@ -159,6 +162,7 @@ SET
WHERE
%s
AND id = @id
AND snapshot_id IS NULL
`
q = fmt.Sprintf(q, scope.SQLFragment())
@@ -195,6 +199,8 @@ INSERT INTO
valid_from,
valid_until,
file_id,
snapshot_id,
source_id,
created_at,
updated_at
)
@@ -206,6 +212,8 @@ VALUES (
@valid_from,
@valid_until,
@file_id,
@snapshot_id,
@source_id,
@created_at,
@updated_at
)
@@ -214,6 +222,8 @@ ON CONFLICT (organization_id, vendor_id) DO UPDATE SET
valid_from = EXCLUDED.valid_from,
valid_until = EXCLUDED.valid_until,
file_id = EXCLUDED.file_id,
snapshot_id = EXCLUDED.snapshot_id,
source_id = EXCLUDED.source_id,
updated_at = EXCLUDED.updated_at
`
args := pgx.StrictNamedArgs{
@@ -224,6 +234,8 @@ ON CONFLICT (organization_id, vendor_id) DO UPDATE SET
"valid_from": vdpa.ValidFrom,
"valid_until": vdpa.ValidUntil,
"file_id": vdpa.FileID,
"snapshot_id": vdpa.SnapshotID,
"source_id": vdpa.SourceID,
"created_at": vdpa.CreatedAt,
"updated_at": vdpa.UpdatedAt,
}
@@ -244,6 +256,7 @@ FROM
WHERE
%s
AND id = @id
AND snapshot_id IS NULL
`
q = fmt.Sprintf(q, scope.SQLFragment())
@@ -278,3 +291,65 @@ WHERE
_, err := conn.Exec(ctx, q, args)
return err
}
func (vdpa VendorDataPrivacyAgreements) InsertVendorSnapshots(
ctx context.Context,
conn pg.Conn,
scope Scoper,
organizationID gid.GID,
snapshotID gid.GID,
) error {
query := `
WITH
snapshot_vendors AS (
SELECT id, source_id
FROM vendors
WHERE organization_id = @organization_id AND snapshot_id = @snapshot_id
)
INSERT INTO vendor_data_privacy_agreements (
tenant_id,
id,
snapshot_id,
source_id,
organization_id,
vendor_id,
valid_from,
valid_until,
file_id,
created_at,
updated_at
)
SELECT
@tenant_id,
generate_gid(decode_base64_unpadded(@tenant_id), @vendor_data_privacy_agreement_entity_type),
@snapshot_id,
vdpa.id,
vdpa.organization_id,
sv.id,
vdpa.valid_from,
vdpa.valid_until,
vdpa.file_id,
vdpa.created_at,
vdpa.updated_at
FROM vendor_data_privacy_agreements vdpa
INNER JOIN snapshot_vendors sv ON sv.source_id = vdpa.vendor_id
WHERE %s AND vdpa.snapshot_id IS NULL
`
query = fmt.Sprintf(query, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"snapshot_id": snapshotID,
"organization_id": organizationID,
"vendor_data_privacy_agreement_entity_type": VendorDataPrivacyAgreementEntityType,
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, query, args)
if err != nil {
return fmt.Errorf("cannot insert vendor data privacy agreement snapshots: %w", err)
}
return nil
}

View File

@@ -39,6 +39,8 @@ type (
DataSensitivity DataSensitivity `db:"data_sensitivity"`
BusinessImpact BusinessImpact `db:"business_impact"`
Notes *string `db:"notes"`
SnapshotID *gid.GID `db:"snapshot_id"`
SourceID *gid.GID `db:"source_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
@@ -137,6 +139,8 @@ SELECT
data_sensitivity,
business_impact,
notes,
snapshot_id,
source_id,
created_at,
updated_at
FROM
@@ -187,6 +191,8 @@ SELECT
data_sensitivity,
business_impact,
notes,
snapshot_id,
source_id,
created_at,
updated_at
FROM
@@ -240,6 +246,8 @@ SELECT
data_sensitivity,
business_impact,
notes,
snapshot_id,
source_id,
created_at,
updated_at
FROM
@@ -270,3 +278,73 @@ WHERE
return nil
}
func (v VendorRiskAssessments) InsertVendorSnapshots(
ctx context.Context,
conn pg.Conn,
scope Scoper,
organizationID gid.GID,
snapshotID gid.GID,
) error {
query := `
WITH
snapshot_vendors AS (
SELECT id, source_id
FROM vendors
WHERE organization_id = @organization_id AND snapshot_id = @snapshot_id
)
INSERT INTO vendor_risk_assessments (
tenant_id,
id,
snapshot_id,
source_id,
vendor_id,
assessed_at,
assessed_by,
approved_by,
approved_at,
expires_at,
data_sensitivity,
business_impact,
notes,
created_at,
updated_at
)
SELECT
@tenant_id,
generate_gid(decode_base64_unpadded(@tenant_id), @vendor_risk_assessment_entity_type),
@snapshot_id,
vra.id,
sv.id,
vra.assessed_at,
vra.assessed_by,
vra.approved_by,
vra.approved_at,
vra.expires_at,
vra.data_sensitivity,
vra.business_impact,
vra.notes,
vra.created_at,
vra.updated_at
FROM vendor_risk_assessments vra
INNER JOIN snapshot_vendors sv ON sv.source_id = vra.vendor_id
WHERE %s AND vra.snapshot_id IS NULL
`
query = fmt.Sprintf(query, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"snapshot_id": snapshotID,
"organization_id": organizationID,
"vendor_risk_assessment_entity_type": VendorRiskAssessmentEntityType,
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, query, args)
if err != nil {
return fmt.Errorf("cannot insert vendor risk assessment snapshots: %w", err)
}
return nil
}

View File

@@ -33,6 +33,8 @@ type (
VendorID gid.GID `db:"vendor_id"`
Name string `db:"name"`
Description *string `db:"description"`
SnapshotID *gid.GID `db:"snapshot_id"`
SourceID *gid.GID `db:"source_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
@@ -71,6 +73,8 @@ SELECT
vendor_id,
name,
description,
snapshot_id,
source_id,
created_at,
updated_at
FROM
@@ -119,6 +123,8 @@ SELECT
vendor_id,
name,
description,
snapshot_id,
source_id,
created_at,
updated_at
FROM
@@ -212,6 +218,7 @@ SET
WHERE
%s
AND id = @vendor_service_id
AND snapshot_id IS NULL
`
q = fmt.Sprintf(q, scope.SQLFragment())
@@ -243,6 +250,7 @@ DELETE FROM
WHERE
%s
AND id = @vendor_service_id
AND snapshot_id IS NULL
`
q = fmt.Sprintf(q, scope.SQLFragment())
@@ -257,3 +265,61 @@ WHERE
return nil
}
func (vs VendorServices) InsertVendorSnapshots(
ctx context.Context,
conn pg.Conn,
scope Scoper,
organizationID gid.GID,
snapshotID gid.GID,
) error {
query := `
WITH
snapshot_vendors AS (
SELECT id, source_id
FROM vendors
WHERE organization_id = @organization_id AND snapshot_id = @snapshot_id
)
INSERT INTO vendor_services (
tenant_id,
id,
snapshot_id,
source_id,
vendor_id,
name,
description,
created_at,
updated_at
)
SELECT
@tenant_id,
generate_gid(decode_base64_unpadded(@tenant_id), @vendor_service_entity_type),
@snapshot_id,
vs.id,
sv.id,
vs.name,
vs.description,
vs.created_at,
vs.updated_at
FROM vendor_services vs
INNER JOIN snapshot_vendors sv ON sv.source_id = vs.vendor_id
WHERE %s AND vs.snapshot_id IS NULL
`
query = fmt.Sprintf(query, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"snapshot_id": snapshotID,
"organization_id": organizationID,
"vendor_service_entity_type": VendorServiceEntityType,
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, query, args)
if err != nil {
return fmt.Errorf("cannot insert vendor service snapshots: %w", err)
}
return nil
}