|
|
|
|
@@ -12,9 +12,9 @@
|
|
|
|
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
|
|
// PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
|
|
|
|
|
// Command migrate-vendor-snapshots-to-documents creates documents and document
|
|
|
|
|
// versions from existing vendor snapshots. For each organization that has vendor
|
|
|
|
|
// snapshots, it generates a vendor list document using the same ProseMirror
|
|
|
|
|
// Command migrate-thirdParty-snapshots-to-documents creates documents and document
|
|
|
|
|
// versions from existing thirdParty snapshots. For each organization that has thirdParty
|
|
|
|
|
// snapshots, it generates a thirdParty list document using the same ProseMirror
|
|
|
|
|
// builder as the publish flow.
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
@@ -72,22 +72,22 @@ func run() error {
|
|
|
|
|
return migrate(ctx, pgClient, dryRun)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type orgWithVendorSnapshots struct {
|
|
|
|
|
type orgWithThirdPartySnapshots struct {
|
|
|
|
|
organizationID gid.GID
|
|
|
|
|
tenantID gid.TenantID
|
|
|
|
|
organizationName string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type vendorSnapshot struct {
|
|
|
|
|
type thirdPartySnapshot struct {
|
|
|
|
|
snapshotID string
|
|
|
|
|
publishedAt time.Time
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func migrate(ctx context.Context, pgClient *pg.Client, dryRun bool) error {
|
|
|
|
|
var orgs []orgWithVendorSnapshots
|
|
|
|
|
var orgs []orgWithThirdPartySnapshots
|
|
|
|
|
err := pgClient.WithConn(ctx, func(ctx context.Context, conn pg.Querier) error {
|
|
|
|
|
var err error
|
|
|
|
|
orgs, err = loadOrgsWithVendorSnapshots(ctx, conn)
|
|
|
|
|
orgs, err = loadOrgsWithThirdPartySnapshots(ctx, conn)
|
|
|
|
|
return err
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
@@ -95,7 +95,7 @@ func migrate(ctx context.Context, pgClient *pg.Client, dryRun bool) error {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(orgs) == 0 {
|
|
|
|
|
fmt.Println("no organizations with vendor snapshots to migrate")
|
|
|
|
|
fmt.Println("no organizations with thirdParty snapshots to migrate")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -107,14 +107,14 @@ func migrate(ctx context.Context, pgClient *pg.Client, dryRun bool) error {
|
|
|
|
|
if dryRun {
|
|
|
|
|
var count int
|
|
|
|
|
err := pgClient.WithConn(ctx, func(ctx context.Context, conn pg.Querier) error {
|
|
|
|
|
snapshots, err := loadVendorSnapshots(ctx, conn, org.organizationID)
|
|
|
|
|
snapshots, err := loadThirdPartySnapshots(ctx, conn, org.organizationID)
|
|
|
|
|
count = len(snapshots)
|
|
|
|
|
return err
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
fmt.Printf("would migrate org %s (%s) — %d vendor snapshot(s)\n",
|
|
|
|
|
fmt.Printf("would migrate org %s (%s) — %d thirdParty snapshot(s)\n",
|
|
|
|
|
org.organizationID, org.organizationName, count)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
@@ -143,8 +143,8 @@ func migrate(ctx context.Context, pgClient *pg.Client, dryRun bool) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func migrateOrg(ctx context.Context, tx pg.Tx, org orgWithVendorSnapshots) error {
|
|
|
|
|
snapshots, err := loadVendorSnapshots(ctx, tx, org.organizationID)
|
|
|
|
|
func migrateOrg(ctx context.Context, tx pg.Tx, org orgWithThirdPartySnapshots) error {
|
|
|
|
|
snapshots, err := loadThirdPartySnapshots(ctx, tx, org.organizationID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
@@ -186,15 +186,15 @@ INSERT INTO documents (
|
|
|
|
|
|
|
|
|
|
_, err = tx.Exec(
|
|
|
|
|
ctx,
|
|
|
|
|
`INSERT INTO generated_documents (organization_id, tenant_id, vendors_document_id, created_at, updated_at)
|
|
|
|
|
VALUES (@organization_id, @tenant_id, @vendors_document_id, @created_at, @updated_at)
|
|
|
|
|
ON CONFLICT (organization_id) DO UPDATE SET vendors_document_id = @vendors_document_id, updated_at = @updated_at`,
|
|
|
|
|
`INSERT INTO generated_documents (organization_id, tenant_id, third_parties_document_id, created_at, updated_at)
|
|
|
|
|
VALUES (@organization_id, @tenant_id, @third_parties_document_id, @created_at, @updated_at)
|
|
|
|
|
ON CONFLICT (organization_id) DO UPDATE SET third_parties_document_id = @third_parties_document_id, updated_at = @updated_at`,
|
|
|
|
|
pgx.NamedArgs{
|
|
|
|
|
"organization_id": org.organizationID,
|
|
|
|
|
"tenant_id": org.tenantID,
|
|
|
|
|
"vendors_document_id": documentID,
|
|
|
|
|
"created_at": now,
|
|
|
|
|
"updated_at": now,
|
|
|
|
|
"organization_id": org.organizationID,
|
|
|
|
|
"tenant_id": org.tenantID,
|
|
|
|
|
"third_parties_document_id": documentID,
|
|
|
|
|
"created_at": now,
|
|
|
|
|
"updated_at": now,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
@@ -234,7 +234,7 @@ INSERT INTO document_versions (
|
|
|
|
|
"tenant_id": org.tenantID,
|
|
|
|
|
"organization_id": org.organizationID,
|
|
|
|
|
"document_id": documentID,
|
|
|
|
|
"title": "Vendors",
|
|
|
|
|
"title": "ThirdParties",
|
|
|
|
|
"major": major + 1,
|
|
|
|
|
"content": content,
|
|
|
|
|
"published_at": snap.publishedAt,
|
|
|
|
|
@@ -251,7 +251,7 @@ INSERT INTO document_versions (
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func loadOrgsWithVendorSnapshots(ctx context.Context, conn pg.Querier) ([]orgWithVendorSnapshots, error) {
|
|
|
|
|
func loadOrgsWithThirdPartySnapshots(ctx context.Context, conn pg.Querier) ([]orgWithThirdPartySnapshots, error) {
|
|
|
|
|
rows, err := conn.Query(
|
|
|
|
|
ctx,
|
|
|
|
|
`
|
|
|
|
|
@@ -263,7 +263,7 @@ SELECT DISTINCT
|
|
|
|
|
FROM organizations o
|
|
|
|
|
WHERE NOT EXISTS (
|
|
|
|
|
SELECT 1 FROM generated_documents gd
|
|
|
|
|
WHERE gd.organization_id = o.id AND gd.vendors_document_id IS NOT NULL
|
|
|
|
|
WHERE gd.organization_id = o.id AND gd.third_parties_document_id IS NOT NULL
|
|
|
|
|
)
|
|
|
|
|
AND EXISTS (
|
|
|
|
|
SELECT 1 FROM snapshots s
|
|
|
|
|
@@ -273,13 +273,13 @@ ORDER BY o.created_at;
|
|
|
|
|
`,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("cannot query organizations with vendor snapshots: %w", err)
|
|
|
|
|
return nil, fmt.Errorf("cannot query organizations with thirdParty snapshots: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
|
|
var result []orgWithVendorSnapshots
|
|
|
|
|
var result []orgWithThirdPartySnapshots
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
var o orgWithVendorSnapshots
|
|
|
|
|
var o orgWithThirdPartySnapshots
|
|
|
|
|
var createdAt time.Time
|
|
|
|
|
if err := rows.Scan(&o.organizationID, &o.tenantID, &o.organizationName, &createdAt); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("cannot scan organization: %w", err)
|
|
|
|
|
@@ -290,7 +290,7 @@ ORDER BY o.created_at;
|
|
|
|
|
return result, rows.Err()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func loadVendorSnapshots(ctx context.Context, conn pg.Querier, organizationID gid.GID) ([]vendorSnapshot, error) {
|
|
|
|
|
func loadThirdPartySnapshots(ctx context.Context, conn pg.Querier, organizationID gid.GID) ([]thirdPartySnapshot, error) {
|
|
|
|
|
rows, err := conn.Query(
|
|
|
|
|
ctx,
|
|
|
|
|
`
|
|
|
|
|
@@ -305,13 +305,13 @@ ORDER BY s.created_at ASC;
|
|
|
|
|
pgx.NamedArgs{"organization_id": organizationID},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("cannot query vendor snapshots for org %s: %w", organizationID, err)
|
|
|
|
|
return nil, fmt.Errorf("cannot query thirdParty snapshots for org %s: %w", organizationID, err)
|
|
|
|
|
}
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
|
|
var result []vendorSnapshot
|
|
|
|
|
var result []thirdPartySnapshot
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
var s vendorSnapshot
|
|
|
|
|
var s thirdPartySnapshot
|
|
|
|
|
if err := rows.Scan(&s.snapshotID, &s.publishedAt); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("cannot scan snapshot: %w", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -321,7 +321,7 @@ ORDER BY s.created_at ASC;
|
|
|
|
|
return result, rows.Err()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type vendorInfo struct {
|
|
|
|
|
type thirdPartyInfo struct {
|
|
|
|
|
id string
|
|
|
|
|
name string
|
|
|
|
|
category string
|
|
|
|
|
@@ -352,7 +352,7 @@ func buildSnapshotContent(
|
|
|
|
|
orgName string,
|
|
|
|
|
publishedAt time.Time,
|
|
|
|
|
) (string, error) {
|
|
|
|
|
vendorRows, err := tx.Query(
|
|
|
|
|
thirdPartyRows, err := tx.Query(
|
|
|
|
|
ctx,
|
|
|
|
|
`
|
|
|
|
|
SELECT
|
|
|
|
|
@@ -376,7 +376,7 @@ SELECT
|
|
|
|
|
v.countries,
|
|
|
|
|
COALESCE(bo.full_name, 'Not assigned'),
|
|
|
|
|
COALESCE(so.full_name, 'Not assigned')
|
|
|
|
|
FROM vendors v
|
|
|
|
|
FROM third_parties v
|
|
|
|
|
LEFT JOIN iam_membership_profiles bo ON bo.id = v.business_owner_profile_id
|
|
|
|
|
LEFT JOIN iam_membership_profiles so ON so.id = v.security_owner_profile_id
|
|
|
|
|
WHERE v.snapshot_id = @snapshot_id
|
|
|
|
|
@@ -385,14 +385,14 @@ ORDER BY v.name ASC;
|
|
|
|
|
pgx.NamedArgs{"snapshot_id": snapshotID},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", fmt.Errorf("cannot load snapshot vendors: %w", err)
|
|
|
|
|
return "", fmt.Errorf("cannot load snapshot thirdParties: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer vendorRows.Close()
|
|
|
|
|
defer thirdPartyRows.Close()
|
|
|
|
|
|
|
|
|
|
var vendors []vendorInfo
|
|
|
|
|
for vendorRows.Next() {
|
|
|
|
|
var v vendorInfo
|
|
|
|
|
if err := vendorRows.Scan(
|
|
|
|
|
var thirdParties []thirdPartyInfo
|
|
|
|
|
for thirdPartyRows.Next() {
|
|
|
|
|
var v thirdPartyInfo
|
|
|
|
|
if err := thirdPartyRows.Scan(
|
|
|
|
|
&v.id, &v.name, &v.category,
|
|
|
|
|
&v.legalName, &v.description, &v.headquarterAddress,
|
|
|
|
|
&v.websiteURL, &v.privacyPolicyURL, &v.serviceLevelAgreementURL,
|
|
|
|
|
@@ -402,52 +402,52 @@ ORDER BY v.name ASC;
|
|
|
|
|
&v.certifications, &v.countries,
|
|
|
|
|
&v.businessOwnerName, &v.securityOwnerName,
|
|
|
|
|
); err != nil {
|
|
|
|
|
return "", fmt.Errorf("cannot scan vendor: %w", err)
|
|
|
|
|
return "", fmt.Errorf("cannot scan thirdParty: %w", err)
|
|
|
|
|
}
|
|
|
|
|
vendors = append(vendors, v)
|
|
|
|
|
thirdParties = append(thirdParties, v)
|
|
|
|
|
}
|
|
|
|
|
if err := vendorRows.Err(); err != nil {
|
|
|
|
|
if err := thirdPartyRows.Err(); err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vendorIDs := make([]string, len(vendors))
|
|
|
|
|
for i, v := range vendors {
|
|
|
|
|
vendorIDs[i] = v.id
|
|
|
|
|
thirdPartyIDs := make([]string, len(thirdParties))
|
|
|
|
|
for i, v := range thirdParties {
|
|
|
|
|
thirdPartyIDs[i] = v.id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
servicesByVendor, err := loadSnapshotServices(ctx, tx, snapshotID, vendorIDs)
|
|
|
|
|
servicesByThirdParty, err := loadSnapshotServices(ctx, tx, snapshotID, thirdPartyIDs)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
contactsByVendor, err := loadSnapshotContacts(ctx, tx, snapshotID, vendorIDs)
|
|
|
|
|
contactsByThirdParty, err := loadSnapshotContacts(ctx, tx, snapshotID, thirdPartyIDs)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assessmentsByVendor, err := loadSnapshotRiskAssessments(ctx, tx, snapshotID, vendorIDs)
|
|
|
|
|
assessmentsByThirdParty, err := loadSnapshotRiskAssessments(ctx, tx, snapshotID, thirdPartyIDs)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reportsByVendor, err := loadSnapshotComplianceReports(ctx, tx, snapshotID, vendorIDs)
|
|
|
|
|
reportsByThirdParty, err := loadSnapshotComplianceReports(ctx, tx, snapshotID, thirdPartyIDs)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
baaByVendor, err := loadSnapshotBAAs(ctx, tx, snapshotID, vendorIDs)
|
|
|
|
|
baaByThirdParty, err := loadSnapshotBAAs(ctx, tx, snapshotID, thirdPartyIDs)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dpaByVendor, err := loadSnapshotDPAs(ctx, tx, snapshotID, vendorIDs)
|
|
|
|
|
dpaByThirdParty, err := loadSnapshotDPAs(ctx, tx, snapshotID, thirdPartyIDs)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rows := make([]docgen.VendorListRow, 0, len(vendors))
|
|
|
|
|
for _, v := range vendors {
|
|
|
|
|
row := docgen.VendorListRow{
|
|
|
|
|
rows := make([]docgen.ThirdPartyListRow, 0, len(thirdParties))
|
|
|
|
|
for _, v := range thirdParties {
|
|
|
|
|
row := docgen.ThirdPartyListRow{
|
|
|
|
|
Name: v.name,
|
|
|
|
|
LegalName: deref(v.legalName),
|
|
|
|
|
Description: deref(v.description),
|
|
|
|
|
@@ -467,99 +467,99 @@ ORDER BY v.name ASC;
|
|
|
|
|
Countries: joinOrDefault(v.countries),
|
|
|
|
|
BusinessOwner: v.businessOwnerName,
|
|
|
|
|
SecurityOwner: v.securityOwnerName,
|
|
|
|
|
Services: servicesByVendor[v.id],
|
|
|
|
|
Contacts: contactsByVendor[v.id],
|
|
|
|
|
RiskAssessments: assessmentsByVendor[v.id],
|
|
|
|
|
ComplianceReports: reportsByVendor[v.id],
|
|
|
|
|
BusinessAssociateAgreement: baaByVendor[v.id],
|
|
|
|
|
DataPrivacyAgreement: dpaByVendor[v.id],
|
|
|
|
|
Services: servicesByThirdParty[v.id],
|
|
|
|
|
Contacts: contactsByThirdParty[v.id],
|
|
|
|
|
RiskAssessments: assessmentsByThirdParty[v.id],
|
|
|
|
|
ComplianceReports: reportsByThirdParty[v.id],
|
|
|
|
|
BusinessAssociateAgreement: baaByThirdParty[v.id],
|
|
|
|
|
DataPrivacyAgreement: dpaByThirdParty[v.id],
|
|
|
|
|
}
|
|
|
|
|
rows = append(rows, row)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
docData := docgen.VendorListData{
|
|
|
|
|
Title: "Vendors",
|
|
|
|
|
OrganizationName: orgName,
|
|
|
|
|
CreatedAt: publishedAt,
|
|
|
|
|
TotalVendors: len(rows),
|
|
|
|
|
Rows: rows,
|
|
|
|
|
docData := docgen.ThirdPartyListData{
|
|
|
|
|
Title: "ThirdParties",
|
|
|
|
|
OrganizationName: orgName,
|
|
|
|
|
CreatedAt: publishedAt,
|
|
|
|
|
TotalThirdParties: len(rows),
|
|
|
|
|
Rows: rows,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return probo.BuildVendorListDocument(docData)
|
|
|
|
|
return probo.BuildThirdPartyListDocument(docData)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func loadSnapshotServices(ctx context.Context, tx pg.Tx, snapshotID string, vendorIDs []string) (map[string][]docgen.VendorListService, error) {
|
|
|
|
|
func loadSnapshotServices(ctx context.Context, tx pg.Tx, snapshotID string, thirdPartyIDs []string) (map[string][]docgen.ThirdPartyListService, error) {
|
|
|
|
|
rows, err := tx.Query(ctx,
|
|
|
|
|
`SELECT vs.vendor_id, vs.name, COALESCE(vs.description, 'Not specified')
|
|
|
|
|
FROM vendor_services vs
|
|
|
|
|
WHERE vs.snapshot_id = @snapshot_id AND vs.vendor_id = ANY(@vendor_ids)
|
|
|
|
|
ORDER BY vs.vendor_id, vs.name ASC`,
|
|
|
|
|
pgx.NamedArgs{"snapshot_id": snapshotID, "vendor_ids": vendorIDs})
|
|
|
|
|
`SELECT vs.third_party_id, vs.name, COALESCE(vs.description, 'Not specified')
|
|
|
|
|
FROM third_party_services vs
|
|
|
|
|
WHERE vs.snapshot_id = @snapshot_id AND vs.third_party_id = ANY(@third_party_ids)
|
|
|
|
|
ORDER BY vs.third_party_id, vs.name ASC`,
|
|
|
|
|
pgx.NamedArgs{"snapshot_id": snapshotID, "third_party_ids": thirdPartyIDs})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("cannot load snapshot services: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
|
|
result := make(map[string][]docgen.VendorListService)
|
|
|
|
|
result := make(map[string][]docgen.ThirdPartyListService)
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
var vendorID, name, desc string
|
|
|
|
|
if err := rows.Scan(&vendorID, &name, &desc); err != nil {
|
|
|
|
|
var thirdPartyID, name, desc string
|
|
|
|
|
if err := rows.Scan(&thirdPartyID, &name, &desc); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("cannot scan service: %w", err)
|
|
|
|
|
}
|
|
|
|
|
result[vendorID] = append(result[vendorID], docgen.VendorListService{Name: name, Description: desc})
|
|
|
|
|
result[thirdPartyID] = append(result[thirdPartyID], docgen.ThirdPartyListService{Name: name, Description: desc})
|
|
|
|
|
}
|
|
|
|
|
return result, rows.Err()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func loadSnapshotContacts(ctx context.Context, tx pg.Tx, snapshotID string, vendorIDs []string) (map[string][]docgen.VendorListContact, error) {
|
|
|
|
|
func loadSnapshotContacts(ctx context.Context, tx pg.Tx, snapshotID string, thirdPartyIDs []string) (map[string][]docgen.ThirdPartyListContact, error) {
|
|
|
|
|
rows, err := tx.Query(ctx,
|
|
|
|
|
`SELECT vc.vendor_id,
|
|
|
|
|
`SELECT vc.third_party_id,
|
|
|
|
|
COALESCE(vc.full_name, 'Not specified'),
|
|
|
|
|
COALESCE(vc.email, 'Not specified'),
|
|
|
|
|
COALESCE(vc.phone, 'Not specified'),
|
|
|
|
|
COALESCE(vc.role, 'Not specified')
|
|
|
|
|
FROM vendor_contacts vc
|
|
|
|
|
WHERE vc.snapshot_id = @snapshot_id AND vc.vendor_id = ANY(@vendor_ids)
|
|
|
|
|
ORDER BY vc.vendor_id, vc.full_name ASC`,
|
|
|
|
|
pgx.NamedArgs{"snapshot_id": snapshotID, "vendor_ids": vendorIDs})
|
|
|
|
|
FROM third_party_contacts vc
|
|
|
|
|
WHERE vc.snapshot_id = @snapshot_id AND vc.third_party_id = ANY(@third_party_ids)
|
|
|
|
|
ORDER BY vc.third_party_id, vc.full_name ASC`,
|
|
|
|
|
pgx.NamedArgs{"snapshot_id": snapshotID, "third_party_ids": thirdPartyIDs})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("cannot load snapshot contacts: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
|
|
result := make(map[string][]docgen.VendorListContact)
|
|
|
|
|
result := make(map[string][]docgen.ThirdPartyListContact)
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
var vendorID, name, email, phone, role string
|
|
|
|
|
if err := rows.Scan(&vendorID, &name, &email, &phone, &role); err != nil {
|
|
|
|
|
var thirdPartyID, name, email, phone, role string
|
|
|
|
|
if err := rows.Scan(&thirdPartyID, &name, &email, &phone, &role); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("cannot scan contact: %w", err)
|
|
|
|
|
}
|
|
|
|
|
result[vendorID] = append(result[vendorID], docgen.VendorListContact{
|
|
|
|
|
result[thirdPartyID] = append(result[thirdPartyID], docgen.ThirdPartyListContact{
|
|
|
|
|
FullName: name, Email: email, Phone: phone, Role: role,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
return result, rows.Err()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func loadSnapshotRiskAssessments(ctx context.Context, tx pg.Tx, snapshotID string, vendorIDs []string) (map[string][]docgen.VendorListRiskAssessment, error) {
|
|
|
|
|
func loadSnapshotRiskAssessments(ctx context.Context, tx pg.Tx, snapshotID string, thirdPartyIDs []string) (map[string][]docgen.ThirdPartyListRiskAssessment, error) {
|
|
|
|
|
rows, err := tx.Query(ctx,
|
|
|
|
|
`SELECT vra.vendor_id, vra.created_at, vra.expires_at, vra.data_sensitivity, vra.business_impact, COALESCE(vra.notes, 'Not specified')
|
|
|
|
|
FROM vendor_risk_assessments vra
|
|
|
|
|
WHERE vra.snapshot_id = @snapshot_id AND vra.vendor_id = ANY(@vendor_ids)
|
|
|
|
|
ORDER BY vra.vendor_id, vra.created_at DESC`,
|
|
|
|
|
pgx.NamedArgs{"snapshot_id": snapshotID, "vendor_ids": vendorIDs})
|
|
|
|
|
`SELECT vra.third_party_id, vra.created_at, vra.expires_at, vra.data_sensitivity, vra.business_impact, COALESCE(vra.notes, 'Not specified')
|
|
|
|
|
FROM third_party_risk_assessments vra
|
|
|
|
|
WHERE vra.snapshot_id = @snapshot_id AND vra.third_party_id = ANY(@third_party_ids)
|
|
|
|
|
ORDER BY vra.third_party_id, vra.created_at DESC`,
|
|
|
|
|
pgx.NamedArgs{"snapshot_id": snapshotID, "third_party_ids": thirdPartyIDs})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("cannot load snapshot risk assessments: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
|
|
result := make(map[string][]docgen.VendorListRiskAssessment)
|
|
|
|
|
result := make(map[string][]docgen.ThirdPartyListRiskAssessment)
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
var vendorID, sensitivity, impact, notes string
|
|
|
|
|
var thirdPartyID, sensitivity, impact, notes string
|
|
|
|
|
var assessedAt, expiresAt time.Time
|
|
|
|
|
if err := rows.Scan(&vendorID, &assessedAt, &expiresAt, &sensitivity, &impact, ¬es); err != nil {
|
|
|
|
|
if err := rows.Scan(&thirdPartyID, &assessedAt, &expiresAt, &sensitivity, &impact, ¬es); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("cannot scan risk assessment: %w", err)
|
|
|
|
|
}
|
|
|
|
|
result[vendorID] = append(result[vendorID], docgen.VendorListRiskAssessment{
|
|
|
|
|
result[thirdPartyID] = append(result[thirdPartyID], docgen.ThirdPartyListRiskAssessment{
|
|
|
|
|
AssessedAt: assessedAt.Format("2006-01-02"),
|
|
|
|
|
ExpiresAt: expiresAt.Format("2006-01-02"),
|
|
|
|
|
DataSensitivity: sensitivity,
|
|
|
|
|
@@ -570,81 +570,81 @@ func loadSnapshotRiskAssessments(ctx context.Context, tx pg.Tx, snapshotID strin
|
|
|
|
|
return result, rows.Err()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func loadSnapshotComplianceReports(ctx context.Context, tx pg.Tx, snapshotID string, vendorIDs []string) (map[string][]docgen.VendorListComplianceReport, error) {
|
|
|
|
|
func loadSnapshotComplianceReports(ctx context.Context, tx pg.Tx, snapshotID string, thirdPartyIDs []string) (map[string][]docgen.ThirdPartyListComplianceReport, error) {
|
|
|
|
|
rows, err := tx.Query(ctx,
|
|
|
|
|
`SELECT vcr.vendor_id, vcr.report_name, vcr.report_date, vcr.valid_until
|
|
|
|
|
FROM vendor_compliance_reports vcr
|
|
|
|
|
WHERE vcr.snapshot_id = @snapshot_id AND vcr.vendor_id = ANY(@vendor_ids)
|
|
|
|
|
ORDER BY vcr.vendor_id, vcr.report_date DESC`,
|
|
|
|
|
pgx.NamedArgs{"snapshot_id": snapshotID, "vendor_ids": vendorIDs})
|
|
|
|
|
`SELECT vcr.third_party_id, vcr.report_name, vcr.report_date, vcr.valid_until
|
|
|
|
|
FROM third_party_compliance_reports vcr
|
|
|
|
|
WHERE vcr.snapshot_id = @snapshot_id AND vcr.third_party_id = ANY(@third_party_ids)
|
|
|
|
|
ORDER BY vcr.third_party_id, vcr.report_date DESC`,
|
|
|
|
|
pgx.NamedArgs{"snapshot_id": snapshotID, "third_party_ids": thirdPartyIDs})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("cannot load snapshot compliance reports: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
|
|
result := make(map[string][]docgen.VendorListComplianceReport)
|
|
|
|
|
result := make(map[string][]docgen.ThirdPartyListComplianceReport)
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
var vendorID, name string
|
|
|
|
|
var thirdPartyID, name string
|
|
|
|
|
var reportDate time.Time
|
|
|
|
|
var validUntil *time.Time
|
|
|
|
|
if err := rows.Scan(&vendorID, &name, &reportDate, &validUntil); err != nil {
|
|
|
|
|
if err := rows.Scan(&thirdPartyID, &name, &reportDate, &validUntil); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("cannot scan compliance report: %w", err)
|
|
|
|
|
}
|
|
|
|
|
vu := "Not specified"
|
|
|
|
|
if validUntil != nil {
|
|
|
|
|
vu = validUntil.Format("2006-01-02")
|
|
|
|
|
}
|
|
|
|
|
result[vendorID] = append(result[vendorID], docgen.VendorListComplianceReport{
|
|
|
|
|
result[thirdPartyID] = append(result[thirdPartyID], docgen.ThirdPartyListComplianceReport{
|
|
|
|
|
ReportName: name, ReportDate: reportDate.Format("2006-01-02"), ValidUntil: vu,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
return result, rows.Err()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func loadSnapshotBAAs(ctx context.Context, tx pg.Tx, snapshotID string, vendorIDs []string) (map[string]*docgen.VendorListAgreement, error) {
|
|
|
|
|
func loadSnapshotBAAs(ctx context.Context, tx pg.Tx, snapshotID string, thirdPartyIDs []string) (map[string]*docgen.ThirdPartyListAgreement, error) {
|
|
|
|
|
rows, err := tx.Query(ctx,
|
|
|
|
|
`SELECT vbaa.vendor_id, vbaa.valid_from, vbaa.valid_until
|
|
|
|
|
FROM vendor_business_associate_agreements vbaa
|
|
|
|
|
WHERE vbaa.snapshot_id = @snapshot_id AND vbaa.vendor_id = ANY(@vendor_ids)`,
|
|
|
|
|
pgx.NamedArgs{"snapshot_id": snapshotID, "vendor_ids": vendorIDs})
|
|
|
|
|
`SELECT vbaa.third_party_id, vbaa.valid_from, vbaa.valid_until
|
|
|
|
|
FROM third_party_business_associate_agreements vbaa
|
|
|
|
|
WHERE vbaa.snapshot_id = @snapshot_id AND vbaa.third_party_id = ANY(@third_party_ids)`,
|
|
|
|
|
pgx.NamedArgs{"snapshot_id": snapshotID, "third_party_ids": thirdPartyIDs})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("cannot load snapshot BAAs: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
|
|
result := make(map[string]*docgen.VendorListAgreement)
|
|
|
|
|
result := make(map[string]*docgen.ThirdPartyListAgreement)
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
var vendorID string
|
|
|
|
|
var thirdPartyID string
|
|
|
|
|
var validFrom, validUntil *time.Time
|
|
|
|
|
if err := rows.Scan(&vendorID, &validFrom, &validUntil); err != nil {
|
|
|
|
|
if err := rows.Scan(&thirdPartyID, &validFrom, &validUntil); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("cannot scan BAA: %w", err)
|
|
|
|
|
}
|
|
|
|
|
result[vendorID] = &docgen.VendorListAgreement{
|
|
|
|
|
result[thirdPartyID] = &docgen.ThirdPartyListAgreement{
|
|
|
|
|
ValidFrom: fmtTime(validFrom), ValidUntil: fmtTime(validUntil),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result, rows.Err()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func loadSnapshotDPAs(ctx context.Context, tx pg.Tx, snapshotID string, vendorIDs []string) (map[string]*docgen.VendorListAgreement, error) {
|
|
|
|
|
func loadSnapshotDPAs(ctx context.Context, tx pg.Tx, snapshotID string, thirdPartyIDs []string) (map[string]*docgen.ThirdPartyListAgreement, error) {
|
|
|
|
|
rows, err := tx.Query(ctx,
|
|
|
|
|
`SELECT vdpa.vendor_id, vdpa.valid_from, vdpa.valid_until
|
|
|
|
|
FROM vendor_data_privacy_agreements vdpa
|
|
|
|
|
WHERE vdpa.snapshot_id = @snapshot_id AND vdpa.vendor_id = ANY(@vendor_ids)`,
|
|
|
|
|
pgx.NamedArgs{"snapshot_id": snapshotID, "vendor_ids": vendorIDs})
|
|
|
|
|
`SELECT vdpa.third_party_id, vdpa.valid_from, vdpa.valid_until
|
|
|
|
|
FROM third_party_data_privacy_agreements vdpa
|
|
|
|
|
WHERE vdpa.snapshot_id = @snapshot_id AND vdpa.third_party_id = ANY(@third_party_ids)`,
|
|
|
|
|
pgx.NamedArgs{"snapshot_id": snapshotID, "third_party_ids": thirdPartyIDs})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("cannot load snapshot DPAs: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
|
|
result := make(map[string]*docgen.VendorListAgreement)
|
|
|
|
|
result := make(map[string]*docgen.ThirdPartyListAgreement)
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
var vendorID string
|
|
|
|
|
var thirdPartyID string
|
|
|
|
|
var validFrom, validUntil *time.Time
|
|
|
|
|
if err := rows.Scan(&vendorID, &validFrom, &validUntil); err != nil {
|
|
|
|
|
if err := rows.Scan(&thirdPartyID, &validFrom, &validUntil); err != nil {
|
|
|
|
|
return nil, fmt.Errorf("cannot scan DPA: %w", err)
|
|
|
|
|
}
|
|
|
|
|
result[vendorID] = &docgen.VendorListAgreement{
|
|
|
|
|
result[thirdPartyID] = &docgen.ThirdPartyListAgreement{
|
|
|
|
|
ValidFrom: fmtTime(validFrom), ValidUntil: fmtTime(validUntil),
|
|
|
|
|
}
|
|
|
|
|
}
|