diff --git a/pkg/probo/coredata/control.go b/pkg/probo/coredata/control.go index 59f2f0192..2b6b299d2 100644 --- a/pkg/probo/coredata/control.go +++ b/pkg/probo/coredata/control.go @@ -117,6 +117,47 @@ LIMIT 1; return nil } +func (c Control) Insert( + ctx context.Context, + conn pg.Conn, +) error { + q := ` +INSERT INTO + controls ( + id, + control_id, + name, + description, + content_ref, + state, + created_at, + updated_at + ) +VALUES ( + @control_id, + @framework_id, + @name, + @description, + @content_ref, + @state, + @created_at, + @updated_at +); +` + + args := pgx.NamedArgs{ + "control_id": c.ID, + "framework_id": c.FrameworkID, + "name": c.Name, + "description": c.Description, + "content_ref": c.ContentRef, + "created_at": c.CreatedAt, + "updated_at": c.UpdatedAt, + } + _, err := conn.Exec(ctx, q, args) + return err +} + func (c *Controls) LoadByFrameworkID( ctx context.Context, conn pg.Conn, diff --git a/pkg/probo/create_control.go b/pkg/probo/create_control.go new file mode 100644 index 000000000..bc93ae233 --- /dev/null +++ b/pkg/probo/create_control.go @@ -0,0 +1,77 @@ +// Copyright (c) 2025 Probo Inc . +// +// 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 probo + +import ( + "context" + "fmt" + "time" + + "github.com/getprobo/probo/pkg/gid" + "github.com/getprobo/probo/pkg/probo/coredata" + "go.gearno.de/kit/pg" +) + +type ( + CreateControlRequest struct { + FrameworkID gid.GID + Name string + Description string + ContentRef string + } +) + +func (s Service) CreateControl( + ctx context.Context, + req CreateControlRequest, +) (*coredata.Control, error) { + now := time.Now() + controlID, err := gid.NewGID(coredata.ControlEntityType) + if err != nil { + return nil, fmt.Errorf("cannot create global id: %w", err) + } + + framework := &coredata.Framework{} + control := &coredata.Control{ + ID: controlID, + FrameworkID: req.FrameworkID, + Name: req.Name, + Description: req.Description, + ContentRef: req.ContentRef, + CreatedAt: now, + UpdatedAt: now, + } + + err = s.pg.WithTx( + ctx, + func(conn pg.Conn) error { + if err := framework.LoadByID(ctx, conn, s.scope, req.FrameworkID); err != nil { + return fmt.Errorf("cannot load framework %q: %w", req.FrameworkID, err) + } + + if err := control.Insert(ctx, conn); err != nil { + return fmt.Errorf("cannot insert control: %w", err) + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + return control, nil +}