Refatcor policies management
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -31,4 +31,6 @@ const (
|
||||
EmailEntityType
|
||||
ControlEntityType
|
||||
RiskEntityType
|
||||
PolicyVersionEntityType
|
||||
PolicyVersionSignatureEntityType
|
||||
)
|
||||
|
||||
60
pkg/coredata/migrations/20250424T094500Z.sql
Normal file
60
pkg/coredata/migrations/20250424T094500Z.sql
Normal file
@@ -0,0 +1,60 @@
|
||||
CREATE TABLE policy_versions (
|
||||
id TEXT PRIMARY KEY,
|
||||
tenant_id TEXT NOT NULL,
|
||||
policy_id TEXT NOT NULL REFERENCES policies(id) ON DELETE CASCADE,
|
||||
version_number INTEGER NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
changelog TEXT NOT NULL,
|
||||
created_by TEXT NOT NULL,
|
||||
status policy_status NOT NULL,
|
||||
published_by TEXT REFERENCES peoples(id),
|
||||
published_at TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
UNIQUE(policy_id, version_number)
|
||||
);
|
||||
|
||||
INSERT INTO policy_versions (
|
||||
id,
|
||||
tenant_id,
|
||||
policy_id,
|
||||
version_number,
|
||||
content,
|
||||
changelog,
|
||||
created_by,
|
||||
status,
|
||||
published_by,
|
||||
published_at,
|
||||
created_at,
|
||||
updated_at
|
||||
)
|
||||
SELECT
|
||||
generate_gid(decode_base64_unpadded(p.tenant_id), 16),
|
||||
p.tenant_id,
|
||||
p.id,
|
||||
1,
|
||||
'',
|
||||
'Initial version',
|
||||
p.owner_id,
|
||||
'DRAFT',
|
||||
NULL,
|
||||
NULL,
|
||||
p.created_at,
|
||||
p.updated_at
|
||||
FROM policies p;
|
||||
|
||||
ALTER TABLE policies RENAME COLUMN name TO title;
|
||||
ALTER TABLE policies DROP COLUMN content;
|
||||
ALTER TABLE policies ADD COLUMN description TEXT NOT NULL DEFAULT '';
|
||||
ALTER TABLE policies ALTER COLUMN owner_id DROP NOT NULL;
|
||||
ALTER TABLE policies DROP COLUMN review_date;
|
||||
ALTER TABLE policies DROP COLUMN status;
|
||||
ALTER TABLE policies ADD COLUMN current_published_version INTEGER;
|
||||
|
||||
UPDATE policies p
|
||||
SET current_published_version = 1
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM policy_versions pv
|
||||
WHERE pv.policy_id = p.id
|
||||
AND pv.status = 'published'
|
||||
);
|
||||
22
pkg/coredata/migrations/20250425T012700Z.sql
Normal file
22
pkg/coredata/migrations/20250425T012700Z.sql
Normal file
@@ -0,0 +1,22 @@
|
||||
CREATE TYPE policy_status_new AS ENUM ('DRAFT', 'PUBLISHED');
|
||||
|
||||
ALTER TABLE policy_versions ADD COLUMN temp_status TEXT;
|
||||
|
||||
UPDATE policy_versions SET temp_status =
|
||||
CASE WHEN status::TEXT = 'ACTIVE' THEN 'PUBLISHED'
|
||||
ELSE status::TEXT
|
||||
END;
|
||||
|
||||
ALTER TABLE policy_versions DROP COLUMN status;
|
||||
|
||||
ALTER TABLE policy_versions ADD COLUMN status policy_status_new;
|
||||
|
||||
UPDATE policy_versions SET status = temp_status::policy_status_new;
|
||||
|
||||
ALTER TABLE policy_versions DROP COLUMN temp_status;
|
||||
|
||||
DROP TYPE policy_status;
|
||||
ALTER TYPE policy_status_new RENAME TO policy_status;
|
||||
|
||||
CREATE UNIQUE INDEX policy_one_draft_version_idx ON policy_versions (policy_id, status)
|
||||
WHERE status = 'DRAFT';
|
||||
15
pkg/coredata/migrations/20250428T024800Z.sql
Normal file
15
pkg/coredata/migrations/20250428T024800Z.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
CREATE TYPE policy_version_signature_state AS ENUM ('REQUESTED', 'SIGNED');
|
||||
|
||||
CREATE TABLE policy_version_signatures (
|
||||
id TEXT PRIMARY KEY,
|
||||
tenant_id TEXT NOT NULL,
|
||||
policy_version_id TEXT NOT NULL REFERENCES policy_versions(id) ON DELETE CASCADE,
|
||||
state policy_version_signature_state NOT NULL,
|
||||
signed_by TEXT NOT NULL REFERENCES peoples(id),
|
||||
signed_at TIMESTAMP WITH TIME ZONE,
|
||||
requested_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
requested_by TEXT NOT NULL REFERENCES peoples(id),
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
UNIQUE (policy_version_id, signed_by)
|
||||
);
|
||||
@@ -102,7 +102,6 @@ func (p *People) LoadByUserID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
organizationID gid.GID,
|
||||
userID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
@@ -120,14 +119,13 @@ FROM
|
||||
peoples
|
||||
WHERE
|
||||
%s
|
||||
AND organization_id = @organization_id
|
||||
AND user_id = @user_id
|
||||
LIMIT 1;
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"organization_id": organizationID, "user_id": userID}
|
||||
args := pgx.StrictNamedArgs{"user_id": userID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// 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 (
|
||||
@@ -14,15 +28,13 @@ import (
|
||||
|
||||
type (
|
||||
Policy struct {
|
||||
ID gid.GID `db:"id"`
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
OwnerID gid.GID `db:"owner_id"`
|
||||
Status PolicyStatus `db:"status"`
|
||||
Name string `db:"name"`
|
||||
Content string `db:"content"`
|
||||
ReviewDate *time.Time `db:"review_date"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
ID gid.GID `db:"id"`
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
OwnerID gid.GID `db:"owner_id"`
|
||||
Title string `db:"title"`
|
||||
CurrentPublishedVersion *int `db:"current_published_version"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
}
|
||||
|
||||
Policies []*Policy
|
||||
@@ -32,8 +44,8 @@ func (p Policy) CursorKey(orderBy PolicyOrderField) page.CursorKey {
|
||||
switch orderBy {
|
||||
case PolicyOrderFieldCreatedAt:
|
||||
return page.NewCursorKey(p.ID, p.CreatedAt)
|
||||
case PolicyOrderFieldName:
|
||||
return page.NewCursorKey(p.ID, p.Name)
|
||||
case PolicyOrderFieldTitle:
|
||||
return page.NewCursorKey(p.ID, p.Title)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
@@ -50,10 +62,8 @@ SELECT
|
||||
id,
|
||||
organization_id,
|
||||
owner_id,
|
||||
name,
|
||||
status,
|
||||
content,
|
||||
review_date,
|
||||
title,
|
||||
current_published_version,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
@@ -93,13 +103,11 @@ func (p *Policies) LoadByOrganizationID(
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
id,
|
||||
id,
|
||||
organization_id,
|
||||
owner_id,
|
||||
name,
|
||||
status,
|
||||
content,
|
||||
review_date,
|
||||
title,
|
||||
current_published_version,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
@@ -140,41 +148,35 @@ func (p Policy) Insert(
|
||||
INSERT INTO
|
||||
policies (
|
||||
tenant_id,
|
||||
id,
|
||||
organization_id,
|
||||
owner_id,
|
||||
name,
|
||||
status,
|
||||
content,
|
||||
review_date,
|
||||
created_at,
|
||||
updated_at
|
||||
id,
|
||||
organization_id,
|
||||
owner_id,
|
||||
title,
|
||||
current_published_version,
|
||||
created_at,
|
||||
updated_at
|
||||
)
|
||||
VALUES (
|
||||
@tenant_id,
|
||||
@policy_id,
|
||||
@organization_id,
|
||||
@owner_id,
|
||||
@name,
|
||||
@status,
|
||||
@content,
|
||||
@review_date,
|
||||
@title,
|
||||
@current_published_version,
|
||||
@created_at,
|
||||
@updated_at
|
||||
);
|
||||
`
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"policy_id": p.ID,
|
||||
"organization_id": p.OrganizationID,
|
||||
"owner_id": p.OwnerID,
|
||||
"name": p.Name,
|
||||
"status": p.Status,
|
||||
"content": p.Content,
|
||||
"review_date": p.ReviewDate,
|
||||
"created_at": p.CreatedAt,
|
||||
"updated_at": p.UpdatedAt,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"policy_id": p.ID,
|
||||
"organization_id": p.OrganizationID,
|
||||
"owner_id": p.OwnerID,
|
||||
"title": p.Title,
|
||||
"current_published_version": p.CurrentPublishedVersion,
|
||||
"created_at": p.CreatedAt,
|
||||
"updated_at": p.UpdatedAt,
|
||||
}
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
return err
|
||||
@@ -207,10 +209,8 @@ func (p *Policy) Update(
|
||||
UPDATE
|
||||
policies
|
||||
SET
|
||||
name = @name,
|
||||
status = @status,
|
||||
content = @content,
|
||||
review_date = @review_date,
|
||||
title = @title,
|
||||
current_published_version = @current_published_version,
|
||||
owner_id = @owner_id,
|
||||
updated_at = @updated_at
|
||||
WHERE %s
|
||||
@@ -219,13 +219,11 @@ WHERE %s
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"policy_id": p.ID,
|
||||
"updated_at": time.Now(),
|
||||
"name": p.Name,
|
||||
"content": p.Content,
|
||||
"status": p.Status,
|
||||
"review_date": p.ReviewDate,
|
||||
"owner_id": p.OwnerID,
|
||||
"policy_id": p.ID,
|
||||
"updated_at": time.Now(),
|
||||
"title": p.Title,
|
||||
"current_published_version": p.CurrentPublishedVersion,
|
||||
"owner_id": p.OwnerID,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
@@ -251,10 +249,8 @@ WITH plcs AS (
|
||||
p.tenant_id,
|
||||
p.organization_id,
|
||||
p.owner_id,
|
||||
p.name,
|
||||
p.content,
|
||||
p.status,
|
||||
p.review_date,
|
||||
p.title,
|
||||
p.current_published_version,
|
||||
p.created_at,
|
||||
p.updated_at
|
||||
FROM
|
||||
@@ -268,10 +264,8 @@ SELECT
|
||||
id,
|
||||
organization_id,
|
||||
owner_id,
|
||||
name,
|
||||
content,
|
||||
status,
|
||||
review_date,
|
||||
title,
|
||||
current_published_version,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
@@ -314,10 +308,8 @@ WITH plcs AS (
|
||||
p.tenant_id,
|
||||
p.organization_id,
|
||||
p.owner_id,
|
||||
p.name,
|
||||
p.content,
|
||||
p.status,
|
||||
p.review_date,
|
||||
p.title,
|
||||
p.current_published_version,
|
||||
p.created_at,
|
||||
p.updated_at
|
||||
FROM
|
||||
@@ -331,10 +323,8 @@ SELECT
|
||||
id,
|
||||
organization_id,
|
||||
owner_id,
|
||||
name,
|
||||
content,
|
||||
status,
|
||||
review_date,
|
||||
title,
|
||||
current_published_version,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
|
||||
@@ -20,7 +20,7 @@ type (
|
||||
|
||||
const (
|
||||
PolicyOrderFieldCreatedAt PolicyOrderField = "CREATED_AT"
|
||||
PolicyOrderFieldName PolicyOrderField = "NAME"
|
||||
PolicyOrderFieldTitle PolicyOrderField = "TITLE"
|
||||
)
|
||||
|
||||
func (p PolicyOrderField) Column() string {
|
||||
|
||||
@@ -25,7 +25,7 @@ type (
|
||||
|
||||
const (
|
||||
PolicyStatusDraft PolicyStatus = iota
|
||||
PolicyStatusActive
|
||||
PolicyStatusPublished
|
||||
)
|
||||
|
||||
func (ps PolicyStatus) MarshalText() ([]byte, error) {
|
||||
@@ -38,8 +38,8 @@ func (ps *PolicyStatus) UnmarshalText(data []byte) error {
|
||||
switch val {
|
||||
case PolicyStatusDraft.String():
|
||||
*ps = PolicyStatusDraft
|
||||
case PolicyStatusActive.String():
|
||||
*ps = PolicyStatusActive
|
||||
case PolicyStatusPublished.String():
|
||||
*ps = PolicyStatusPublished
|
||||
default:
|
||||
return fmt.Errorf("invalid PolicyStatus value: %q", val)
|
||||
}
|
||||
@@ -53,8 +53,8 @@ func (ps PolicyStatus) String() string {
|
||||
switch ps {
|
||||
case PolicyStatusDraft:
|
||||
val = "DRAFT"
|
||||
case PolicyStatusActive:
|
||||
val = "ACTIVE"
|
||||
case PolicyStatusPublished:
|
||||
val = "PUBLISHED"
|
||||
}
|
||||
|
||||
return val
|
||||
|
||||
344
pkg/coredata/policy_version.go
Normal file
344
pkg/coredata/policy_version.go
Normal file
@@ -0,0 +1,344 @@
|
||||
// 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 (
|
||||
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"`
|
||||
}
|
||||
|
||||
PolicyVersions []*PolicyVersion
|
||||
)
|
||||
|
||||
func (p *PolicyVersions) LoadByPolicyID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
policyID gid.GID,
|
||||
cursor *page.Cursor[PolicyVersionOrderField],
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
id,
|
||||
policy_id,
|
||||
version_number,
|
||||
content,
|
||||
changelog,
|
||||
created_by,
|
||||
status,
|
||||
published_by,
|
||||
published_at,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
policy_versions
|
||||
WHERE
|
||||
%s
|
||||
AND policy_id = @policy_id
|
||||
AND %s
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"policy_id": policyID}
|
||||
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)
|
||||
}
|
||||
|
||||
policyVersions, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[PolicyVersion])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect policy versions: %w", err)
|
||||
}
|
||||
|
||||
*p = policyVersions
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p PolicyVersion) CursorKey(orderBy PolicyVersionOrderField) page.CursorKey {
|
||||
switch orderBy {
|
||||
case PolicyVersionOrderFieldCreatedAt:
|
||||
return page.NewCursorKey(p.ID, p.CreatedAt)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func (p *PolicyVersion) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
policyVersionID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
id,
|
||||
policy_id,
|
||||
version_number,
|
||||
content,
|
||||
changelog,
|
||||
created_by,
|
||||
status,
|
||||
published_by,
|
||||
published_at,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
policy_versions
|
||||
WHERE
|
||||
%s
|
||||
AND id = @policy_version_id
|
||||
LIMIT 1;
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"policy_version_id": policyVersionID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query policy versions: %w", err)
|
||||
}
|
||||
|
||||
policyVersion, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[PolicyVersion])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect policy version: %w", err)
|
||||
}
|
||||
|
||||
*p = policyVersion
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p PolicyVersion) Insert(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
) error {
|
||||
q := `
|
||||
INSERT INTO policy_versions (
|
||||
tenant_id,
|
||||
id,
|
||||
policy_id,
|
||||
version_number,
|
||||
content,
|
||||
changelog,
|
||||
created_by,
|
||||
status,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES (
|
||||
@tenant_id,
|
||||
@id,
|
||||
@policy_id,
|
||||
@version_number,
|
||||
@content,
|
||||
@changelog,
|
||||
@created_by,
|
||||
@status,
|
||||
@created_at,
|
||||
@updated_at
|
||||
)
|
||||
`
|
||||
|
||||
now := time.Now()
|
||||
args := pgx.StrictNamedArgs{
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"id": p.ID,
|
||||
"policy_id": p.PolicyID,
|
||||
"version_number": p.VersionNumber,
|
||||
"content": p.Content,
|
||||
"changelog": p.Changelog,
|
||||
"created_by": p.CreatedBy,
|
||||
"status": PolicyStatusDraft,
|
||||
"created_at": now,
|
||||
"updated_at": now,
|
||||
}
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating/updating policy version: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PolicyVersion) LoadByPolicyIDAndVersionNumber(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
policyID gid.GID,
|
||||
versionNumber int,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
id,
|
||||
policy_id,
|
||||
version_number,
|
||||
content,
|
||||
changelog,
|
||||
created_by,
|
||||
status,
|
||||
published_by,
|
||||
published_at,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
policy_versions
|
||||
WHERE
|
||||
%s
|
||||
AND policy_id = @policy_id
|
||||
AND version_number = @version_number
|
||||
LIMIT 1;
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"policy_id": policyID,
|
||||
"version_number": versionNumber,
|
||||
}
|
||||
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query policy versions: %w", err)
|
||||
}
|
||||
|
||||
policyVersion, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[PolicyVersion])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect policy version: %w", err)
|
||||
}
|
||||
|
||||
*p = policyVersion
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PolicyVersion) LoadLatestVersion(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
policyID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
id,
|
||||
policy_id,
|
||||
version_number,
|
||||
content,
|
||||
changelog,
|
||||
created_by,
|
||||
status,
|
||||
published_by,
|
||||
published_at,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
policy_versions
|
||||
WHERE
|
||||
%s
|
||||
AND policy_id = @policy_id
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 1;
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"policy_id": policyID,
|
||||
}
|
||||
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query policy versions: %w", err)
|
||||
}
|
||||
|
||||
policyVersion, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[PolicyVersion])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect policy version: %w", err)
|
||||
}
|
||||
|
||||
*p = policyVersion
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p PolicyVersion) Update(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
) error {
|
||||
q := `
|
||||
UPDATE policy_versions SET
|
||||
changelog = @changelog,
|
||||
status = @status,
|
||||
content = @content,
|
||||
published_by = @published_by,
|
||||
published_at = @published_at,
|
||||
updated_at = @updated_at
|
||||
WHERE %s
|
||||
AND id = @policy_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,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot update policy version: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
41
pkg/coredata/policy_version_order_field.go
Normal file
41
pkg/coredata/policy_version_order_field.go
Normal file
@@ -0,0 +1,41 @@
|
||||
// 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
|
||||
|
||||
type (
|
||||
PolicyVersionOrderField string
|
||||
)
|
||||
|
||||
const (
|
||||
PolicyVersionOrderFieldCreatedAt PolicyVersionOrderField = "CREATED_AT"
|
||||
PolicyVersionOrderFieldVersion PolicyVersionOrderField = "VERSION"
|
||||
)
|
||||
|
||||
func (p PolicyVersionOrderField) Column() string {
|
||||
return string(p)
|
||||
}
|
||||
|
||||
func (p PolicyVersionOrderField) String() string {
|
||||
return string(p)
|
||||
}
|
||||
|
||||
func (p PolicyVersionOrderField) MarshalText() ([]byte, error) {
|
||||
return []byte(p.String()), nil
|
||||
}
|
||||
|
||||
func (p *PolicyVersionOrderField) UnmarshalText(text []byte) error {
|
||||
*p = PolicyVersionOrderField(text)
|
||||
return nil
|
||||
}
|
||||
197
pkg/coredata/policy_version_signature.go
Normal file
197
pkg/coredata/policy_version_signature.go
Normal file
@@ -0,0 +1,197 @@
|
||||
// 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) 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
|
||||
}
|
||||
41
pkg/coredata/policy_version_signature_order_field.go
Normal file
41
pkg/coredata/policy_version_signature_order_field.go
Normal file
@@ -0,0 +1,41 @@
|
||||
// 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
|
||||
|
||||
type (
|
||||
PolicyVersionSignatureOrderField string
|
||||
)
|
||||
|
||||
const (
|
||||
PolicyVersionSignatureOrderFieldCreatedAt PolicyVersionSignatureOrderField = "CREATED_AT"
|
||||
PolicyVersionSignatureOrderFieldSignedAt PolicyVersionSignatureOrderField = "SIGNED_AT"
|
||||
)
|
||||
|
||||
func (p PolicyVersionSignatureOrderField) Column() string {
|
||||
return string(p)
|
||||
}
|
||||
|
||||
func (p PolicyVersionSignatureOrderField) String() string {
|
||||
return string(p)
|
||||
}
|
||||
|
||||
func (p PolicyVersionSignatureOrderField) MarshalText() ([]byte, error) {
|
||||
return []byte(p.String()), nil
|
||||
}
|
||||
|
||||
func (p *PolicyVersionSignatureOrderField) UnmarshalText(text []byte) error {
|
||||
*p = PolicyVersionSignatureOrderField(text)
|
||||
return nil
|
||||
}
|
||||
74
pkg/coredata/policy_version_signature_state.go
Normal file
74
pkg/coredata/policy_version_signature_state.go
Normal file
@@ -0,0 +1,74 @@
|
||||
// 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 (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type (
|
||||
PolicyVersionSignatureState string
|
||||
)
|
||||
|
||||
const (
|
||||
PolicyVersionSignatureStateRequested PolicyVersionSignatureState = "REQUESTED"
|
||||
PolicyVersionSignatureStateSigned PolicyVersionSignatureState = "SIGNED"
|
||||
)
|
||||
|
||||
func (pvs PolicyVersionSignatureState) MarshalText() ([]byte, error) {
|
||||
return []byte(pvs.String()), nil
|
||||
}
|
||||
|
||||
func (pvs *PolicyVersionSignatureState) UnmarshalText(data []byte) error {
|
||||
val := string(data)
|
||||
|
||||
switch val {
|
||||
case PolicyVersionSignatureStateRequested.String():
|
||||
*pvs = PolicyVersionSignatureStateRequested
|
||||
case PolicyVersionSignatureStateSigned.String():
|
||||
*pvs = PolicyVersionSignatureStateSigned
|
||||
default:
|
||||
return fmt.Errorf("invalid MesureState value: %q", val)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pvs PolicyVersionSignatureState) String() string {
|
||||
var val string
|
||||
|
||||
switch pvs {
|
||||
case PolicyVersionSignatureStateRequested:
|
||||
val = "REQUESTED"
|
||||
case PolicyVersionSignatureStateSigned:
|
||||
val = "SIGNED"
|
||||
}
|
||||
|
||||
return val
|
||||
}
|
||||
|
||||
func (pvs *PolicyVersionSignatureState) Scan(value any) error {
|
||||
val, ok := value.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid scan source for MesureState, expected string got %T", value)
|
||||
}
|
||||
|
||||
return pvs.UnmarshalText([]byte(val))
|
||||
}
|
||||
|
||||
func (pvs PolicyVersionSignatureState) Value() (driver.Value, error) {
|
||||
return pvs.String(), nil
|
||||
}
|
||||
@@ -71,7 +71,6 @@ func (s PeopleService) Get(
|
||||
|
||||
func (s PeopleService) GetByUserID(
|
||||
ctx context.Context,
|
||||
organizationID gid.GID,
|
||||
userID gid.GID,
|
||||
) (*coredata.People, error) {
|
||||
people := &coredata.People{}
|
||||
@@ -79,7 +78,7 @@ func (s PeopleService) GetByUserID(
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
return people.LoadByUserID(ctx, conn, s.svc.scope, organizationID, userID)
|
||||
return people.LoadByUserID(ctx, conn, s.svc.scope, userID)
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -18,21 +18,21 @@ type PolicyService struct {
|
||||
type (
|
||||
CreatePolicyRequest struct {
|
||||
OrganizationID gid.GID
|
||||
Name string
|
||||
Status coredata.PolicyStatus
|
||||
Title string
|
||||
Content string
|
||||
ReviewDate *time.Time
|
||||
OwnerID gid.GID
|
||||
CreatedBy gid.GID
|
||||
}
|
||||
|
||||
UpdatePolicyRequest struct {
|
||||
ID gid.GID
|
||||
ExpectedVersion int
|
||||
Name *string
|
||||
Content *string
|
||||
Status *coredata.PolicyStatus
|
||||
ReviewDate *time.Time
|
||||
OwnerID *gid.GID
|
||||
UpdatePolicyVersionRequest struct {
|
||||
ID gid.GID
|
||||
Content string
|
||||
}
|
||||
|
||||
RequestSignatureRequest struct {
|
||||
PolicyVersionID gid.GID
|
||||
RequestedBy gid.GID
|
||||
Signatory gid.GID
|
||||
}
|
||||
)
|
||||
|
||||
@@ -56,38 +56,203 @@ func (s *PolicyService) Get(
|
||||
return policy, nil
|
||||
}
|
||||
|
||||
func (s *PolicyService) PublishVersion(
|
||||
ctx context.Context,
|
||||
policyID gid.GID,
|
||||
publishedBy gid.GID,
|
||||
) (*coredata.Policy, *coredata.PolicyVersion, error) {
|
||||
policy := &coredata.Policy{}
|
||||
policyVersion := &coredata.PolicyVersion{}
|
||||
now := time.Now()
|
||||
|
||||
err := s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(tx pg.Conn) error {
|
||||
if err := policy.LoadByID(ctx, tx, s.svc.scope, policyID); err != nil {
|
||||
return fmt.Errorf("cannot load policy %q: %w", policyID, err)
|
||||
}
|
||||
|
||||
if err := policyVersion.LoadLatestVersion(ctx, tx, s.svc.scope, policyID); err != nil {
|
||||
return fmt.Errorf("cannot load current draft: %w", err)
|
||||
}
|
||||
|
||||
if policyVersion.Status != coredata.PolicyStatusDraft {
|
||||
return fmt.Errorf("cannot publish version")
|
||||
}
|
||||
|
||||
policy.CurrentPublishedVersion = &policyVersion.VersionNumber
|
||||
policy.UpdatedAt = now
|
||||
|
||||
policyVersion.Status = coredata.PolicyStatusPublished
|
||||
policyVersion.PublishedAt = &now
|
||||
policyVersion.PublishedBy = &publishedBy
|
||||
policyVersion.UpdatedAt = now
|
||||
|
||||
if err := policy.Update(ctx, tx, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot update policy: %w", err)
|
||||
}
|
||||
|
||||
if err := policyVersion.Update(ctx, tx, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot update policy version: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return policy, policyVersion, nil
|
||||
}
|
||||
|
||||
func (s *PolicyService) Create(
|
||||
ctx context.Context,
|
||||
req CreatePolicyRequest,
|
||||
) (*coredata.Policy, error) {
|
||||
) (*coredata.Policy, *coredata.PolicyVersion, error) {
|
||||
now := time.Now()
|
||||
policyID, err := gid.NewGID(s.svc.scope.GetTenantID(), coredata.PolicyEntityType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create policy global id: %w", err)
|
||||
return nil, nil, fmt.Errorf("cannot create policy global id: %w", err)
|
||||
}
|
||||
|
||||
policyVersionID, err := gid.NewGID(s.svc.scope.GetTenantID(), coredata.PolicyVersionEntityType)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("cannot create policy version global id: %w", err)
|
||||
}
|
||||
|
||||
organization := &coredata.Organization{}
|
||||
policy := &coredata.Policy{
|
||||
ID: policyID,
|
||||
OrganizationID: req.OrganizationID,
|
||||
OwnerID: req.OwnerID,
|
||||
Name: req.Name,
|
||||
Content: req.Content,
|
||||
Status: req.Status,
|
||||
ReviewDate: req.ReviewDate,
|
||||
Title: req.Title,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
policyVersion := &coredata.PolicyVersion{
|
||||
ID: policyVersionID,
|
||||
PolicyID: policyID,
|
||||
VersionNumber: 1,
|
||||
Content: req.Content,
|
||||
CreatedBy: req.CreatedBy,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
err = s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
if err := policy.Insert(ctx, conn, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot insert policy: %w", err)
|
||||
}
|
||||
|
||||
if err := policyVersion.Update(ctx, conn, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot create policy version: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return policy, policyVersion, nil
|
||||
}
|
||||
|
||||
func (s *PolicyService) UpdateVersion(
|
||||
ctx context.Context,
|
||||
req UpdatePolicyVersionRequest,
|
||||
) (*coredata.PolicyVersion, error) {
|
||||
policyVersion := &coredata.PolicyVersion{}
|
||||
|
||||
err := s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
if err := policyVersion.LoadByID(ctx, conn, s.svc.scope, req.ID); err != nil {
|
||||
return fmt.Errorf("cannot load policy version %q: %w", req.ID, err)
|
||||
}
|
||||
|
||||
if policyVersion.Status != coredata.PolicyStatusDraft {
|
||||
return fmt.Errorf("cannot update published version")
|
||||
}
|
||||
|
||||
policyVersion.Content = req.Content
|
||||
policyVersion.UpdatedAt = time.Now()
|
||||
|
||||
if err := policyVersion.Update(ctx, conn, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot update policy version: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return policyVersion, nil
|
||||
}
|
||||
|
||||
func (s *PolicyService) GetVersionSignature(
|
||||
ctx context.Context,
|
||||
signatureID gid.GID,
|
||||
) (*coredata.PolicyVersionSignature, error) {
|
||||
policyVersionSignature := &coredata.PolicyVersionSignature{}
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
return policyVersionSignature.LoadByID(ctx, conn, s.svc.scope, signatureID)
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return policyVersionSignature, nil
|
||||
}
|
||||
|
||||
func (s *PolicyService) RequestSignature(
|
||||
ctx context.Context,
|
||||
req RequestSignatureRequest,
|
||||
) (*coredata.PolicyVersionSignature, error) {
|
||||
policyVersionSignatureID, err := gid.NewGID(s.svc.scope.GetTenantID(), coredata.PolicyVersionSignatureEntityType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create policy version signature global id: %w", err)
|
||||
}
|
||||
|
||||
policyVersion, err := s.GetVersion(ctx, req.PolicyVersionID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot get policy version: %w", err)
|
||||
}
|
||||
|
||||
if policyVersion.Status != coredata.PolicyStatusPublished {
|
||||
return nil, fmt.Errorf("cannot request signature for unpublished version")
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
policyVersionSignature := &coredata.PolicyVersionSignature{
|
||||
ID: policyVersionSignatureID,
|
||||
PolicyVersionID: req.PolicyVersionID,
|
||||
State: coredata.PolicyVersionSignatureStateRequested,
|
||||
RequestedBy: req.RequestedBy,
|
||||
RequestedAt: now,
|
||||
SignedBy: req.Signatory,
|
||||
SignedAt: nil,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err = s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
if err := organization.LoadByID(ctx, conn, s.svc.scope, req.OrganizationID); err != nil {
|
||||
return fmt.Errorf("cannot load organization %q: %w", req.OrganizationID, err)
|
||||
}
|
||||
|
||||
if err := policy.Insert(ctx, conn, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot insert policy: %w", err)
|
||||
if err := policyVersionSignature.Insert(ctx, conn, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot insert policy version signature: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -98,54 +263,77 @@ func (s *PolicyService) Create(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return policy, nil
|
||||
return policyVersionSignature, nil
|
||||
}
|
||||
|
||||
func (s *PolicyService) Update(
|
||||
func (s *PolicyService) ListSignatures(
|
||||
ctx context.Context,
|
||||
req UpdatePolicyRequest,
|
||||
) (*coredata.Policy, error) {
|
||||
policy := &coredata.Policy{}
|
||||
policyVersionID gid.GID,
|
||||
cursor *page.Cursor[coredata.PolicyVersionSignatureOrderField],
|
||||
) (*page.Page[*coredata.PolicyVersionSignature, coredata.PolicyVersionSignatureOrderField], error) {
|
||||
var policyVersionSignatures coredata.PolicyVersionSignatures
|
||||
|
||||
err := s.svc.pg.WithTx(
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
if err := policy.LoadByID(ctx, conn, s.svc.scope, req.ID); err != nil {
|
||||
return fmt.Errorf("cannot load policy %q: %w", req.ID, err)
|
||||
return policyVersionSignatures.LoadByPolicyVersionID(ctx, conn, s.svc.scope, policyVersionID, cursor)
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return page.NewPage(policyVersionSignatures, cursor), nil
|
||||
}
|
||||
|
||||
func (s *PolicyService) CreateDraft(
|
||||
ctx context.Context,
|
||||
policyID gid.GID,
|
||||
createdBy gid.GID,
|
||||
) (*coredata.PolicyVersion, error) {
|
||||
draftVersionID, err := gid.NewGID(s.svc.scope.GetTenantID(), coredata.PolicyVersionEntityType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create policy version global id: %w", err)
|
||||
}
|
||||
|
||||
latestVersion := &coredata.PolicyVersion{}
|
||||
draftVersion := &coredata.PolicyVersion{}
|
||||
now := time.Now()
|
||||
|
||||
err = s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
if err := latestVersion.LoadLatestVersion(ctx, conn, s.svc.scope, policyID); err != nil {
|
||||
return fmt.Errorf("cannot load latest version: %w", err)
|
||||
}
|
||||
|
||||
if req.Name != nil {
|
||||
policy.Name = *req.Name
|
||||
if latestVersion.Status != coredata.PolicyStatusPublished {
|
||||
return fmt.Errorf("cannot create draft from unpublished version")
|
||||
}
|
||||
|
||||
if req.Content != nil {
|
||||
policy.Content = *req.Content
|
||||
}
|
||||
draftVersion.ID = draftVersionID
|
||||
draftVersion.PolicyID = policyID
|
||||
draftVersion.VersionNumber = latestVersion.VersionNumber + 1
|
||||
draftVersion.Content = latestVersion.Content
|
||||
draftVersion.Status = coredata.PolicyStatusDraft
|
||||
draftVersion.CreatedBy = createdBy
|
||||
draftVersion.CreatedAt = now
|
||||
draftVersion.UpdatedAt = now
|
||||
|
||||
if req.Status != nil {
|
||||
policy.Status = *req.Status
|
||||
}
|
||||
|
||||
if req.ReviewDate != nil {
|
||||
policy.ReviewDate = req.ReviewDate
|
||||
}
|
||||
|
||||
if req.OwnerID != nil {
|
||||
policy.OwnerID = *req.OwnerID
|
||||
}
|
||||
|
||||
if err := policy.Update(ctx, conn, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot update policy: %w", err)
|
||||
if err := draftVersion.Insert(ctx, conn, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot create draft: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return policy, nil
|
||||
return draftVersion, nil
|
||||
}
|
||||
|
||||
func (s *PolicyService) Delete(
|
||||
@@ -162,6 +350,47 @@ func (s *PolicyService) Delete(
|
||||
)
|
||||
}
|
||||
|
||||
func (s *PolicyService) ListVersions(
|
||||
ctx context.Context,
|
||||
policyID gid.GID,
|
||||
cursor *page.Cursor[coredata.PolicyVersionOrderField],
|
||||
) (*page.Page[*coredata.PolicyVersion, coredata.PolicyVersionOrderField], error) {
|
||||
var policyVersions coredata.PolicyVersions
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
return policyVersions.LoadByPolicyID(ctx, conn, s.svc.scope, policyID, cursor)
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return page.NewPage(policyVersions, cursor), nil
|
||||
}
|
||||
|
||||
func (s *PolicyService) GetVersion(
|
||||
ctx context.Context,
|
||||
policyVersionID gid.GID,
|
||||
) (*coredata.PolicyVersion, error) {
|
||||
policyVersion := &coredata.PolicyVersion{}
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
return policyVersion.LoadByID(ctx, conn, s.svc.scope, policyVersionID)
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return policyVersion, nil
|
||||
}
|
||||
|
||||
func (s *PolicyService) ListByOrganizationID(
|
||||
ctx context.Context,
|
||||
organizationID gid.GID,
|
||||
|
||||
@@ -95,8 +95,8 @@ enum PolicyStatus
|
||||
@goModel(model: "github.com/getprobo/probo/pkg/coredata.PolicyStatus") {
|
||||
DRAFT
|
||||
@goEnum(value: "github.com/getprobo/probo/pkg/coredata.PolicyStatusDraft")
|
||||
ACTIVE
|
||||
@goEnum(value: "github.com/getprobo/probo/pkg/coredata.PolicyStatusActive")
|
||||
PUBLISHED
|
||||
@goEnum(value: "github.com/getprobo/probo/pkg/coredata.PolicyStatusPublished")
|
||||
}
|
||||
|
||||
enum EvidenceType
|
||||
@@ -184,7 +184,14 @@ enum TaskOrderField
|
||||
|
||||
enum PolicyOrderField
|
||||
@goModel(model: "github.com/getprobo/probo/pkg/coredata.PolicyOrderField") {
|
||||
NAME
|
||||
TITLE
|
||||
@goEnum(
|
||||
value: "github.com/getprobo/probo/pkg/coredata.PolicyOrderFieldTitle"
|
||||
)
|
||||
CREATED_AT
|
||||
@goEnum(
|
||||
value: "github.com/getprobo/probo/pkg/coredata.PolicyOrderFieldCreatedAt"
|
||||
)
|
||||
}
|
||||
|
||||
enum RiskOrderField
|
||||
@@ -260,6 +267,18 @@ enum BusinessImpact
|
||||
@goEnum(value: "github.com/getprobo/probo/pkg/coredata.BusinessImpactCritical")
|
||||
}
|
||||
|
||||
enum PolicyVersionOrderField
|
||||
@goModel(model: "github.com/getprobo/probo/pkg/coredata.PolicyVersionOrderField") {
|
||||
VERSION
|
||||
@goEnum(
|
||||
value: "github.com/getprobo/probo/pkg/coredata.PolicyVersionOrderFieldVersion"
|
||||
)
|
||||
CREATED_AT
|
||||
@goEnum(
|
||||
value: "github.com/getprobo/probo/pkg/coredata.PolicyVersionOrderFieldCreatedAt"
|
||||
)
|
||||
}
|
||||
|
||||
# Order Input Types
|
||||
input UserOrder
|
||||
@goModel(
|
||||
@@ -359,6 +378,18 @@ input ConnectorOrder {
|
||||
direction: OrderDirection!
|
||||
}
|
||||
|
||||
input PolicyVersionOrder
|
||||
@goModel(
|
||||
model: "github.com/getprobo/probo/pkg/server/api/console/v1/types.PolicyVersionOrderBy"
|
||||
) {
|
||||
direction: OrderDirection!
|
||||
field: PolicyVersionOrderField!
|
||||
}
|
||||
|
||||
input PolicyVersionFilter {
|
||||
status: PolicyStatus
|
||||
}
|
||||
|
||||
# Core Types
|
||||
type Organization implements Node {
|
||||
id: ID!
|
||||
@@ -631,12 +662,20 @@ type Evidence implements Node {
|
||||
|
||||
type Policy implements Node {
|
||||
id: ID!
|
||||
name: String!
|
||||
status: PolicyStatus!
|
||||
content: String!
|
||||
reviewDate: Datetime
|
||||
title: String!
|
||||
description: String!
|
||||
currentPublishedVersion: Int
|
||||
owner: People! @goField(forceResolver: true)
|
||||
|
||||
versions(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: PolicyVersionOrder
|
||||
filter: PolicyVersionFilter
|
||||
): PolicyVersionConnection! @goField(forceResolver: true)
|
||||
|
||||
controls(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
@@ -852,6 +891,16 @@ type VendorRiskAssessmentEdge {
|
||||
node: VendorRiskAssessment!
|
||||
}
|
||||
|
||||
type PolicyVersionConnection {
|
||||
edges: [PolicyVersionEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type PolicyVersionEdge {
|
||||
cursor: CursorKey!
|
||||
node: PolicyVersion!
|
||||
}
|
||||
|
||||
# Root Types
|
||||
type Query {
|
||||
node(id: ID!): Node!
|
||||
@@ -952,8 +1001,11 @@ type Mutation {
|
||||
|
||||
# Policy mutations
|
||||
createPolicy(input: CreatePolicyInput!): CreatePolicyPayload!
|
||||
updatePolicy(input: UpdatePolicyInput!): UpdatePolicyPayload!
|
||||
deletePolicy(input: DeletePolicyInput!): DeletePolicyPayload!
|
||||
publishPolicyVersion(input: PublishPolicyVersionInput!): PublishPolicyVersionPayload!
|
||||
createDraftPolicyVersion(input: CreateDraftPolicyVersionInput!): CreateDraftPolicyVersionPayload!
|
||||
updatePolicyVersion(input: UpdatePolicyVersionInput!): UpdatePolicyVersionPayload!
|
||||
requestSignature(input: RequestSignatureInput!): RequestSignaturePayload!
|
||||
|
||||
createVendorRiskAssessment(input: CreateVendorRiskAssessmentInput!): CreateVendorRiskAssessmentPayload!
|
||||
}
|
||||
@@ -1224,20 +1276,17 @@ input DeleteVendorComplianceReportInput {
|
||||
|
||||
input CreatePolicyInput {
|
||||
organizationId: ID!
|
||||
name: String!
|
||||
title: String!
|
||||
content: String!
|
||||
status: PolicyStatus!
|
||||
reviewDate: Datetime
|
||||
ownerId: ID!
|
||||
}
|
||||
|
||||
input UpdatePolicyInput {
|
||||
id: ID!
|
||||
name: String
|
||||
title: String
|
||||
content: String
|
||||
status: PolicyStatus
|
||||
reviewDate: Datetime
|
||||
ownerId: ID
|
||||
createdBy: ID
|
||||
}
|
||||
|
||||
input DeletePolicyInput {
|
||||
@@ -1414,6 +1463,7 @@ type DeleteVendorComplianceReportPayload {
|
||||
|
||||
type CreatePolicyPayload {
|
||||
policyEdge: PolicyEdge!
|
||||
policyVersionEdge: PolicyVersionEdge!
|
||||
}
|
||||
|
||||
type UpdatePolicyPayload {
|
||||
@@ -1489,4 +1539,112 @@ input DeleteMesureInput {
|
||||
|
||||
type DeleteMesurePayload {
|
||||
deletedMesureId: ID!
|
||||
}
|
||||
}
|
||||
|
||||
type PolicyVersion implements Node {
|
||||
id: ID!
|
||||
policy: Policy! @goField(forceResolver: true)
|
||||
status: PolicyStatus!
|
||||
version: Int!
|
||||
content: String!
|
||||
changelog: String!
|
||||
|
||||
signatures(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: PolicyVersionSignatureOrder
|
||||
): PolicyVersionSignatureConnection! @goField(forceResolver: true)
|
||||
|
||||
publishedBy: People @goField(forceResolver: true)
|
||||
publishedAt: Datetime
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
type PolicyVersionSignatureConnection {
|
||||
edges: [PolicyVersionSignatureEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type PolicyVersionSignatureEdge {
|
||||
cursor: CursorKey!
|
||||
node: PolicyVersionSignature!
|
||||
}
|
||||
|
||||
input PolicyVersionSignatureOrder {
|
||||
field: PolicyVersionSignatureOrderField!
|
||||
direction: OrderDirection!
|
||||
}
|
||||
|
||||
enum PolicyVersionSignatureState
|
||||
@goModel(model: "github.com/getprobo/probo/pkg/coredata.PolicyVersionSignatureState") {
|
||||
REQUESTED
|
||||
@goEnum(
|
||||
value: "github.com/getprobo/probo/pkg/coredata.PolicyVersionSignatureStateRequested"
|
||||
)
|
||||
SIGNED
|
||||
@goEnum(
|
||||
value: "github.com/getprobo/probo/pkg/coredata.PolicyVersionSignatureStateSigned"
|
||||
)
|
||||
}
|
||||
|
||||
enum PolicyVersionSignatureOrderField
|
||||
@goModel(model: "github.com/getprobo/probo/pkg/coredata.PolicyVersionSignatureOrderField") {
|
||||
CREATED_AT
|
||||
@goEnum(
|
||||
value: "github.com/getprobo/probo/pkg/coredata.PolicyVersionSignatureOrderFieldCreatedAt"
|
||||
)
|
||||
SIGNED_AT
|
||||
@goEnum(
|
||||
value: "github.com/getprobo/probo/pkg/coredata.PolicyVersionSignatureOrderFieldSignedAt"
|
||||
)
|
||||
}
|
||||
|
||||
type PolicyVersionSignature implements Node {
|
||||
id: ID!
|
||||
policyVersion: PolicyVersion! @goField(forceResolver: true)
|
||||
state: PolicyVersionSignatureState!
|
||||
signedBy: People! @goField(forceResolver: true)
|
||||
signedAt: Datetime
|
||||
requestedAt: Datetime!
|
||||
requestedBy: People! @goField(forceResolver: true)
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
input RequestSignatureInput {
|
||||
policyVersionId: ID!
|
||||
signatoryId: ID!
|
||||
}
|
||||
|
||||
type RequestSignaturePayload {
|
||||
policyVersionSignatureEdge: PolicyVersionSignatureEdge!
|
||||
}
|
||||
|
||||
input PublishPolicyVersionInput {
|
||||
policyId: ID!
|
||||
}
|
||||
|
||||
type PublishPolicyVersionPayload {
|
||||
policyVersion: PolicyVersion!
|
||||
policy: Policy!
|
||||
}
|
||||
|
||||
type CreateDraftPolicyVersionPayload {
|
||||
policyVersionEdge: PolicyVersionEdge!
|
||||
}
|
||||
|
||||
input CreateDraftPolicyVersionInput {
|
||||
policyID: ID!
|
||||
}
|
||||
|
||||
input UpdatePolicyVersionInput {
|
||||
policyVersionId: ID!
|
||||
content: String!
|
||||
}
|
||||
|
||||
type UpdatePolicyVersionPayload {
|
||||
policyVersion: PolicyVersion!
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -44,12 +44,10 @@ func NewPolicyEdge(policy *coredata.Policy, orderBy coredata.PolicyOrderField) *
|
||||
|
||||
func NewPolicy(policy *coredata.Policy) *Policy {
|
||||
return &Policy{
|
||||
ID: policy.ID,
|
||||
Name: policy.Name,
|
||||
Content: policy.Content,
|
||||
CreatedAt: policy.CreatedAt,
|
||||
UpdatedAt: policy.UpdatedAt,
|
||||
Status: policy.Status,
|
||||
ReviewDate: policy.ReviewDate,
|
||||
ID: policy.ID,
|
||||
Title: policy.Title,
|
||||
CurrentPublishedVersion: policy.CurrentPublishedVersion,
|
||||
CreatedAt: policy.CreatedAt,
|
||||
UpdatedAt: policy.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
56
pkg/server/api/console/v1/types/policy_version.go
Normal file
56
pkg/server/api/console/v1/types/policy_version.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// 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 types
|
||||
|
||||
import (
|
||||
"github.com/getprobo/probo/pkg/coredata"
|
||||
"github.com/getprobo/probo/pkg/page"
|
||||
)
|
||||
|
||||
type (
|
||||
PolicyVersionOrderBy OrderBy[coredata.PolicyVersionOrderField]
|
||||
)
|
||||
|
||||
func NewPolicyVersionConnection(page *page.Page[*coredata.PolicyVersion, coredata.PolicyVersionOrderField]) *PolicyVersionConnection {
|
||||
edges := make([]*PolicyVersionEdge, len(page.Data))
|
||||
for i, policyVersion := range page.Data {
|
||||
edges[i] = NewPolicyVersionEdge(policyVersion, page.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &PolicyVersionConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(page),
|
||||
}
|
||||
}
|
||||
|
||||
func NewPolicyVersionEdge(policyVersion *coredata.PolicyVersion, orderBy coredata.PolicyVersionOrderField) *PolicyVersionEdge {
|
||||
return &PolicyVersionEdge{
|
||||
Cursor: policyVersion.CursorKey(orderBy),
|
||||
Node: NewPolicyVersion(policyVersion),
|
||||
}
|
||||
}
|
||||
|
||||
func NewPolicyVersion(policyVersion *coredata.PolicyVersion) *PolicyVersion {
|
||||
return &PolicyVersion{
|
||||
ID: policyVersion.ID,
|
||||
Version: policyVersion.VersionNumber,
|
||||
Content: policyVersion.Content,
|
||||
Status: policyVersion.Status,
|
||||
PublishedAt: policyVersion.PublishedAt,
|
||||
Changelog: policyVersion.Changelog,
|
||||
CreatedAt: policyVersion.CreatedAt,
|
||||
UpdatedAt: policyVersion.UpdatedAt,
|
||||
}
|
||||
}
|
||||
54
pkg/server/api/console/v1/types/policy_version_signature.go
Normal file
54
pkg/server/api/console/v1/types/policy_version_signature.go
Normal file
@@ -0,0 +1,54 @@
|
||||
// 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 types
|
||||
|
||||
import (
|
||||
"github.com/getprobo/probo/pkg/coredata"
|
||||
"github.com/getprobo/probo/pkg/page"
|
||||
)
|
||||
|
||||
type (
|
||||
PolicyVersionSignatureOrderBy OrderBy[coredata.PolicyVersionSignatureOrderField]
|
||||
)
|
||||
|
||||
func NewPolicyVersionSignatureConnection(page *page.Page[*coredata.PolicyVersionSignature, coredata.PolicyVersionSignatureOrderField]) *PolicyVersionSignatureConnection {
|
||||
edges := make([]*PolicyVersionSignatureEdge, len(page.Data))
|
||||
for i, policyVersionSignature := range page.Data {
|
||||
edges[i] = NewPolicyVersionSignatureEdge(policyVersionSignature, page.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &PolicyVersionSignatureConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(page),
|
||||
}
|
||||
}
|
||||
|
||||
func NewPolicyVersionSignatureEdge(policyVersionSignature *coredata.PolicyVersionSignature, orderBy coredata.PolicyVersionSignatureOrderField) *PolicyVersionSignatureEdge {
|
||||
return &PolicyVersionSignatureEdge{
|
||||
Cursor: policyVersionSignature.CursorKey(orderBy),
|
||||
Node: NewPolicyVersionSignature(policyVersionSignature),
|
||||
}
|
||||
}
|
||||
|
||||
func NewPolicyVersionSignature(policyVersionSignature *coredata.PolicyVersionSignature) *PolicyVersionSignature {
|
||||
return &PolicyVersionSignature{
|
||||
ID: policyVersionSignature.ID,
|
||||
State: policyVersionSignature.State,
|
||||
SignedAt: policyVersionSignature.SignedAt,
|
||||
RequestedAt: policyVersionSignature.RequestedAt,
|
||||
CreatedAt: policyVersionSignature.CreatedAt,
|
||||
UpdatedAt: policyVersionSignature.UpdatedAt,
|
||||
}
|
||||
}
|
||||
@@ -104,6 +104,14 @@ type CreateControlPolicyMappingPayload struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
type CreateDraftPolicyVersionInput struct {
|
||||
PolicyID gid.GID `json:"policyID"`
|
||||
}
|
||||
|
||||
type CreateDraftPolicyVersionPayload struct {
|
||||
PolicyVersionEdge *PolicyVersionEdge `json:"policyVersionEdge"`
|
||||
}
|
||||
|
||||
type CreateEvidenceInput struct {
|
||||
TaskID gid.GID `json:"taskId"`
|
||||
Name string `json:"name"`
|
||||
@@ -159,16 +167,15 @@ type CreatePeoplePayload struct {
|
||||
}
|
||||
|
||||
type CreatePolicyInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
Name string `json:"name"`
|
||||
Content string `json:"content"`
|
||||
Status coredata.PolicyStatus `json:"status"`
|
||||
ReviewDate *time.Time `json:"reviewDate,omitempty"`
|
||||
OwnerID gid.GID `json:"ownerId"`
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
OwnerID gid.GID `json:"ownerId"`
|
||||
}
|
||||
|
||||
type CreatePolicyPayload struct {
|
||||
PolicyEdge *PolicyEdge `json:"policyEdge"`
|
||||
PolicyEdge *PolicyEdge `json:"policyEdge"`
|
||||
PolicyVersionEdge *PolicyVersionEdge `json:"policyVersionEdge"`
|
||||
}
|
||||
|
||||
type CreateRiskInput struct {
|
||||
@@ -556,15 +563,15 @@ type PeopleEdge struct {
|
||||
}
|
||||
|
||||
type Policy struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Status coredata.PolicyStatus `json:"status"`
|
||||
Content string `json:"content"`
|
||||
ReviewDate *time.Time `json:"reviewDate,omitempty"`
|
||||
Owner *People `json:"owner"`
|
||||
Controls *ControlConnection `json:"controls"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
ID gid.GID `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
CurrentPublishedVersion *int `json:"currentPublishedVersion,omitempty"`
|
||||
Owner *People `json:"owner"`
|
||||
Versions *PolicyVersionConnection `json:"versions"`
|
||||
Controls *ControlConnection `json:"controls"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func (Policy) IsNode() {}
|
||||
@@ -580,6 +587,76 @@ type PolicyEdge struct {
|
||||
Node *Policy `json:"node"`
|
||||
}
|
||||
|
||||
type PolicyVersion struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Policy *Policy `json:"policy"`
|
||||
Status coredata.PolicyStatus `json:"status"`
|
||||
Version int `json:"version"`
|
||||
Content string `json:"content"`
|
||||
Changelog string `json:"changelog"`
|
||||
Signatures *PolicyVersionSignatureConnection `json:"signatures"`
|
||||
PublishedBy *People `json:"publishedBy,omitempty"`
|
||||
PublishedAt *time.Time `json:"publishedAt,omitempty"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func (PolicyVersion) IsNode() {}
|
||||
func (this PolicyVersion) GetID() gid.GID { return this.ID }
|
||||
|
||||
type PolicyVersionConnection struct {
|
||||
Edges []*PolicyVersionEdge `json:"edges"`
|
||||
PageInfo *PageInfo `json:"pageInfo"`
|
||||
}
|
||||
|
||||
type PolicyVersionEdge struct {
|
||||
Cursor page.CursorKey `json:"cursor"`
|
||||
Node *PolicyVersion `json:"node"`
|
||||
}
|
||||
|
||||
type PolicyVersionFilter struct {
|
||||
Status *coredata.PolicyStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
type PolicyVersionSignature struct {
|
||||
ID gid.GID `json:"id"`
|
||||
PolicyVersion *PolicyVersion `json:"policyVersion"`
|
||||
State coredata.PolicyVersionSignatureState `json:"state"`
|
||||
SignedBy *People `json:"signedBy"`
|
||||
SignedAt *time.Time `json:"signedAt,omitempty"`
|
||||
RequestedAt time.Time `json:"requestedAt"`
|
||||
RequestedBy *People `json:"requestedBy"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func (PolicyVersionSignature) IsNode() {}
|
||||
func (this PolicyVersionSignature) GetID() gid.GID { return this.ID }
|
||||
|
||||
type PolicyVersionSignatureConnection struct {
|
||||
Edges []*PolicyVersionSignatureEdge `json:"edges"`
|
||||
PageInfo *PageInfo `json:"pageInfo"`
|
||||
}
|
||||
|
||||
type PolicyVersionSignatureEdge struct {
|
||||
Cursor page.CursorKey `json:"cursor"`
|
||||
Node *PolicyVersionSignature `json:"node"`
|
||||
}
|
||||
|
||||
type PolicyVersionSignatureOrder struct {
|
||||
Field coredata.PolicyVersionSignatureOrderField `json:"field"`
|
||||
Direction page.OrderDirection `json:"direction"`
|
||||
}
|
||||
|
||||
type PublishPolicyVersionInput struct {
|
||||
PolicyID gid.GID `json:"policyId"`
|
||||
}
|
||||
|
||||
type PublishPolicyVersionPayload struct {
|
||||
PolicyVersion *PolicyVersion `json:"policyVersion"`
|
||||
Policy *Policy `json:"policy"`
|
||||
}
|
||||
|
||||
type Query struct {
|
||||
}
|
||||
|
||||
@@ -603,6 +680,15 @@ type RequestEvidencePayload struct {
|
||||
EvidenceEdge *EvidenceEdge `json:"evidenceEdge"`
|
||||
}
|
||||
|
||||
type RequestSignatureInput struct {
|
||||
PolicyVersionID gid.GID `json:"policyVersionId"`
|
||||
SignatoryID gid.GID `json:"signatoryId"`
|
||||
}
|
||||
|
||||
type RequestSignaturePayload struct {
|
||||
PolicyVersionSignatureEdge *PolicyVersionSignatureEdge `json:"policyVersionSignatureEdge"`
|
||||
}
|
||||
|
||||
type Risk struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
@@ -720,18 +806,26 @@ type UpdatePeoplePayload struct {
|
||||
}
|
||||
|
||||
type UpdatePolicyInput struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Content *string `json:"content,omitempty"`
|
||||
Status *coredata.PolicyStatus `json:"status,omitempty"`
|
||||
ReviewDate *time.Time `json:"reviewDate,omitempty"`
|
||||
OwnerID *gid.GID `json:"ownerId,omitempty"`
|
||||
ID gid.GID `json:"id"`
|
||||
Title *string `json:"title,omitempty"`
|
||||
Content *string `json:"content,omitempty"`
|
||||
OwnerID *gid.GID `json:"ownerId,omitempty"`
|
||||
CreatedBy *gid.GID `json:"createdBy,omitempty"`
|
||||
}
|
||||
|
||||
type UpdatePolicyPayload struct {
|
||||
Policy *Policy `json:"policy"`
|
||||
}
|
||||
|
||||
type UpdatePolicyVersionInput struct {
|
||||
PolicyVersionID gid.GID `json:"policyVersionId"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type UpdatePolicyVersionPayload struct {
|
||||
PolicyVersion *PolicyVersion `json:"policyVersion"`
|
||||
}
|
||||
|
||||
type UpdateRiskInput struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
|
||||
@@ -1008,41 +1008,29 @@ func (r *mutationResolver) DeleteVendorComplianceReport(ctx context.Context, inp
|
||||
func (r *mutationResolver) CreatePolicy(ctx context.Context, input types.CreatePolicyInput) (*types.CreatePolicyPayload, error) {
|
||||
svc := GetTenantService(ctx, r.proboSvc, input.OrganizationID.TenantID())
|
||||
|
||||
policy, err := svc.Policies.Create(ctx, probo.CreatePolicyRequest{
|
||||
OrganizationID: input.OrganizationID,
|
||||
Name: input.Name,
|
||||
Content: input.Content,
|
||||
Status: input.Status,
|
||||
ReviewDate: input.ReviewDate,
|
||||
OwnerID: input.OwnerID,
|
||||
})
|
||||
user := UserFromContext(ctx)
|
||||
people, err := svc.Peoples.GetByUserID(ctx, user.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get people: %w", err))
|
||||
}
|
||||
|
||||
policy, policyVersion, err := svc.Policies.Create(
|
||||
ctx,
|
||||
probo.CreatePolicyRequest{
|
||||
OrganizationID: input.OrganizationID,
|
||||
Title: input.Title,
|
||||
OwnerID: input.OwnerID,
|
||||
Content: input.Content,
|
||||
CreatedBy: people.ID,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot create policy: %w", err))
|
||||
}
|
||||
|
||||
return &types.CreatePolicyPayload{
|
||||
PolicyEdge: types.NewPolicyEdge(policy, coredata.PolicyOrderFieldCreatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdatePolicy is the resolver for the updatePolicy field.
|
||||
func (r *mutationResolver) UpdatePolicy(ctx context.Context, input types.UpdatePolicyInput) (*types.UpdatePolicyPayload, error) {
|
||||
svc := GetTenantService(ctx, r.proboSvc, input.ID.TenantID())
|
||||
|
||||
policy, err := svc.Policies.Update(ctx, probo.UpdatePolicyRequest{
|
||||
ID: input.ID,
|
||||
Name: input.Name,
|
||||
Content: input.Content,
|
||||
Status: input.Status,
|
||||
ReviewDate: input.ReviewDate,
|
||||
OwnerID: input.OwnerID,
|
||||
})
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot update policy: %w", err))
|
||||
}
|
||||
|
||||
return &types.UpdatePolicyPayload{
|
||||
Policy: types.NewPolicy(policy),
|
||||
PolicyEdge: types.NewPolicyEdge(policy, coredata.PolicyOrderFieldTitle),
|
||||
PolicyVersionEdge: types.NewPolicyVersionEdge(policyVersion, coredata.PolicyVersionOrderFieldCreatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1060,16 +1048,96 @@ func (r *mutationResolver) DeletePolicy(ctx context.Context, input types.DeleteP
|
||||
}, nil
|
||||
}
|
||||
|
||||
// PublishPolicyVersion is the resolver for the publishPolicyVersion field.
|
||||
func (r *mutationResolver) PublishPolicyVersion(ctx context.Context, input types.PublishPolicyVersionInput) (*types.PublishPolicyVersionPayload, error) {
|
||||
svc := GetTenantService(ctx, r.proboSvc, input.PolicyID.TenantID())
|
||||
user := UserFromContext(ctx)
|
||||
|
||||
people, err := svc.Peoples.GetByUserID(ctx, user.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get people: %w", err))
|
||||
}
|
||||
|
||||
policy, policyVersion, err := svc.Policies.PublishVersion(ctx, input.PolicyID, people.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot publish policy version: %w", err))
|
||||
}
|
||||
|
||||
return &types.PublishPolicyVersionPayload{
|
||||
PolicyVersion: types.NewPolicyVersion(policyVersion),
|
||||
Policy: types.NewPolicy(policy),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateDraftPolicyVersion is the resolver for the createDraftPolicyVersion field.
|
||||
func (r *mutationResolver) CreateDraftPolicyVersion(ctx context.Context, input types.CreateDraftPolicyVersionInput) (*types.CreateDraftPolicyVersionPayload, error) {
|
||||
svc := GetTenantService(ctx, r.proboSvc, input.PolicyID.TenantID())
|
||||
|
||||
user := UserFromContext(ctx)
|
||||
people, err := svc.Peoples.GetByUserID(ctx, user.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get people: %w", err))
|
||||
}
|
||||
|
||||
policyVersion, err := svc.Policies.CreateDraft(ctx, input.PolicyID, people.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot create draft policy version: %w", err))
|
||||
}
|
||||
|
||||
return &types.CreateDraftPolicyVersionPayload{
|
||||
PolicyVersionEdge: types.NewPolicyVersionEdge(policyVersion, coredata.PolicyVersionOrderFieldCreatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdatePolicyVersion is the resolver for the updatePolicyVersion field.
|
||||
func (r *mutationResolver) UpdatePolicyVersion(ctx context.Context, input types.UpdatePolicyVersionInput) (*types.UpdatePolicyVersionPayload, error) {
|
||||
svc := GetTenantService(ctx, r.proboSvc, input.PolicyVersionID.TenantID())
|
||||
|
||||
policyVersion, err := svc.Policies.UpdateVersion(ctx, probo.UpdatePolicyVersionRequest{
|
||||
ID: input.PolicyVersionID,
|
||||
Content: input.Content,
|
||||
})
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot update policy version: %w", err))
|
||||
}
|
||||
|
||||
return &types.UpdatePolicyVersionPayload{
|
||||
PolicyVersion: types.NewPolicyVersion(policyVersion),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RequestSignature is the resolver for the requestSignature field.
|
||||
func (r *mutationResolver) RequestSignature(ctx context.Context, input types.RequestSignatureInput) (*types.RequestSignaturePayload, error) {
|
||||
svc := GetTenantService(ctx, r.proboSvc, input.PolicyVersionID.TenantID())
|
||||
|
||||
user := UserFromContext(ctx)
|
||||
|
||||
people, err := svc.Peoples.GetByUserID(ctx, user.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get people: %w", err))
|
||||
}
|
||||
|
||||
policyVersionSignature, err := svc.Policies.RequestSignature(
|
||||
ctx,
|
||||
probo.RequestSignatureRequest{
|
||||
PolicyVersionID: input.PolicyVersionID,
|
||||
RequestedBy: people.ID,
|
||||
Signatory: input.SignatoryID,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot request signature: %w", err))
|
||||
}
|
||||
|
||||
return &types.RequestSignaturePayload{
|
||||
PolicyVersionSignatureEdge: types.NewPolicyVersionSignatureEdge(policyVersionSignature, coredata.PolicyVersionSignatureOrderFieldCreatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateVendorRiskAssessment is the resolver for the createVendorRiskAssessment field.
|
||||
func (r *mutationResolver) CreateVendorRiskAssessment(ctx context.Context, input types.CreateVendorRiskAssessmentInput) (*types.CreateVendorRiskAssessmentPayload, error) {
|
||||
svc := GetTenantService(ctx, r.proboSvc, input.VendorID.TenantID())
|
||||
|
||||
fmt.Println("input.AssessedBy", input.AssessedBy)
|
||||
fmt.Println("input.ExpiresAt", input.ExpiresAt)
|
||||
fmt.Println("input.DataSensitivity", input.DataSensitivity)
|
||||
fmt.Println("input.BusinessImpact", input.BusinessImpact)
|
||||
fmt.Println("input.Notes", input.Notes)
|
||||
|
||||
vendorRiskAssessment, err := svc.Vendors.CreateRiskAssessment(
|
||||
ctx,
|
||||
probo.CreateVendorRiskAssessmentRequest{
|
||||
@@ -1225,7 +1293,7 @@ func (r *organizationResolver) Policies(ctx context.Context, obj *types.Organiza
|
||||
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
|
||||
|
||||
pageOrderBy := page.OrderBy[coredata.PolicyOrderField]{
|
||||
Field: coredata.PolicyOrderFieldName,
|
||||
Field: coredata.PolicyOrderFieldTitle,
|
||||
Direction: page.OrderDirectionDesc,
|
||||
}
|
||||
if orderBy != nil {
|
||||
@@ -1313,6 +1381,31 @@ func (r *policyResolver) Owner(ctx context.Context, obj *types.Policy) (*types.P
|
||||
return types.NewPeople(owner), nil
|
||||
}
|
||||
|
||||
// Versions is the resolver for the versions field.
|
||||
func (r *policyResolver) Versions(ctx context.Context, obj *types.Policy, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PolicyVersionOrderBy, filter *types.PolicyVersionFilter) (*types.PolicyVersionConnection, error) {
|
||||
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
|
||||
|
||||
pageOrderBy := page.OrderBy[coredata.PolicyVersionOrderField]{
|
||||
Field: coredata.PolicyVersionOrderFieldCreatedAt,
|
||||
Direction: page.OrderDirectionDesc,
|
||||
}
|
||||
if orderBy != nil {
|
||||
pageOrderBy = page.OrderBy[coredata.PolicyVersionOrderField]{
|
||||
Field: orderBy.Field,
|
||||
Direction: orderBy.Direction,
|
||||
}
|
||||
}
|
||||
|
||||
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
||||
|
||||
page, err := svc.Policies.ListVersions(ctx, obj.ID, cursor)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list policy versions: %w", err))
|
||||
}
|
||||
|
||||
return types.NewPolicyVersionConnection(page), nil
|
||||
}
|
||||
|
||||
// Controls is the resolver for the controls field.
|
||||
func (r *policyResolver) Controls(ctx context.Context, obj *types.Policy, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) {
|
||||
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
|
||||
@@ -1338,6 +1431,120 @@ func (r *policyResolver) Controls(ctx context.Context, obj *types.Policy, first
|
||||
return types.NewControlConnection(page), nil
|
||||
}
|
||||
|
||||
// Policy is the resolver for the policy field.
|
||||
func (r *policyVersionResolver) Policy(ctx context.Context, obj *types.PolicyVersion) (*types.Policy, error) {
|
||||
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
|
||||
|
||||
policyVersion, err := svc.Policies.GetVersion(ctx, obj.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get policy version: %w", err))
|
||||
}
|
||||
|
||||
policy, err := svc.Policies.Get(ctx, policyVersion.PolicyID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get policy: %w", err))
|
||||
}
|
||||
|
||||
return types.NewPolicy(policy), nil
|
||||
}
|
||||
|
||||
// Signatures is the resolver for the signatures field.
|
||||
func (r *policyVersionResolver) Signatures(ctx context.Context, obj *types.PolicyVersion, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PolicyVersionSignatureOrder) (*types.PolicyVersionSignatureConnection, error) {
|
||||
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
|
||||
|
||||
pageOrderBy := page.OrderBy[coredata.PolicyVersionSignatureOrderField]{
|
||||
Field: coredata.PolicyVersionSignatureOrderFieldCreatedAt,
|
||||
Direction: page.OrderDirectionDesc,
|
||||
}
|
||||
if orderBy != nil {
|
||||
pageOrderBy = page.OrderBy[coredata.PolicyVersionSignatureOrderField]{
|
||||
Field: orderBy.Field,
|
||||
Direction: orderBy.Direction,
|
||||
}
|
||||
}
|
||||
|
||||
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
||||
|
||||
page, err := svc.Policies.ListSignatures(ctx, obj.ID, cursor)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list policy version signatures: %w", err))
|
||||
}
|
||||
|
||||
return types.NewPolicyVersionSignatureConnection(page), nil
|
||||
}
|
||||
|
||||
// PublishedBy is the resolver for the publishedBy field.
|
||||
func (r *policyVersionResolver) PublishedBy(ctx context.Context, obj *types.PolicyVersion) (*types.People, error) {
|
||||
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
|
||||
|
||||
policyVersion, err := svc.Policies.GetVersion(ctx, obj.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get policy version: %w", err))
|
||||
}
|
||||
|
||||
if policyVersion.PublishedBy == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
people, err := svc.Peoples.Get(ctx, *policyVersion.PublishedBy)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get people: %w", err))
|
||||
}
|
||||
|
||||
return types.NewPeople(people), nil
|
||||
}
|
||||
|
||||
// PolicyVersion is the resolver for the policyVersion field.
|
||||
func (r *policyVersionSignatureResolver) PolicyVersion(ctx context.Context, obj *types.PolicyVersionSignature) (*types.PolicyVersion, error) {
|
||||
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
|
||||
|
||||
policyVersionSignature, err := svc.Policies.GetVersionSignature(ctx, obj.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get policy version signature: %w", err))
|
||||
}
|
||||
|
||||
policyVersion, err := svc.Policies.GetVersion(ctx, policyVersionSignature.PolicyVersionID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get policy version: %w", err))
|
||||
}
|
||||
|
||||
return types.NewPolicyVersion(policyVersion), nil
|
||||
}
|
||||
|
||||
// SignedBy is the resolver for the signedBy field.
|
||||
func (r *policyVersionSignatureResolver) SignedBy(ctx context.Context, obj *types.PolicyVersionSignature) (*types.People, error) {
|
||||
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
|
||||
|
||||
policyVersionSignature, err := svc.Policies.GetVersionSignature(ctx, obj.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get policy version signature: %w", err))
|
||||
}
|
||||
|
||||
people, err := svc.Peoples.Get(ctx, policyVersionSignature.SignedBy)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get people: %w", err))
|
||||
}
|
||||
|
||||
return types.NewPeople(people), nil
|
||||
}
|
||||
|
||||
// RequestedBy is the resolver for the requestedBy field.
|
||||
func (r *policyVersionSignatureResolver) RequestedBy(ctx context.Context, obj *types.PolicyVersionSignature) (*types.People, error) {
|
||||
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
|
||||
|
||||
policyVersionSignature, err := svc.Policies.GetVersionSignature(ctx, obj.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get policy version signature: %w", err))
|
||||
}
|
||||
|
||||
people, err := svc.Peoples.Get(ctx, policyVersionSignature.RequestedBy)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get people: %w", err))
|
||||
}
|
||||
|
||||
return types.NewPeople(people), nil
|
||||
}
|
||||
|
||||
// Node is the resolver for the node field.
|
||||
func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error) {
|
||||
svc := GetTenantService(ctx, r.proboSvc, id.TenantID())
|
||||
@@ -1417,6 +1624,18 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error
|
||||
panic(fmt.Errorf("cannot get vendor compliance report: %w", err))
|
||||
}
|
||||
return types.NewVendorComplianceReport(vendorComplianceReport), nil
|
||||
case coredata.PolicyVersionEntityType:
|
||||
policyVersion, err := svc.Policies.GetVersion(ctx, id)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get policy version: %w", err))
|
||||
}
|
||||
return types.NewPolicyVersion(policyVersion), nil
|
||||
case coredata.PolicyVersionSignatureEntityType:
|
||||
policyVersionSignature, err := svc.Policies.GetVersionSignature(ctx, id)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get policy version signature: %w", err))
|
||||
}
|
||||
return types.NewPolicyVersionSignature(policyVersionSignature), nil
|
||||
default:
|
||||
}
|
||||
|
||||
@@ -1579,7 +1798,7 @@ func (r *taskResolver) Evidences(ctx context.Context, obj *types.Task, first *in
|
||||
func (r *userResolver) People(ctx context.Context, obj *types.User, organizationID gid.GID) (*types.People, error) {
|
||||
svc := GetTenantService(ctx, r.proboSvc, organizationID.TenantID())
|
||||
|
||||
people, err := svc.Peoples.GetByUserID(ctx, organizationID, obj.ID)
|
||||
people, err := svc.Peoples.GetByUserID(ctx, obj.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to get people: %w", err))
|
||||
}
|
||||
@@ -1778,6 +1997,14 @@ func (r *Resolver) Organization() schema.OrganizationResolver { return &organiza
|
||||
// Policy returns schema.PolicyResolver implementation.
|
||||
func (r *Resolver) Policy() schema.PolicyResolver { return &policyResolver{r} }
|
||||
|
||||
// PolicyVersion returns schema.PolicyVersionResolver implementation.
|
||||
func (r *Resolver) PolicyVersion() schema.PolicyVersionResolver { return &policyVersionResolver{r} }
|
||||
|
||||
// PolicyVersionSignature returns schema.PolicyVersionSignatureResolver implementation.
|
||||
func (r *Resolver) PolicyVersionSignature() schema.PolicyVersionSignatureResolver {
|
||||
return &policyVersionSignatureResolver{r}
|
||||
}
|
||||
|
||||
// Query returns schema.QueryResolver implementation.
|
||||
func (r *Resolver) Query() schema.QueryResolver { return &queryResolver{r} }
|
||||
|
||||
@@ -1813,6 +2040,8 @@ type mesureResolver struct{ *Resolver }
|
||||
type mutationResolver struct{ *Resolver }
|
||||
type organizationResolver struct{ *Resolver }
|
||||
type policyResolver struct{ *Resolver }
|
||||
type policyVersionResolver struct{ *Resolver }
|
||||
type policyVersionSignatureResolver struct{ *Resolver }
|
||||
type queryResolver struct{ *Resolver }
|
||||
type riskResolver struct{ *Resolver }
|
||||
type taskResolver struct{ *Resolver }
|
||||
|
||||
Reference in New Issue
Block a user