Rename policy to document

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2025-05-29 19:58:23 -07:00
parent c9f2df3cdd
commit e1b4079e1f
76 changed files with 5572 additions and 4690 deletions

View File

@@ -56,11 +56,11 @@ func (c Control) CursorKey(orderBy ControlOrderField) page.CursorKey {
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
}
func (c *Controls) LoadByPolicyID(
func (c *Controls) LoadByDocumentID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
policyID gid.GID,
documentID gid.GID,
cursor *page.Cursor[ControlOrderField],
) error {
q := `
@@ -77,9 +77,9 @@ WITH ctrl AS (
FROM
controls c
INNER JOIN
controls_policies cp ON c.id = cp.control_id
controls_documents cp ON c.id = cp.control_id
WHERE
cp.policy_id = @policy_id
cp.document_id = @document_id
)
SELECT
id,
@@ -97,7 +97,7 @@ WHERE %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
args := pgx.NamedArgs{"policy_id": policyID}
args := pgx.NamedArgs{"document_id": documentID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
@@ -196,9 +196,9 @@ WITH ctrl AS (
FROM
controls c
LEFT JOIN
controls_policies cp ON c.id = cp.control_id
controls_documents cp ON c.id = cp.control_id
LEFT JOIN
risks_policies rp ON cp.policy_id = rp.policy_id
risks_documents rp ON cp.document_id = rp.document_id
LEFT JOIN
controls_measures cm ON c.id = cm.control_id
LEFT JOIN
@@ -449,7 +449,7 @@ UPDATE controls SET
updated_at = @updated_at
WHERE %s
AND id = @control_id
RETURNING
RETURNING
id,
framework_id,
tenant_id,

View File

@@ -26,67 +26,67 @@ import (
)
type (
ControlPolicy struct {
ControlID gid.GID `db:"control_id"`
PolicyID gid.GID `db:"policy_id"`
TenantID gid.TenantID `db:"tenant_id"`
CreatedAt time.Time `db:"created_at"`
ControlDocument struct {
ControlID gid.GID `db:"control_id"`
DocumentID gid.GID `db:"document_id"`
TenantID gid.TenantID `db:"tenant_id"`
CreatedAt time.Time `db:"created_at"`
}
ControlPolicies []*ControlPolicy
ControlDocuments []*ControlDocument
)
func (cp ControlPolicy) Insert(
func (cp ControlDocument) Insert(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
INSERT INTO
controls_policies (
controls_documents (
control_id,
policy_id,
document_id,
tenant_id,
created_at
)
VALUES (
@control_id,
@policy_id,
@document_id,
@tenant_id,
@created_at
);
`
args := pgx.StrictNamedArgs{
"control_id": cp.ControlID,
"policy_id": cp.PolicyID,
"tenant_id": scope.GetTenantID(),
"created_at": cp.CreatedAt,
"control_id": cp.ControlID,
"document_id": cp.DocumentID,
"tenant_id": scope.GetTenantID(),
"created_at": cp.CreatedAt,
}
_, err := conn.Exec(ctx, q, args)
return err
}
func (cp ControlPolicy) Delete(
func (cp ControlDocument) Delete(
ctx context.Context,
conn pg.Conn,
scope Scoper,
controlID gid.GID,
policyID gid.GID,
documentID gid.GID,
) error {
q := `
DELETE
FROM
controls_policies
controls_documents
WHERE
%s
AND control_id = @control_id
AND policy_id = @policy_id;
AND document_id = @document_id;
`
args := pgx.StrictNamedArgs{
"control_id": controlID,
"policy_id": policyID,
"control_id": controlID,
"document_id": documentID,
}
maps.Copy(args, scope.SQLArguments())

View File

@@ -27,7 +27,7 @@ import (
)
type (
Policy struct {
Document struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
OwnerID gid.GID `db:"owner_id"`
@@ -37,25 +37,25 @@ type (
UpdatedAt time.Time `db:"updated_at"`
}
Policies []*Policy
Documents []*Document
)
func (p Policy) CursorKey(orderBy PolicyOrderField) page.CursorKey {
func (p Document) CursorKey(orderBy DocumentOrderField) page.CursorKey {
switch orderBy {
case PolicyOrderFieldCreatedAt:
case DocumentOrderFieldCreatedAt:
return page.NewCursorKey(p.ID, p.CreatedAt)
case PolicyOrderFieldTitle:
case DocumentOrderFieldTitle:
return page.NewCursorKey(p.ID, p.Title)
}
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
}
func (p *Policy) LoadByID(
func (p *Document) LoadByID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
policyID gid.GID,
documentID gid.GID,
) error {
q := `
SELECT
@@ -67,39 +67,39 @@ SELECT
created_at,
updated_at
FROM
policies
documents
WHERE
%s
AND id = @policy_id
AND id = @document_id
LIMIT 1;
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"policy_id": policyID}
args := pgx.StrictNamedArgs{"document_id": documentID}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policies: %w", err)
return fmt.Errorf("cannot query documents: %w", err)
}
policy, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Policy])
document, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Document])
if err != nil {
return fmt.Errorf("cannot collect policy: %w", err)
return fmt.Errorf("cannot collect document: %w", err)
}
*p = policy
*p = document
return nil
}
func (p *Policies) LoadByOrganizationID(
func (p *Documents) LoadByOrganizationID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
organizationID gid.GID,
cursor *page.Cursor[PolicyOrderField],
cursor *page.Cursor[DocumentOrderField],
) error {
q := `
SELECT
@@ -111,7 +111,7 @@ SELECT
created_at,
updated_at
FROM
policies
documents
WHERE
%s
AND organization_id = @organization_id
@@ -126,27 +126,27 @@ WHERE
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policies: %w", err)
return fmt.Errorf("cannot query documents: %w", err)
}
policies, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Policy])
documents, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Document])
if err != nil {
return fmt.Errorf("cannot collect policies: %w", err)
return fmt.Errorf("cannot collect documents: %w", err)
}
*p = policies
*p = documents
return nil
}
func (p Policy) Insert(
func (p Document) Insert(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
INSERT INTO
policies (
documents (
tenant_id,
id,
organization_id,
@@ -158,7 +158,7 @@ INSERT INTO
)
VALUES (
@tenant_id,
@policy_id,
@document_id,
@organization_id,
@owner_id,
@title,
@@ -170,7 +170,7 @@ VALUES (
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"policy_id": p.ID,
"document_id": p.ID,
"organization_id": p.OrganizationID,
"owner_id": p.OwnerID,
"title": p.Title,
@@ -182,44 +182,44 @@ VALUES (
return err
}
func (p Policy) Delete(
func (p Document) Delete(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
DELETE FROM policies WHERE %s AND id = @policy_id
DELETE FROM documents WHERE %s AND id = @document_id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"policy_id": p.ID}
args := pgx.StrictNamedArgs{"document_id": p.ID}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
return err
}
func (p *Policy) Update(
func (p *Document) Update(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
UPDATE
policies
documents
SET
title = @title,
current_published_version = @current_published_version,
owner_id = @owner_id,
updated_at = @updated_at
WHERE %s
AND id = @policy_id
AND id = @document_id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"policy_id": p.ID,
"document_id": p.ID,
"updated_at": time.Now(),
"title": p.Title,
"current_published_version": p.CurrentPublishedVersion,
@@ -229,18 +229,18 @@ WHERE %s
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot update policy: %w", err)
return fmt.Errorf("cannot update document: %w", err)
}
return nil
}
func (p *Policies) LoadByControlID(
func (p *Documents) LoadByControlID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
controlID gid.GID,
cursor *page.Cursor[PolicyOrderField],
cursor *page.Cursor[DocumentOrderField],
) error {
q := `
WITH plcs AS (
@@ -254,9 +254,9 @@ WITH plcs AS (
p.created_at,
p.updated_at
FROM
policies p
documents p
INNER JOIN
controls_policies cp ON p.id = cp.policy_id
controls_documents cp ON p.id = cp.document_id
WHERE
cp.control_id = @control_id
)
@@ -281,25 +281,25 @@ WHERE %s
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policies: %w", err)
return fmt.Errorf("cannot query documents: %w", err)
}
policies, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Policy])
documents, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Document])
if err != nil {
return fmt.Errorf("cannot collect policies: %w", err)
return fmt.Errorf("cannot collect documents: %w", err)
}
*p = policies
*p = documents
return nil
}
func (p *Policies) LoadByRiskID(
func (p *Documents) LoadByRiskID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
riskID gid.GID,
cursor *page.Cursor[PolicyOrderField],
cursor *page.Cursor[DocumentOrderField],
) error {
q := `
WITH plcs AS (
@@ -313,9 +313,9 @@ WITH plcs AS (
p.created_at,
p.updated_at
FROM
policies p
documents p
INNER JOIN
risks_policies rp ON p.id = rp.policy_id
risks_documents rp ON p.id = rp.document_id
WHERE
rp.risk_id = @risk_id
)
@@ -340,15 +340,15 @@ WHERE %s
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policies: %w", err)
return fmt.Errorf("cannot query documents: %w", err)
}
policies, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Policy])
documents, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Document])
if err != nil {
return fmt.Errorf("cannot collect policies: %w", err)
return fmt.Errorf("cannot collect documents: %w", err)
}
*p = policies
*p = documents
return nil
}

View File

@@ -15,27 +15,27 @@
package coredata
type (
PolicyOrderField string
DocumentOrderField string
)
const (
PolicyOrderFieldCreatedAt PolicyOrderField = "CREATED_AT"
PolicyOrderFieldTitle PolicyOrderField = "TITLE"
DocumentOrderFieldCreatedAt DocumentOrderField = "CREATED_AT"
DocumentOrderFieldTitle DocumentOrderField = "TITLE"
)
func (p PolicyOrderField) Column() string {
func (p DocumentOrderField) Column() string {
return string(p)
}
func (p PolicyOrderField) String() string {
func (p DocumentOrderField) String() string {
return string(p)
}
func (p PolicyOrderField) MarshalText() ([]byte, error) {
func (p DocumentOrderField) MarshalText() ([]byte, error) {
return []byte(p.String()), nil
}
func (p *PolicyOrderField) UnmarshalText(text []byte) error {
*p = PolicyOrderField(text)
func (p *DocumentOrderField) UnmarshalText(text []byte) error {
*p = DocumentOrderField(text)
return nil
}

View File

@@ -20,55 +20,55 @@ import (
)
type (
PolicyStatus uint8
DocumentStatus uint8
)
const (
PolicyStatusDraft PolicyStatus = iota
PolicyStatusPublished
DocumentStatusDraft DocumentStatus = iota
DocumentStatusPublished
)
func (ps PolicyStatus) MarshalText() ([]byte, error) {
func (ps DocumentStatus) MarshalText() ([]byte, error) {
return []byte(ps.String()), nil
}
func (ps *PolicyStatus) UnmarshalText(data []byte) error {
func (ps *DocumentStatus) UnmarshalText(data []byte) error {
val := string(data)
switch val {
case PolicyStatusDraft.String():
*ps = PolicyStatusDraft
case PolicyStatusPublished.String():
*ps = PolicyStatusPublished
case DocumentStatusDraft.String():
*ps = DocumentStatusDraft
case DocumentStatusPublished.String():
*ps = DocumentStatusPublished
default:
return fmt.Errorf("invalid PolicyStatus value: %q", val)
return fmt.Errorf("invalid DocumentStatus value: %q", val)
}
return nil
}
func (ps PolicyStatus) String() string {
func (ps DocumentStatus) String() string {
var val string
switch ps {
case PolicyStatusDraft:
case DocumentStatusDraft:
val = "DRAFT"
case PolicyStatusPublished:
case DocumentStatusPublished:
val = "PUBLISHED"
}
return val
}
func (ps *PolicyStatus) Scan(value any) error {
func (ps *DocumentStatus) Scan(value any) error {
val, ok := value.(string)
if !ok {
return fmt.Errorf("invalid scan source for PolicyStatus, expected string got %T", value)
return fmt.Errorf("invalid scan source for DocumentStatus, expected string got %T", value)
}
return ps.UnmarshalText([]byte(val))
}
func (ps PolicyStatus) Value() (driver.Value, error) {
func (ps DocumentStatus) Value() (driver.Value, error) {
return ps.String(), nil
}

View File

@@ -27,34 +27,34 @@ import (
)
type (
PolicyVersion struct {
ID gid.GID `db:"id"`
PolicyID gid.GID `db:"policy_id"`
VersionNumber int `db:"version_number"`
Content string `db:"content"`
Changelog string `db:"changelog"`
CreatedBy gid.GID `db:"created_by"`
Status PolicyStatus `db:"status"`
PublishedBy *gid.GID `db:"published_by"`
PublishedAt *time.Time `db:"published_at"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
DocumentVersion struct {
ID gid.GID `db:"id"`
DocumentID gid.GID `db:"document_id"`
VersionNumber int `db:"version_number"`
Content string `db:"content"`
Changelog string `db:"changelog"`
CreatedBy gid.GID `db:"created_by"`
Status DocumentStatus `db:"status"`
PublishedBy *gid.GID `db:"published_by"`
PublishedAt *time.Time `db:"published_at"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
PolicyVersions []*PolicyVersion
DocumentVersions []*DocumentVersion
)
func (p *PolicyVersions) LoadByPolicyID(
func (p *DocumentVersions) LoadByDocumentID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
policyID gid.GID,
cursor *page.Cursor[PolicyVersionOrderField],
documentID gid.GID,
cursor *page.Cursor[DocumentVersionOrderField],
) error {
q := `
SELECT
id,
policy_id,
document_id,
version_number,
content,
changelog,
@@ -65,53 +65,53 @@ SELECT
created_at,
updated_at
FROM
policy_versions
document_versions
WHERE
%s
AND policy_id = @policy_id
AND document_id = @document_id
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
args := pgx.StrictNamedArgs{"policy_id": policyID}
args := pgx.StrictNamedArgs{"document_id": documentID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policy versions: %w", err)
return fmt.Errorf("cannot query document versions: %w", err)
}
policyVersions, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[PolicyVersion])
documentVersions, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[DocumentVersion])
if err != nil {
return fmt.Errorf("cannot collect policy versions: %w", err)
return fmt.Errorf("cannot collect document versions: %w", err)
}
*p = policyVersions
*p = documentVersions
return nil
}
func (p PolicyVersion) CursorKey(orderBy PolicyVersionOrderField) page.CursorKey {
func (p DocumentVersion) CursorKey(orderBy DocumentVersionOrderField) page.CursorKey {
switch orderBy {
case PolicyVersionOrderFieldCreatedAt:
case DocumentVersionOrderFieldCreatedAt:
return page.NewCursorKey(p.ID, p.CreatedAt)
}
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
}
func (p *PolicyVersion) LoadByID(
func (p *DocumentVersion) LoadByID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
policyVersionID gid.GID,
documentVersionID gid.GID,
) error {
q := `
SELECT
id,
policy_id,
document_id,
version_number,
content,
changelog,
@@ -122,43 +122,43 @@ SELECT
created_at,
updated_at
FROM
policy_versions
document_versions
WHERE
%s
AND id = @policy_version_id
AND id = @document_version_id
LIMIT 1;
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"policy_version_id": policyVersionID}
args := pgx.StrictNamedArgs{"document_version_id": documentVersionID}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policy versions: %w", err)
return fmt.Errorf("cannot query document versions: %w", err)
}
policyVersion, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[PolicyVersion])
documentVersion, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[DocumentVersion])
if err != nil {
return fmt.Errorf("cannot collect policy version: %w", err)
return fmt.Errorf("cannot collect document version: %w", err)
}
*p = policyVersion
*p = documentVersion
return nil
}
func (p PolicyVersion) Insert(
func (p DocumentVersion) Insert(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
INSERT INTO policy_versions (
INSERT INTO document_versions (
tenant_id,
id,
policy_id,
document_id,
version_number,
content,
changelog,
@@ -169,7 +169,7 @@ INSERT INTO policy_versions (
) VALUES (
@tenant_id,
@id,
@policy_id,
@document_id,
@version_number,
@content,
@changelog,
@@ -184,7 +184,7 @@ INSERT INTO policy_versions (
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"id": p.ID,
"policy_id": p.PolicyID,
"document_id": p.DocumentID,
"version_number": p.VersionNumber,
"content": p.Content,
"changelog": p.Changelog,
@@ -196,23 +196,23 @@ INSERT INTO policy_versions (
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("error creating/updating policy version: %w", err)
return fmt.Errorf("error creating/updating document version: %w", err)
}
return nil
}
func (p *PolicyVersion) LoadByPolicyIDAndVersionNumber(
func (p *DocumentVersion) LoadByDocumentIDAndVersionNumber(
ctx context.Context,
conn pg.Conn,
scope Scoper,
policyID gid.GID,
documentID gid.GID,
versionNumber int,
) error {
q := `
SELECT
id,
policy_id,
document_id,
version_number,
content,
changelog,
@@ -223,10 +223,10 @@ SELECT
created_at,
updated_at
FROM
policy_versions
document_versions
WHERE
%s
AND policy_id = @policy_id
AND document_id = @document_id
AND version_number = @version_number
LIMIT 1;
`
@@ -234,7 +234,7 @@ LIMIT 1;
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"policy_id": policyID,
"document_id": documentID,
"version_number": versionNumber,
}
@@ -242,29 +242,29 @@ LIMIT 1;
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policy versions: %w", err)
return fmt.Errorf("cannot query document versions: %w", err)
}
policyVersion, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[PolicyVersion])
documentVersion, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[DocumentVersion])
if err != nil {
return fmt.Errorf("cannot collect policy version: %w", err)
return fmt.Errorf("cannot collect document version: %w", err)
}
*p = policyVersion
*p = documentVersion
return nil
}
func (p *PolicyVersion) LoadLatestVersion(
func (p *DocumentVersion) LoadLatestVersion(
ctx context.Context,
conn pg.Conn,
scope Scoper,
policyID gid.GID,
documentID gid.GID,
) error {
q := `
SELECT
id,
policy_id,
document_id,
version_number,
content,
changelog,
@@ -275,10 +275,10 @@ SELECT
created_at,
updated_at
FROM
policy_versions
document_versions
WHERE
%s
AND policy_id = @policy_id
AND document_id = @document_id
ORDER BY created_at DESC
LIMIT 1;
`
@@ -286,58 +286,58 @@ LIMIT 1;
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"policy_id": policyID,
"document_id": documentID,
}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policy versions: %w", err)
return fmt.Errorf("cannot query document versions: %w", err)
}
policyVersion, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[PolicyVersion])
documentVersion, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[DocumentVersion])
if err != nil {
return fmt.Errorf("cannot collect policy version: %w", err)
return fmt.Errorf("cannot collect document version: %w", err)
}
*p = policyVersion
*p = documentVersion
return nil
}
func (p PolicyVersion) Update(
func (p DocumentVersion) Update(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
UPDATE policy_versions SET
UPDATE document_versions SET
changelog = @changelog,
status = @status,
status = @status,
content = @content,
published_by = @published_by,
published_at = @published_at,
updated_at = @updated_at
WHERE %s
AND id = @policy_version_id;`
AND id = @document_version_id;`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"policy_version_id": p.ID,
"changelog": p.Changelog,
"status": p.Status,
"content": p.Content,
"published_by": p.PublishedBy,
"published_at": p.PublishedAt,
"updated_at": p.UpdatedAt,
"document_version_id": p.ID,
"changelog": p.Changelog,
"status": p.Status,
"content": p.Content,
"published_by": p.PublishedBy,
"published_at": p.PublishedAt,
"updated_at": p.UpdatedAt,
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot update policy version: %w", err)
return fmt.Errorf("cannot update document version: %w", err)
}
return nil

View File

@@ -15,27 +15,27 @@
package coredata
type (
PolicyVersionOrderField string
DocumentVersionOrderField string
)
const (
PolicyVersionOrderFieldCreatedAt PolicyVersionOrderField = "CREATED_AT"
PolicyVersionOrderFieldVersion PolicyVersionOrderField = "VERSION"
DocumentVersionOrderFieldCreatedAt DocumentVersionOrderField = "CREATED_AT"
DocumentVersionOrderFieldVersion DocumentVersionOrderField = "VERSION"
)
func (p PolicyVersionOrderField) Column() string {
func (p DocumentVersionOrderField) Column() string {
return string(p)
}
func (p PolicyVersionOrderField) String() string {
func (p DocumentVersionOrderField) String() string {
return string(p)
}
func (p PolicyVersionOrderField) MarshalText() ([]byte, error) {
func (p DocumentVersionOrderField) MarshalText() ([]byte, error) {
return []byte(p.String()), nil
}
func (p *PolicyVersionOrderField) UnmarshalText(text []byte) error {
*p = PolicyVersionOrderField(text)
func (p *DocumentVersionOrderField) UnmarshalText(text []byte) error {
*p = DocumentVersionOrderField(text)
return nil
}

View File

@@ -0,0 +1,281 @@
// 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 (
"context"
"fmt"
"maps"
"time"
"github.com/getprobo/probo/pkg/gid"
"github.com/getprobo/probo/pkg/page"
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
)
type (
DocumentVersionSignature struct {
ID gid.GID `json:"id"`
DocumentVersionID gid.GID `json:"document_version_id"`
State DocumentVersionSignatureState `json:"state"`
SignedBy gid.GID `json:"signed_by"`
SignedAt *time.Time `json:"signed_at"`
RequestedAt time.Time `json:"requested_at"`
RequestedBy gid.GID `json:"requested_by"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
DocumentVersionSignatures []*DocumentVersionSignature
)
func (pvs DocumentVersionSignature) CursorKey(orderBy DocumentVersionSignatureOrderField) page.CursorKey {
switch orderBy {
case DocumentVersionSignatureOrderFieldCreatedAt:
return page.NewCursorKey(pvs.ID, pvs.CreatedAt)
case DocumentVersionSignatureOrderFieldSignedAt:
return page.NewCursorKey(pvs.ID, pvs.SignedAt)
}
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
}
func (pvs *DocumentVersionSignature) LoadByDocumentVersionIDAndSignatory(
ctx context.Context,
conn pg.Conn,
scope Scoper,
documentVersionID gid.GID,
signatory gid.GID,
) error {
q := `
SELECT
id,
document_version_id,
state,
signed_by,
signed_at,
requested_at,
requested_by,
created_at,
updated_at
FROM
document_version_signatures
WHERE
%s
AND document_version_id = @document_version_id
AND signed_by = @signatory
LIMIT 1
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"document_version_id": documentVersionID, "signatory": signatory}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query document version signature: %w", err)
}
documentVersionSignature, err := pgx.CollectOneRow(rows, pgx.RowToStructByName[DocumentVersionSignature])
if err != nil {
return fmt.Errorf("cannot collect document version signature: %w", err)
}
*pvs = documentVersionSignature
return nil
}
func (pvs *DocumentVersionSignature) LoadByID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
signatureID gid.GID,
) error {
q := `
SELECT
id,
document_version_id,
state,
signed_by,
signed_at,
requested_at,
requested_by,
created_at,
updated_at
FROM
document_version_signatures
WHERE
id = @document_version_signature_id
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"document_version_signature_id": signatureID}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query document version signature: %w", err)
}
documentVersionSignature, err := pgx.CollectOneRow(rows, pgx.RowToStructByName[DocumentVersionSignature])
if err != nil {
return fmt.Errorf("cannot collect document version signature: %w", err)
}
*pvs = documentVersionSignature
return nil
}
func (pvs DocumentVersionSignature) Insert(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
INSERT INTO document_version_signatures (
id,
tenant_id,
document_version_id,
state,
signed_by,
signed_at,
requested_at,
requested_by,
created_at,
updated_at
) VALUES (
@id,
@tenant_id,
@document_version_id,
@state,
@signed_by,
@signed_at,
@requested_at,
@requested_by,
@created_at,
@updated_at
)
`
args := pgx.StrictNamedArgs{
"id": pvs.ID,
"tenant_id": scope.GetTenantID(),
"document_version_id": pvs.DocumentVersionID,
"state": pvs.State,
"signed_by": pvs.SignedBy,
"signed_at": pvs.SignedAt,
"requested_at": pvs.RequestedAt,
"requested_by": pvs.RequestedBy,
"created_at": pvs.CreatedAt,
"updated_at": pvs.UpdatedAt,
}
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot insert document version signature: %w", err)
}
return nil
}
func (pvss *DocumentVersionSignatures) LoadByDocumentVersionID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
documentVersionID gid.GID,
cursor *page.Cursor[DocumentVersionSignatureOrderField],
) error {
q := `
SELECT
id,
document_version_id,
state,
signed_by,
signed_at,
requested_at,
requested_by,
created_at,
updated_at
FROM
document_version_signatures
WHERE
%s
AND document_version_id = @document_version_id
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
args := pgx.StrictNamedArgs{"document_version_id": documentVersionID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query document version signatures: %w", err)
}
documentVersionSignatures, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[DocumentVersionSignature])
if err != nil {
return fmt.Errorf("cannot collect document version signatures: %w", err)
}
*pvss = documentVersionSignatures
return nil
}
func (pvs *DocumentVersionSignature) Update(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
UPDATE document_version_signatures
SET
state = @state,
signed_by = @signed_by,
signed_at = @signed_at,
updated_at = @updated_at
WHERE
%s
AND id = @id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"id": pvs.ID,
"state": pvs.State,
"signed_by": pvs.SignedBy,
"signed_at": pvs.SignedAt,
"updated_at": pvs.UpdatedAt,
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot update document version signature: %w", err)
}
return nil
}

View File

@@ -15,27 +15,27 @@
package coredata
type (
PolicyVersionSignatureOrderField string
DocumentVersionSignatureOrderField string
)
const (
PolicyVersionSignatureOrderFieldCreatedAt PolicyVersionSignatureOrderField = "CREATED_AT"
PolicyVersionSignatureOrderFieldSignedAt PolicyVersionSignatureOrderField = "SIGNED_AT"
DocumentVersionSignatureOrderFieldCreatedAt DocumentVersionSignatureOrderField = "CREATED_AT"
DocumentVersionSignatureOrderFieldSignedAt DocumentVersionSignatureOrderField = "SIGNED_AT"
)
func (p PolicyVersionSignatureOrderField) Column() string {
func (p DocumentVersionSignatureOrderField) Column() string {
return string(p)
}
func (p PolicyVersionSignatureOrderField) String() string {
func (p DocumentVersionSignatureOrderField) String() string {
return string(p)
}
func (p PolicyVersionSignatureOrderField) MarshalText() ([]byte, error) {
func (p DocumentVersionSignatureOrderField) MarshalText() ([]byte, error) {
return []byte(p.String()), nil
}
func (p *PolicyVersionSignatureOrderField) UnmarshalText(text []byte) error {
*p = PolicyVersionSignatureOrderField(text)
func (p *DocumentVersionSignatureOrderField) UnmarshalText(text []byte) error {
*p = DocumentVersionSignatureOrderField(text)
return nil
}

View File

@@ -20,57 +20,57 @@ import (
)
type (
PolicyVersionSignatureState string
DocumentVersionSignatureState string
)
const (
PolicyVersionSignatureStateRequested PolicyVersionSignatureState = "REQUESTED"
PolicyVersionSignatureStateSigned PolicyVersionSignatureState = "SIGNED"
DocumentVersionSignatureStateRequested DocumentVersionSignatureState = "REQUESTED"
DocumentVersionSignatureStateSigned DocumentVersionSignatureState = "SIGNED"
)
func (pvs PolicyVersionSignatureState) MarshalText() ([]byte, error) {
func (pvs DocumentVersionSignatureState) MarshalText() ([]byte, error) {
return []byte(pvs.String()), nil
}
func (pvs *PolicyVersionSignatureState) UnmarshalText(data []byte) error {
func (pvs *DocumentVersionSignatureState) UnmarshalText(data []byte) error {
val := string(data)
switch val {
case PolicyVersionSignatureStateRequested.String():
*pvs = PolicyVersionSignatureStateRequested
case PolicyVersionSignatureStateSigned.String():
*pvs = PolicyVersionSignatureStateSigned
case DocumentVersionSignatureStateRequested.String():
*pvs = DocumentVersionSignatureStateRequested
case DocumentVersionSignatureStateSigned.String():
*pvs = DocumentVersionSignatureStateSigned
default:
return fmt.Errorf("invalid PolicyVersionSignatureState value: %q", val)
return fmt.Errorf("invalid DocumentVersionSignatureState value: %q", val)
}
return nil
}
func (pvs PolicyVersionSignatureState) String() string {
func (pvs DocumentVersionSignatureState) String() string {
var val string
switch pvs {
case PolicyVersionSignatureStateRequested:
case DocumentVersionSignatureStateRequested:
val = "REQUESTED"
case PolicyVersionSignatureStateSigned:
case DocumentVersionSignatureStateSigned:
val = "SIGNED"
default:
panic(fmt.Errorf("invalid PolicyVersionSignatureState value: %q", string(pvs)))
panic(fmt.Errorf("invalid DocumentVersionSignatureState value: %q", string(pvs)))
}
return val
}
func (pvs *PolicyVersionSignatureState) Scan(value any) error {
func (pvs *DocumentVersionSignatureState) Scan(value any) error {
val, ok := value.(string)
if !ok {
return fmt.Errorf("invalid scan source for PolicyVersionSignatureState, expected string got %T", value)
return fmt.Errorf("invalid scan source for DocumentVersionSignatureState, expected string got %T", value)
}
return pvs.UnmarshalText([]byte(val))
}
func (pvs PolicyVersionSignatureState) Value() (driver.Value, error) {
func (pvs DocumentVersionSignatureState) Value() (driver.Value, error) {
return pvs.String(), nil
}

View File

@@ -25,12 +25,12 @@ const (
VendorEntityType
PeopleEntityType
VendorComplianceReportEntityType
PolicyEntityType
DocumentEntityType
UserEntityType
SessionEntityType
EmailEntityType
ControlEntityType
RiskEntityType
PolicyVersionEntityType
PolicyVersionSignatureEntityType
DocumentVersionEntityType
DocumentVersionSignatureEntityType
)

View File

@@ -0,0 +1,36 @@
-- Rename policies table to documents
ALTER TABLE policies RENAME TO documents;
-- Rename risks_policies table to risks_documents
ALTER TABLE risks_policies RENAME TO risks_documents;
-- Update the foreign key reference in risks_documents
ALTER TABLE risks_documents RENAME CONSTRAINT risks_policies_policy_id_fkey TO risks_documents_document_id_fkey;
-- Rename the policy_id column in risks_documents to document_id
ALTER TABLE risks_documents RENAME COLUMN policy_id TO document_id;
-- Rename controls_policies table to controls_documents
ALTER TABLE controls_policies RENAME TO controls_documents;
-- Update controls_documents foreign key and column
ALTER TABLE controls_documents RENAME COLUMN policy_id TO document_id;
ALTER TABLE controls_documents RENAME CONSTRAINT controls_policies_policy_id_fkey TO controls_documents_document_id_fkey;
-- Rename policy_versions table to document_versions
ALTER TABLE policy_versions RENAME TO document_versions;
-- Update document_versions foreign key and column
ALTER TABLE document_versions RENAME COLUMN policy_id TO document_id;
ALTER TABLE document_versions RENAME CONSTRAINT policy_versions_policy_id_fkey TO document_versions_document_id_fkey;
-- Rename policy_version_signatures table to document_version_signatures
ALTER TABLE policy_version_signatures RENAME TO document_version_signatures;
-- Update document_version_signatures foreign key and column
ALTER TABLE document_version_signatures RENAME COLUMN policy_version_id TO document_version_id;
ALTER TABLE document_version_signatures RENAME CONSTRAINT policy_version_signatures_policy_version_id_fkey TO document_version_signatures_document_version_id_fkey;
-- Rename the unique index, preserving the WHERE clause
DROP INDEX policy_one_draft_version_idx;
CREATE UNIQUE INDEX document_one_draft_version_idx ON document_versions (document_id, status) WHERE status = 'DRAFT';

View File

@@ -1,281 +0,0 @@
// 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 (
"context"
"fmt"
"maps"
"time"
"github.com/getprobo/probo/pkg/gid"
"github.com/getprobo/probo/pkg/page"
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
)
type (
PolicyVersionSignature struct {
ID gid.GID `json:"id"`
PolicyVersionID gid.GID `json:"policy_version_id"`
State PolicyVersionSignatureState `json:"state"`
SignedBy gid.GID `json:"signed_by"`
SignedAt *time.Time `json:"signed_at"`
RequestedAt time.Time `json:"requested_at"`
RequestedBy gid.GID `json:"requested_by"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
PolicyVersionSignatures []*PolicyVersionSignature
)
func (pvs PolicyVersionSignature) CursorKey(orderBy PolicyVersionSignatureOrderField) page.CursorKey {
switch orderBy {
case PolicyVersionSignatureOrderFieldCreatedAt:
return page.NewCursorKey(pvs.ID, pvs.CreatedAt)
case PolicyVersionSignatureOrderFieldSignedAt:
return page.NewCursorKey(pvs.ID, pvs.SignedAt)
}
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
}
func (pvs *PolicyVersionSignature) LoadByPolicyVersionIDAndSignatory(
ctx context.Context,
conn pg.Conn,
scope Scoper,
policyVersionID gid.GID,
signatory gid.GID,
) error {
q := `
SELECT
id,
policy_version_id,
state,
signed_by,
signed_at,
requested_at,
requested_by,
created_at,
updated_at
FROM
policy_version_signatures
WHERE
%s
AND policy_version_id = @policy_version_id
AND signed_by = @signatory
LIMIT 1
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"policy_version_id": policyVersionID, "signatory": signatory}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policy version signature: %w", err)
}
policyVersionSignature, err := pgx.CollectOneRow(rows, pgx.RowToStructByName[PolicyVersionSignature])
if err != nil {
return fmt.Errorf("cannot collect policy version signature: %w", err)
}
*pvs = policyVersionSignature
return nil
}
func (pvs *PolicyVersionSignature) LoadByID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
signatureID gid.GID,
) error {
q := `
SELECT
id,
policy_version_id,
state,
signed_by,
signed_at,
requested_at,
requested_by,
created_at,
updated_at
FROM
policy_version_signatures
WHERE
id = @policy_version_signature_id
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"policy_version_signature_id": signatureID}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policy version signature: %w", err)
}
policyVersionSignature, err := pgx.CollectOneRow(rows, pgx.RowToStructByName[PolicyVersionSignature])
if err != nil {
return fmt.Errorf("cannot collect policy version signature: %w", err)
}
*pvs = policyVersionSignature
return nil
}
func (pvs PolicyVersionSignature) Insert(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
INSERT INTO policy_version_signatures (
id,
tenant_id,
policy_version_id,
state,
signed_by,
signed_at,
requested_at,
requested_by,
created_at,
updated_at
) VALUES (
@id,
@tenant_id,
@policy_version_id,
@state,
@signed_by,
@signed_at,
@requested_at,
@requested_by,
@created_at,
@updated_at
)
`
args := pgx.StrictNamedArgs{
"id": pvs.ID,
"tenant_id": scope.GetTenantID(),
"policy_version_id": pvs.PolicyVersionID,
"state": pvs.State,
"signed_by": pvs.SignedBy,
"signed_at": pvs.SignedAt,
"requested_at": pvs.RequestedAt,
"requested_by": pvs.RequestedBy,
"created_at": pvs.CreatedAt,
"updated_at": pvs.UpdatedAt,
}
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot insert policy version signature: %w", err)
}
return nil
}
func (pvss *PolicyVersionSignatures) LoadByPolicyVersionID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
policyVersionID gid.GID,
cursor *page.Cursor[PolicyVersionSignatureOrderField],
) error {
q := `
SELECT
id,
policy_version_id,
state,
signed_by,
signed_at,
requested_at,
requested_by,
created_at,
updated_at
FROM
policy_version_signatures
WHERE
%s
AND policy_version_id = @policy_version_id
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
args := pgx.StrictNamedArgs{"policy_version_id": policyVersionID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policy version signatures: %w", err)
}
policyVersionSignatures, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[PolicyVersionSignature])
if err != nil {
return fmt.Errorf("cannot collect policy version signatures: %w", err)
}
*pvss = policyVersionSignatures
return nil
}
func (pvs *PolicyVersionSignature) Update(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
UPDATE policy_version_signatures
SET
state = @state,
signed_by = @signed_by,
signed_at = @signed_at,
updated_at = @updated_at
WHERE
%s
AND id = @id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"id": pvs.ID,
"state": pvs.State,
"signed_by": pvs.SignedBy,
"signed_at": pvs.SignedAt,
"updated_at": pvs.UpdatedAt,
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot update policy version signature: %w", err)
}
return nil
}

View File

@@ -26,69 +26,69 @@ import (
)
type (
RiskPolicy struct {
RiskID gid.GID `db:"risk_id"`
PolicyID gid.GID `db:"policy_id"`
TenantID gid.TenantID `db:"tenant_id"`
CreatedAt time.Time `db:"created_at"`
RiskDocument struct {
RiskID gid.GID `db:"risk_id"`
DocumentID gid.GID `db:"document_id"`
TenantID gid.TenantID `db:"tenant_id"`
CreatedAt time.Time `db:"created_at"`
}
RiskPolicies []*RiskPolicy
RiskDocuments []*RiskDocument
)
func (rp RiskPolicy) Insert(
func (rp RiskDocument) Insert(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
INSERT INTO
risks_policies (
risks_documents (
risk_id,
policy_id,
document_id,
tenant_id,
created_at
)
VALUES (
@risk_id,
@policy_id,
@document_id,
@tenant_id,
@created_at
);
`
args := pgx.StrictNamedArgs{
"risk_id": rp.RiskID,
"policy_id": rp.PolicyID,
"tenant_id": scope.GetTenantID(),
"created_at": rp.CreatedAt,
"risk_id": rp.RiskID,
"document_id": rp.DocumentID,
"tenant_id": scope.GetTenantID(),
"created_at": rp.CreatedAt,
}
_, err := conn.Exec(ctx, q, args)
return err
}
func (rp RiskPolicy) Delete(
func (rp RiskDocument) Delete(
ctx context.Context,
conn pg.Conn,
scope Scoper,
riskID gid.GID,
policyID gid.GID,
documentID gid.GID,
) error {
q := `
DELETE
FROM
risks_policies
risks_documents
WHERE
%s
AND risk_id = @risk_id
AND policy_id = @policy_id;
AND document_id = @document_id;
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"risk_id": riskID,
"policy_id": policyID,
"risk_id": riskID,
"document_id": documentID,
}
maps.Copy(args, scope.SQLArguments())