@@ -40,6 +40,7 @@ type (
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
Version int `db:"version"`
|
||||
Standards []string `db:"standards"`
|
||||
}
|
||||
|
||||
Controls []*Control
|
||||
@@ -76,6 +77,7 @@ SELECT
|
||||
content_ref,
|
||||
created_at,
|
||||
updated_at,
|
||||
standards,
|
||||
version
|
||||
FROM
|
||||
controls
|
||||
@@ -124,6 +126,7 @@ INSERT INTO
|
||||
content_ref,
|
||||
created_at,
|
||||
updated_at,
|
||||
standards,
|
||||
version
|
||||
)
|
||||
VALUES (
|
||||
@@ -138,6 +141,7 @@ VALUES (
|
||||
@content_ref,
|
||||
@created_at,
|
||||
@updated_at,
|
||||
@standards,
|
||||
@version
|
||||
);
|
||||
`
|
||||
@@ -155,6 +159,7 @@ VALUES (
|
||||
"updated_at": c.UpdatedAt,
|
||||
"state": c.State,
|
||||
"importance": c.Importance,
|
||||
"standards": c.Standards,
|
||||
}
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
return err
|
||||
@@ -179,6 +184,7 @@ SELECT
|
||||
content_ref,
|
||||
created_at,
|
||||
updated_at,
|
||||
standards,
|
||||
version
|
||||
FROM
|
||||
controls
|
||||
@@ -237,7 +243,8 @@ RETURNING
|
||||
content_ref,
|
||||
created_at,
|
||||
updated_at,
|
||||
version
|
||||
version,
|
||||
standards
|
||||
`
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
|
||||
2
pkg/coredata/migrations/20250313T160700Z.sql
Normal file
2
pkg/coredata/migrations/20250313T160700Z.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE controls ADD COLUMN standards TEXT[] DEFAULT '{}' NOT NULL;
|
||||
ALTER TABLE controls ALTER COLUMN standards DROP DEFAULT;
|
||||
@@ -43,6 +43,25 @@ type (
|
||||
Name *string
|
||||
Description *string
|
||||
}
|
||||
|
||||
ImportFrameworkRequest struct {
|
||||
Data struct {
|
||||
Framework struct {
|
||||
Name string `json:"name"`
|
||||
ContentRef string `json:"content-ref"`
|
||||
Description string `json:"description"`
|
||||
Version string `json:"version"`
|
||||
Controls []struct {
|
||||
ContentRef string `json:"content-ref"`
|
||||
Category string `json:"category"`
|
||||
Importance coredata.ControlImportance `json:"importance"`
|
||||
Standards []string `json:"standards"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
} `json:"controls"`
|
||||
} `json:"framework"`
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
func (s FrameworkService) Create(
|
||||
@@ -163,3 +182,76 @@ func (s FrameworkService) Delete(
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (s FrameworkService) Import(
|
||||
ctx context.Context,
|
||||
organizationID gid.GID,
|
||||
req ImportFrameworkRequest,
|
||||
) (*coredata.Framework, error) {
|
||||
|
||||
now := time.Now()
|
||||
|
||||
frameworkID, err := gid.NewGID(organizationID.TenantID(), coredata.FrameworkEntityType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create global id: %w", err)
|
||||
}
|
||||
|
||||
framework := &coredata.Framework{
|
||||
ID: frameworkID,
|
||||
OrganizationID: organizationID,
|
||||
Name: req.Data.Framework.Name,
|
||||
Description: req.Data.Framework.Description,
|
||||
ContentRef: req.Data.Framework.ContentRef,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
importedControls := coredata.Controls{}
|
||||
for _, control := range req.Data.Framework.Controls {
|
||||
controlID, err := gid.NewGID(organizationID.TenantID(), coredata.ControlEntityType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create global id: %w", err)
|
||||
}
|
||||
|
||||
importedControl := &coredata.Control{
|
||||
ID: controlID,
|
||||
FrameworkID: frameworkID,
|
||||
Category: control.Category,
|
||||
Importance: coredata.ControlImportance(control.Importance),
|
||||
Name: control.Name,
|
||||
Description: control.Description,
|
||||
State: coredata.ControlStateNotStarted,
|
||||
ContentRef: control.ContentRef,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
Standards: control.Standards,
|
||||
}
|
||||
|
||||
importedControls = append(importedControls, importedControl)
|
||||
}
|
||||
|
||||
err = s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(tx pg.Conn) error {
|
||||
|
||||
err := framework.Insert(ctx, tx, s.svc.scope)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot insert framework: %w", err)
|
||||
}
|
||||
|
||||
for _, importedControl := range importedControls {
|
||||
if err := importedControl.Insert(ctx, tx, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot insert control: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return framework, nil
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package console_v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@@ -348,7 +349,21 @@ func (r *mutationResolver) UpdateFramework(ctx context.Context, input types.Upda
|
||||
|
||||
// ImportFramework is the resolver for the importFramework field.
|
||||
func (r *mutationResolver) ImportFramework(ctx context.Context, input types.ImportFrameworkInput) (*types.ImportFrameworkPayload, error) {
|
||||
panic(fmt.Errorf("not implemented: ImportFramework - importFramework"))
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID())
|
||||
|
||||
req := probo.ImportFrameworkRequest{}
|
||||
if err := json.NewDecoder(input.File.File).Decode(&req.Data); err != nil {
|
||||
return nil, fmt.Errorf("cannot decode framework: %w", err)
|
||||
}
|
||||
|
||||
framework, err := svc.Frameworks.Import(ctx, input.OrganizationID, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot import framework: %w", err)
|
||||
}
|
||||
|
||||
return &types.ImportFrameworkPayload{
|
||||
FrameworkEdge: types.NewFrameworkEdge(framework),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateControl is the resolver for the createControl field.
|
||||
|
||||
Reference in New Issue
Block a user