From 9640e4266a7375fa74db59d4cbfc8ac9a707e76d Mon Sep 17 00:00:00 2001 From: gearnode Date: Tue, 11 Mar 2025 17:02:28 +0100 Subject: [PATCH] Refactor probo service Signed-off-by: gearnode --- pkg/coredata/user.go | 13 +- pkg/probo/control_service.go | 167 +++++++++++++++ pkg/probo/create_control.go | 80 ------- pkg/probo/create_framework.go | 68 ------ pkg/probo/create_people.go | 79 ------- pkg/probo/create_task.go | 78 ------- pkg/probo/create_vendor.go | 74 +------ pkg/probo/delete_evidence.go | 42 ---- pkg/probo/delete_framework.go | 37 ---- pkg/probo/delete_people.go | 37 ---- pkg/probo/delete_task.go | 36 ---- pkg/probo/delete_vendor.go | 36 ---- ...create_evidence.go => evidence_service.go} | 115 +++++++++- pkg/probo/framework_service.go | 165 +++++++++++++++ pkg/probo/get_control.go | 43 ---- pkg/probo/get_evidence.go | 43 ---- pkg/probo/get_evidence_file_url.go | 52 ----- pkg/probo/get_framework.go | 43 ---- pkg/probo/get_organization.go | 48 ----- pkg/probo/get_people.go | 43 ---- pkg/probo/get_task.go | 43 ---- pkg/probo/get_vendor.go | 43 ---- pkg/probo/list_control_tasks.go | 51 ----- pkg/probo/list_framework_controls.go | 51 ----- pkg/probo/list_organization_frameworks.go | 51 ----- pkg/probo/list_organization_peoples.go | 51 ----- pkg/probo/list_organization_vendors.go | 51 ----- pkg/probo/list_task_evidences.go | 51 ----- ...rganization.go => organization_service.go} | 39 +++- pkg/probo/people_service.go | 180 ++++++++++++++++ pkg/probo/policy_service.go | 4 +- pkg/probo/service.go | 36 +++- pkg/probo/task_service.go | 176 ++++++++++++++++ pkg/probo/update_control.go | 58 ----- pkg/probo/update_framework.go | 54 ----- pkg/probo/update_people.go | 58 ----- pkg/probo/update_task.go | 56 ----- pkg/probo/update_vendor.go | 69 ------ pkg/probo/vendor_service.go | 199 ++++++++++++++++++ pkg/server/api/console/v1/v1_resolver.go | 72 ++++--- 40 files changed, 1101 insertions(+), 1591 deletions(-) create mode 100644 pkg/probo/control_service.go delete mode 100644 pkg/probo/create_control.go delete mode 100644 pkg/probo/create_framework.go delete mode 100644 pkg/probo/create_people.go delete mode 100644 pkg/probo/create_task.go delete mode 100644 pkg/probo/delete_evidence.go delete mode 100644 pkg/probo/delete_framework.go delete mode 100644 pkg/probo/delete_people.go delete mode 100644 pkg/probo/delete_task.go delete mode 100644 pkg/probo/delete_vendor.go rename pkg/probo/{create_evidence.go => evidence_service.go} (50%) create mode 100644 pkg/probo/framework_service.go delete mode 100644 pkg/probo/get_control.go delete mode 100644 pkg/probo/get_evidence.go delete mode 100644 pkg/probo/get_evidence_file_url.go delete mode 100644 pkg/probo/get_framework.go delete mode 100644 pkg/probo/get_organization.go delete mode 100644 pkg/probo/get_people.go delete mode 100644 pkg/probo/get_task.go delete mode 100644 pkg/probo/get_vendor.go delete mode 100644 pkg/probo/list_control_tasks.go delete mode 100644 pkg/probo/list_framework_controls.go delete mode 100644 pkg/probo/list_organization_frameworks.go delete mode 100644 pkg/probo/list_organization_peoples.go delete mode 100644 pkg/probo/list_organization_vendors.go delete mode 100644 pkg/probo/list_task_evidences.go rename pkg/probo/{create_organization.go => organization_service.go} (71%) create mode 100644 pkg/probo/people_service.go create mode 100644 pkg/probo/task_service.go delete mode 100644 pkg/probo/update_control.go delete mode 100644 pkg/probo/update_framework.go delete mode 100644 pkg/probo/update_people.go delete mode 100644 pkg/probo/update_task.go delete mode 100644 pkg/probo/update_vendor.go create mode 100644 pkg/probo/vendor_service.go diff --git a/pkg/coredata/user.go b/pkg/coredata/user.go index b87016bf4..c3cfa5f2c 100644 --- a/pkg/coredata/user.go +++ b/pkg/coredata/user.go @@ -30,12 +30,13 @@ import ( type ( User struct { - ID gid.GID `db:"id"` - EmailAddress string `db:"email_address"` - HashedPassword []byte `db:"hashed_password"` - FullName string `db:"fullname"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` + ID gid.GID `db:"id"` + EmailAddress string `db:"email_address"` + HashedPassword []byte `db:"hashed_password"` + FullName string `db:"fullname"` + EmailAddressVerified bool `db:"email_address_verified"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` } ErrUserNotFound struct { diff --git a/pkg/probo/control_service.go b/pkg/probo/control_service.go new file mode 100644 index 000000000..cab05d85b --- /dev/null +++ b/pkg/probo/control_service.go @@ -0,0 +1,167 @@ +// 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/coredata" + "github.com/getprobo/probo/pkg/gid" + "github.com/getprobo/probo/pkg/page" + "go.gearno.de/kit/pg" +) + +type ( + ControlService struct { + svc *TenantService + } + + CreateControlRequest struct { + FrameworkID gid.GID + Name string + Description string + ContentRef string + Category string + } + + UpdateControlRequest struct { + ID gid.GID + ExpectedVersion int + Name *string + Description *string + Category *string + State *coredata.ControlState + } +) + +func (s ControlService) Get( + ctx context.Context, + controlID gid.GID, +) (*coredata.Control, error) { + control := &coredata.Control{} + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return control.LoadByID(ctx, conn, s.svc.scope, controlID) + }, + ) + + if err != nil { + return nil, err + } + + return control, nil +} + +func (s ControlService) Update( + ctx context.Context, + req UpdateControlRequest, +) (*coredata.Control, error) { + params := coredata.UpdateControlParams{ + ExpectedVersion: req.ExpectedVersion, + Name: req.Name, + Description: req.Description, + Category: req.Category, + State: req.State, + } + + control := &coredata.Control{ID: req.ID} + + err := s.svc.pg.WithTx( + ctx, + func(conn pg.Conn) error { + return control.Update(ctx, conn, s.svc.scope, params) + }) + if err != nil { + return nil, err + } + + return control, nil +} + +func (s ControlService) ListForFrameworkID( + ctx context.Context, + frameworkID gid.GID, + cursor *page.Cursor, +) (*page.Page[*coredata.Control], error) { + var controls coredata.Controls + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return controls.LoadByFrameworkID( + ctx, + conn, + s.svc.scope, + frameworkID, + cursor, + ) + }, + ) + + if err != nil { + return nil, err + } + + return page.NewPage(controls, cursor), nil +} + +func (s ControlService) Create( + ctx context.Context, + req CreateControlRequest, +) (*coredata.Control, error) { + now := time.Now() + controlID, err := gid.NewGID(s.svc.scope.GetTenantID(), coredata.ControlEntityType) + if err != nil { + return nil, fmt.Errorf("cannot create control global id: %w", err) + } + + framework := &coredata.Framework{} + control := &coredata.Control{ + ID: controlID, + FrameworkID: req.FrameworkID, + Name: req.Name, + Description: req.Description, + Category: req.Category, + State: coredata.ControlStateNotStarted, + ContentRef: req.ContentRef, + CreatedAt: now, + UpdatedAt: now, + } + + err = s.svc.pg.WithTx( + ctx, + func(conn pg.Conn) error { + if err := framework.LoadByID(ctx, conn, s.svc.scope, req.FrameworkID); err != nil { + return fmt.Errorf("cannot load framework %q: %w", req.FrameworkID, err) + } + + if err := control.Insert(ctx, conn, s.svc.scope); err != nil { + return fmt.Errorf("cannot insert control: %w", err) + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + return control, nil +} diff --git a/pkg/probo/create_control.go b/pkg/probo/create_control.go deleted file mode 100644 index ffc74f43b..000000000 --- a/pkg/probo/create_control.go +++ /dev/null @@ -1,80 +0,0 @@ -// 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/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -type ( - CreateControlRequest struct { - FrameworkID gid.GID - Name string - Description string - ContentRef string - Category string - } -) - -func (s Service) CreateControl( - ctx context.Context, - req CreateControlRequest, -) (*coredata.Control, error) { - now := time.Now() - controlID, err := gid.NewGID(s.scope.GetTenantID(), coredata.ControlEntityType) - if err != nil { - return nil, fmt.Errorf("cannot create control global id: %w", err) - } - - framework := &coredata.Framework{} - control := &coredata.Control{ - ID: controlID, - FrameworkID: req.FrameworkID, - Name: req.Name, - Description: req.Description, - Category: req.Category, - State: coredata.ControlStateNotStarted, - 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, s.scope); err != nil { - return fmt.Errorf("cannot insert control: %w", err) - } - - return nil - }, - ) - - if err != nil { - return nil, err - } - - return control, nil -} diff --git a/pkg/probo/create_framework.go b/pkg/probo/create_framework.go deleted file mode 100644 index f09ce5c0c..000000000 --- a/pkg/probo/create_framework.go +++ /dev/null @@ -1,68 +0,0 @@ -// 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/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -type ( - CreateFrameworkRequest struct { - OrganizationID gid.GID - Name string - Description string - ContentRef string - } -) - -func (s Service) CreateFramework( - ctx context.Context, - req CreateFrameworkRequest, -) (*coredata.Framework, error) { - now := time.Now() - frameworkID, err := gid.NewGID(s.scope.GetTenantID(), coredata.FrameworkEntityType) - if err != nil { - return nil, fmt.Errorf("cannot create global id: %w", err) - } - - framework := &coredata.Framework{ - ID: frameworkID, - OrganizationID: req.OrganizationID, - Name: req.Name, - Description: req.Description, - ContentRef: req.ContentRef, - CreatedAt: now, - UpdatedAt: now, - } - - err = s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - return framework.Insert(ctx, conn, s.scope) - }, - ) - - if err != nil { - return nil, err - } - - return framework, nil -} diff --git a/pkg/probo/create_people.go b/pkg/probo/create_people.go deleted file mode 100644 index 4fc8cf63e..000000000 --- a/pkg/probo/create_people.go +++ /dev/null @@ -1,79 +0,0 @@ -// 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/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -type ( - CreatePeopleRequest struct { - OrganizationID gid.GID - FullName string - PrimaryEmailAddress string - AdditionalEmailAddresses []string - Kind coredata.PeopleKind - } -) - -func (s Service) CreatePeople( - ctx context.Context, - req CreatePeopleRequest, -) (*coredata.People, error) { - now := time.Now() - peopleID, err := gid.NewGID(s.scope.GetTenantID(), coredata.PeopleEntityType) - if err != nil { - return nil, fmt.Errorf("cannot create people global id: %w", err) - } - - organization := &coredata.Organization{} - people := &coredata.People{ - ID: peopleID, - OrganizationID: req.OrganizationID, - Kind: req.Kind, - FullName: req.FullName, - PrimaryEmailAddress: req.PrimaryEmailAddress, - AdditionalEmailAddresses: req.AdditionalEmailAddresses, - CreatedAt: now, - UpdatedAt: now, - } - - err = s.pg.WithTx( - ctx, - func(conn pg.Conn) error { - if err := organization.LoadByID(ctx, conn, s.scope, req.OrganizationID); err != nil { - return fmt.Errorf("cannot load organization %q: %w", req.OrganizationID, err) - } - - if err := people.Insert(ctx, conn, s.scope); err != nil { - return fmt.Errorf("cannot insert people: %w", err) - } - - return nil - }, - ) - - if err != nil { - return nil, err - } - - return people, nil -} diff --git a/pkg/probo/create_task.go b/pkg/probo/create_task.go deleted file mode 100644 index 93c18dbe7..000000000 --- a/pkg/probo/create_task.go +++ /dev/null @@ -1,78 +0,0 @@ -// 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/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -type ( - CreateTaskRequest struct { - ControlID gid.GID - Name string - ContentRef string - Description string - } -) - -func (s Service) CreateTask( - ctx context.Context, - req CreateTaskRequest, -) (*coredata.Task, error) { - now := time.Now() - taskID, err := gid.NewGID(s.scope.GetTenantID(), coredata.TaskEntityType) - if err != nil { - return nil, fmt.Errorf("cannot create task global id: %w", err) - } - - control := &coredata.Control{} - task := &coredata.Task{ - ID: taskID, - ControlID: req.ControlID, - Name: req.Name, - ContentRef: req.ContentRef, - State: coredata.TaskStateTodo, - Description: req.Description, - CreatedAt: now, - UpdatedAt: now, - } - - err = s.pg.WithTx( - ctx, - func(conn pg.Conn) error { - if err := control.LoadByID(ctx, conn, s.scope, req.ControlID); err != nil { - return fmt.Errorf("cannot laod control %q: %w", req.ControlID, err) - } - - if err := task.Insert(ctx, conn, s.scope); err != nil { - return fmt.Errorf("cannot insert task: %w", err) - } - - return nil - }, - ) - - if err != nil { - return nil, err - } - - return task, nil -} diff --git a/pkg/probo/create_vendor.go b/pkg/probo/create_vendor.go index cfe5b452f..2a6f4b791 100644 --- a/pkg/probo/create_vendor.go +++ b/pkg/probo/create_vendor.go @@ -14,76 +14,4 @@ package probo -import ( - "context" - "fmt" - "time" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -type ( - CreateVendorRequest struct { - OrganizationID gid.GID - Name string - Description string - ServiceStartAt time.Time - ServiceTerminationAt *time.Time - ServiceCriticality coredata.ServiceCriticality - RiskTier coredata.RiskTier - StatusPageURL *string - TermsOfServiceURL *string - PrivacyPolicyURL *string - } -) - -func (s Service) CreateVendor( - ctx context.Context, - req CreateVendorRequest, -) (*coredata.Vendor, error) { - now := time.Now() - vendorID, err := gid.NewGID(s.scope.GetTenantID(), coredata.VendorEntityType) - if err != nil { - return nil, fmt.Errorf("cannot create vendor global id: %w", err) - } - - organization := &coredata.Organization{} - vendor := &coredata.Vendor{ - ID: vendorID, - OrganizationID: req.OrganizationID, - Name: req.Name, - CreatedAt: now, - UpdatedAt: now, - Description: req.Description, - ServiceStartAt: req.ServiceStartAt, - ServiceTerminationAt: req.ServiceTerminationAt, - ServiceCriticality: req.ServiceCriticality, - RiskTier: req.RiskTier, - StatusPageURL: req.StatusPageURL, - TermsOfServiceURL: req.TermsOfServiceURL, - PrivacyPolicyURL: req.PrivacyPolicyURL, - } - - err = s.pg.WithTx( - ctx, - func(conn pg.Conn) error { - if err := organization.LoadByID(ctx, conn, s.scope, req.OrganizationID); err != nil { - return fmt.Errorf("cannot load organization %q: %w", req.OrganizationID, err) - } - - if err := vendor.Insert(ctx, conn, s.scope); err != nil { - return fmt.Errorf("cannot insert vendor: %w", err) - } - - return nil - }, - ) - - if err != nil { - return nil, err - } - - return vendor, nil -} +type () diff --git a/pkg/probo/delete_evidence.go b/pkg/probo/delete_evidence.go deleted file mode 100644 index bf1b4ffee..000000000 --- a/pkg/probo/delete_evidence.go +++ /dev/null @@ -1,42 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -func (s *Service) DeleteEvidence( - ctx context.Context, - evidenceID gid.GID, -) error { - evidence := &coredata.Evidence{ID: evidenceID} - - return s.pg.WithTx( - ctx, - func(conn pg.Conn) error { - if err := evidence.Delete(ctx, conn, s.scope); err != nil { - return fmt.Errorf("cannot delete evidence: %w", err) - } - - return nil - }, - ) -} diff --git a/pkg/probo/delete_framework.go b/pkg/probo/delete_framework.go deleted file mode 100644 index cf5ccad7d..000000000 --- a/pkg/probo/delete_framework.go +++ /dev/null @@ -1,37 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -func (s Service) DeleteFramework( - ctx context.Context, - frameworkID gid.GID, -) error { - framework := &coredata.Framework{ID: frameworkID} - - return s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - return framework.Delete(ctx, conn, s.scope) - }, - ) -} diff --git a/pkg/probo/delete_people.go b/pkg/probo/delete_people.go deleted file mode 100644 index 0f3e7094e..000000000 --- a/pkg/probo/delete_people.go +++ /dev/null @@ -1,37 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -func (s Service) DeletePeople( - ctx context.Context, - peopleID gid.GID, -) error { - people := coredata.People{ID: peopleID} - - return s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - return people.Delete(ctx, conn, s.scope) - }, - ) -} diff --git a/pkg/probo/delete_task.go b/pkg/probo/delete_task.go deleted file mode 100644 index 48dfdf8d8..000000000 --- a/pkg/probo/delete_task.go +++ /dev/null @@ -1,36 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -func (s Service) DeleteTask( - ctx context.Context, - taskID gid.GID, -) error { - task := coredata.Task{ID: taskID} - return s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - return task.Delete(ctx, conn, s.scope) - }, - ) -} diff --git a/pkg/probo/delete_vendor.go b/pkg/probo/delete_vendor.go deleted file mode 100644 index ffa05fa85..000000000 --- a/pkg/probo/delete_vendor.go +++ /dev/null @@ -1,36 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -func (s Service) DeleteVendor( - ctx context.Context, - vendorID gid.GID, -) error { - vendor := coredata.Vendor{ID: vendorID} - return s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - return vendor.Delete(ctx, conn, s.scope) - }, - ) -} diff --git a/pkg/probo/create_evidence.go b/pkg/probo/evidence_service.go similarity index 50% rename from pkg/probo/create_evidence.go rename to pkg/probo/evidence_service.go index d61a5d8dd..6f922eff2 100644 --- a/pkg/probo/create_evidence.go +++ b/pkg/probo/evidence_service.go @@ -26,11 +26,16 @@ import ( "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/getprobo/probo/pkg/coredata" "github.com/getprobo/probo/pkg/gid" + "github.com/getprobo/probo/pkg/page" "go.gearno.de/crypto/uuid" "go.gearno.de/kit/pg" ) type ( + EvidenceService struct { + svc *TenantService + } + CreateEvidenceRequest struct { TaskID gid.GID Name string @@ -38,12 +43,32 @@ type ( } ) -func (s Service) CreateEvidence( +func (s EvidenceService) Get( + ctx context.Context, + evidenceID gid.GID, +) (*coredata.Evidence, error) { + evidence := &coredata.Evidence{} + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return evidence.LoadByID(ctx, conn, s.svc.scope, evidenceID) + }, + ) + + if err != nil { + return nil, err + } + + return evidence, nil +} + +func (s EvidenceService) Create( ctx context.Context, req CreateEvidenceRequest, ) (*coredata.Evidence, error) { now := time.Now() - evidenceID, err := gid.NewGID(s.scope.GetTenantID(), coredata.EvidenceEntityType) + evidenceID, err := gid.NewGID(s.svc.scope.GetTenantID(), coredata.EvidenceEntityType) if err != nil { return nil, fmt.Errorf("cannot create evidence global id: %w", err) } @@ -60,8 +85,8 @@ func (s Service) CreateEvidence( return nil, fmt.Errorf("cannot generate object key: %w", err) } - putObjectOutput, err := s.s3.PutObject(ctx, &s3.PutObjectInput{ - Bucket: aws.String(s.bucket), + putObjectOutput, err := s.svc.s3.PutObject(ctx, &s3.PutObjectInput{ + Bucket: aws.String(s.svc.bucket), Key: aws.String(objectKey.String()), Body: req.File, ContentType: aws.String(contentType), @@ -70,8 +95,8 @@ func (s Service) CreateEvidence( return nil, fmt.Errorf("cannot upload file to S3: %w", err) } - headOutput, err := s.s3.HeadObject(ctx, &s3.HeadObjectInput{ - Bucket: aws.String(s.bucket), + headOutput, err := s.svc.s3.HeadObject(ctx, &s3.HeadObjectInput{ + Bucket: aws.String(s.svc.bucket), Key: aws.String(objectKey.String()), }) if err != nil { @@ -93,14 +118,14 @@ func (s Service) CreateEvidence( UpdatedAt: now, } - err = s.pg.WithTx( + err = s.svc.pg.WithTx( ctx, func(conn pg.Conn) error { - if err := task.LoadByID(ctx, conn, s.scope, req.TaskID); err != nil { + if err := task.LoadByID(ctx, conn, s.svc.scope, req.TaskID); err != nil { return fmt.Errorf("cannot load task %q: %w", req.TaskID, err) } - if err := evidence.Insert(ctx, conn, s.scope); err != nil { + if err := evidence.Insert(ctx, conn, s.svc.scope); err != nil { return fmt.Errorf("cannot insert evidence: %w", err) } @@ -115,3 +140,75 @@ func (s Service) CreateEvidence( return evidence, nil } + +func (s EvidenceService) GenerateFileURL( + ctx context.Context, + evidenceID gid.GID, + expiresIn time.Duration, +) (*string, error) { + evidence, err := s.Get(ctx, evidenceID) + if err != nil { + return nil, fmt.Errorf("cannot get evidence: %w", err) + } + + presignClient := s3.NewPresignClient(s.svc.s3) + + presignedReq, err := presignClient.PresignGetObject(ctx, &s3.GetObjectInput{ + Bucket: aws.String(s.svc.bucket), + Key: aws.String(evidence.ObjectKey), + ResponseContentType: aws.String(evidence.MimeType), + ResponseContentDisposition: aws.String(fmt.Sprintf("attachment; filename=\"%s\"", evidence.Filename)), + }, func(opts *s3.PresignOptions) { + opts.Expires = expiresIn + }) + if err != nil { + return nil, fmt.Errorf("cannot presign GetObject request: %w", err) + } + + return &presignedReq.URL, nil +} + +func (s EvidenceService) ListForTaskID( + ctx context.Context, + taskID gid.GID, + cursor *page.Cursor, +) (*page.Page[*coredata.Evidence], error) { + var evidences coredata.Evidences + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return evidences.LoadByTaskID( + ctx, + conn, + s.svc.scope, + taskID, + cursor, + ) + }, + ) + + if err != nil { + return nil, err + } + + return page.NewPage(evidences, cursor), nil +} + +func (s *EvidenceService) Delete( + ctx context.Context, + evidenceID gid.GID, +) error { + evidence := &coredata.Evidence{ID: evidenceID} + + return s.svc.pg.WithTx( + ctx, + func(conn pg.Conn) error { + if err := evidence.Delete(ctx, conn, s.svc.scope); err != nil { + return fmt.Errorf("cannot delete evidence: %w", err) + } + + return nil + }, + ) +} diff --git a/pkg/probo/framework_service.go b/pkg/probo/framework_service.go new file mode 100644 index 000000000..d5c52d70d --- /dev/null +++ b/pkg/probo/framework_service.go @@ -0,0 +1,165 @@ +// 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/coredata" + "github.com/getprobo/probo/pkg/gid" + "github.com/getprobo/probo/pkg/page" + "go.gearno.de/kit/pg" +) + +type ( + FrameworkService struct { + svc *TenantService + } + + CreateFrameworkRequest struct { + OrganizationID gid.GID + Name string + Description string + ContentRef string + } + + UpdateFrameworkRequest struct { + ID gid.GID + ExpectedVersion int + Name *string + Description *string + } +) + +func (s FrameworkService) Create( + ctx context.Context, + req CreateFrameworkRequest, +) (*coredata.Framework, error) { + now := time.Now() + frameworkID, err := gid.NewGID(s.svc.scope.GetTenantID(), coredata.FrameworkEntityType) + if err != nil { + return nil, fmt.Errorf("cannot create global id: %w", err) + } + + framework := &coredata.Framework{ + ID: frameworkID, + OrganizationID: req.OrganizationID, + Name: req.Name, + Description: req.Description, + ContentRef: req.ContentRef, + CreatedAt: now, + UpdatedAt: now, + } + + err = s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return framework.Insert(ctx, conn, s.svc.scope) + }, + ) + + if err != nil { + return nil, err + } + + return framework, nil +} + +func (s FrameworkService) ListForOrganizationID( + ctx context.Context, + organizationID gid.GID, + cursor *page.Cursor, +) (*page.Page[*coredata.Framework], error) { + var frameworks coredata.Frameworks + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return frameworks.LoadByOrganizationID( + ctx, + conn, + s.svc.scope, + organizationID, + cursor, + ) + }, + ) + + if err != nil { + return nil, err + } + + return page.NewPage(frameworks, cursor), nil +} + +func (s FrameworkService) Get( + ctx context.Context, + frameworkID gid.GID, +) (*coredata.Framework, error) { + framework := &coredata.Framework{} + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return framework.LoadByID(ctx, conn, s.svc.scope, frameworkID) + }, + ) + + if err != nil { + return nil, err + } + + return framework, nil +} + +func (s FrameworkService) Update( + ctx context.Context, + req UpdateFrameworkRequest, +) (*coredata.Framework, error) { + params := coredata.UpdateFrameworkParams{ + ExpectedVersion: req.ExpectedVersion, + Name: req.Name, + Description: req.Description, + } + + framework := &coredata.Framework{ID: req.ID} + + err := s.svc.pg.WithTx( + ctx, + func(conn pg.Conn) error { + return framework.Update(ctx, conn, s.svc.scope, params) + }) + if err != nil { + return nil, err + } + + return framework, nil +} + +func (s FrameworkService) Delete( + ctx context.Context, + frameworkID gid.GID, +) error { + framework := &coredata.Framework{ID: frameworkID} + + return s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return framework.Delete(ctx, conn, s.svc.scope) + }, + ) +} diff --git a/pkg/probo/get_control.go b/pkg/probo/get_control.go deleted file mode 100644 index 21f4b1571..000000000 --- a/pkg/probo/get_control.go +++ /dev/null @@ -1,43 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -func (s Service) GetControl( - ctx context.Context, - controlID gid.GID, -) (*coredata.Control, error) { - control := &coredata.Control{} - - err := s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - return control.LoadByID(ctx, conn, s.scope, controlID) - }, - ) - - if err != nil { - return nil, err - } - - return control, nil -} diff --git a/pkg/probo/get_evidence.go b/pkg/probo/get_evidence.go deleted file mode 100644 index 4831bb527..000000000 --- a/pkg/probo/get_evidence.go +++ /dev/null @@ -1,43 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -func (s Service) GetEvidence( - ctx context.Context, - evidenceID gid.GID, -) (*coredata.Evidence, error) { - evidence := &coredata.Evidence{} - - err := s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - return evidence.LoadByID(ctx, conn, s.scope, evidenceID) - }, - ) - - if err != nil { - return nil, err - } - - return evidence, nil -} diff --git a/pkg/probo/get_evidence_file_url.go b/pkg/probo/get_evidence_file_url.go deleted file mode 100644 index 24d685327..000000000 --- a/pkg/probo/get_evidence_file_url.go +++ /dev/null @@ -1,52 +0,0 @@ -// 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/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/s3" - "github.com/getprobo/probo/pkg/gid" -) - -func (s Service) GetEvidenceFileURL( - ctx context.Context, - evidenceID gid.GID, - expiresIn time.Duration, -) (*string, error) { - evidence, err := s.GetEvidence(ctx, evidenceID) - if err != nil { - return nil, fmt.Errorf("cannot get evidence: %w", err) - } - - presignClient := s3.NewPresignClient(s.s3) - - presignedReq, err := presignClient.PresignGetObject(ctx, &s3.GetObjectInput{ - Bucket: aws.String(s.bucket), - Key: aws.String(evidence.ObjectKey), - ResponseContentType: aws.String(evidence.MimeType), - ResponseContentDisposition: aws.String(fmt.Sprintf("attachment; filename=\"%s\"", evidence.Filename)), - }, func(opts *s3.PresignOptions) { - opts.Expires = expiresIn - }) - if err != nil { - return nil, fmt.Errorf("cannot presign GetObject request: %w", err) - } - - return &presignedReq.URL, nil -} diff --git a/pkg/probo/get_framework.go b/pkg/probo/get_framework.go deleted file mode 100644 index b38f07a82..000000000 --- a/pkg/probo/get_framework.go +++ /dev/null @@ -1,43 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -func (s Service) GetFramework( - ctx context.Context, - frameworkID gid.GID, -) (*coredata.Framework, error) { - framework := &coredata.Framework{} - - err := s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - return framework.LoadByID(ctx, conn, s.scope, frameworkID) - }, - ) - - if err != nil { - return nil, err - } - - return framework, nil -} diff --git a/pkg/probo/get_organization.go b/pkg/probo/get_organization.go deleted file mode 100644 index b6c0eddc7..000000000 --- a/pkg/probo/get_organization.go +++ /dev/null @@ -1,48 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -func (s Service) GetOrganization( - ctx context.Context, - organizationID gid.GID, -) (*coredata.Organization, error) { - organization := &coredata.Organization{} - - err := s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - return organization.LoadByID( - ctx, - conn, - s.scope, - organizationID, - ) - }, - ) - - if err != nil { - return nil, err - } - - return organization, nil -} diff --git a/pkg/probo/get_people.go b/pkg/probo/get_people.go deleted file mode 100644 index a30f5b322..000000000 --- a/pkg/probo/get_people.go +++ /dev/null @@ -1,43 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -func (s Service) GetPeople( - ctx context.Context, - peopleID gid.GID, -) (*coredata.People, error) { - people := &coredata.People{} - - err := s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - return people.LoadByID(ctx, conn, s.scope, peopleID) - }, - ) - - if err != nil { - return nil, err - } - - return people, nil -} diff --git a/pkg/probo/get_task.go b/pkg/probo/get_task.go deleted file mode 100644 index 7590ef82d..000000000 --- a/pkg/probo/get_task.go +++ /dev/null @@ -1,43 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -func (s Service) GetTask( - ctx context.Context, - taskID gid.GID, -) (*coredata.Task, error) { - task := &coredata.Task{} - - err := s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - return task.LoadByID(ctx, conn, s.scope, taskID) - }, - ) - - if err != nil { - return nil, err - } - - return task, nil -} diff --git a/pkg/probo/get_vendor.go b/pkg/probo/get_vendor.go deleted file mode 100644 index 4f108f522..000000000 --- a/pkg/probo/get_vendor.go +++ /dev/null @@ -1,43 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -func (s Service) GetVendor( - ctx context.Context, - vendorID gid.GID, -) (*coredata.Vendor, error) { - vendor := &coredata.Vendor{} - - err := s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - return vendor.LoadByID(ctx, conn, s.scope, vendorID) - }, - ) - - if err != nil { - return nil, err - } - - return vendor, nil -} diff --git a/pkg/probo/list_control_tasks.go b/pkg/probo/list_control_tasks.go deleted file mode 100644 index 77324d61a..000000000 --- a/pkg/probo/list_control_tasks.go +++ /dev/null @@ -1,51 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "github.com/getprobo/probo/pkg/page" - "go.gearno.de/kit/pg" -) - -func (s Service) ListControlTasks( - ctx context.Context, - controlID gid.GID, - cursor *page.Cursor, -) (*page.Page[*coredata.Task], error) { - var tasks coredata.Tasks - - err := s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - return tasks.LoadByControlID( - ctx, - conn, - s.scope, - controlID, - cursor, - ) - }, - ) - - if err != nil { - return nil, err - } - - return page.NewPage(tasks, cursor), nil -} diff --git a/pkg/probo/list_framework_controls.go b/pkg/probo/list_framework_controls.go deleted file mode 100644 index 4339aac1f..000000000 --- a/pkg/probo/list_framework_controls.go +++ /dev/null @@ -1,51 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "github.com/getprobo/probo/pkg/page" - "go.gearno.de/kit/pg" -) - -func (s Service) ListFrameworkControls( - ctx context.Context, - frameworkID gid.GID, - cursor *page.Cursor, -) (*page.Page[*coredata.Control], error) { - var controls coredata.Controls - - err := s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - return controls.LoadByFrameworkID( - ctx, - conn, - s.scope, - frameworkID, - cursor, - ) - }, - ) - - if err != nil { - return nil, err - } - - return page.NewPage(controls, cursor), nil -} diff --git a/pkg/probo/list_organization_frameworks.go b/pkg/probo/list_organization_frameworks.go deleted file mode 100644 index fc1374ae7..000000000 --- a/pkg/probo/list_organization_frameworks.go +++ /dev/null @@ -1,51 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "github.com/getprobo/probo/pkg/page" - "go.gearno.de/kit/pg" -) - -func (s Service) ListOrganizationFrameworks( - ctx context.Context, - organizationID gid.GID, - cursor *page.Cursor, -) (*page.Page[*coredata.Framework], error) { - var frameworks coredata.Frameworks - - err := s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - return frameworks.LoadByOrganizationID( - ctx, - conn, - s.scope, - organizationID, - cursor, - ) - }, - ) - - if err != nil { - return nil, err - } - - return page.NewPage(frameworks, cursor), nil -} diff --git a/pkg/probo/list_organization_peoples.go b/pkg/probo/list_organization_peoples.go deleted file mode 100644 index 7bcf877f0..000000000 --- a/pkg/probo/list_organization_peoples.go +++ /dev/null @@ -1,51 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "github.com/getprobo/probo/pkg/page" - "go.gearno.de/kit/pg" -) - -func (s Service) ListOrganizationPeoples( - ctx context.Context, - organizationID gid.GID, - cursor *page.Cursor, -) (*page.Page[*coredata.People], error) { - var peoples coredata.Peoples - - err := s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - return peoples.LoadByOrganizationID( - ctx, - conn, - s.scope, - organizationID, - cursor, - ) - }, - ) - - if err != nil { - return nil, err - } - - return page.NewPage(peoples, cursor), nil -} diff --git a/pkg/probo/list_organization_vendors.go b/pkg/probo/list_organization_vendors.go deleted file mode 100644 index 43083c825..000000000 --- a/pkg/probo/list_organization_vendors.go +++ /dev/null @@ -1,51 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "github.com/getprobo/probo/pkg/page" - "go.gearno.de/kit/pg" -) - -func (s Service) ListOrganizationVendors( - ctx context.Context, - organizationID gid.GID, - cursor *page.Cursor, -) (*page.Page[*coredata.Vendor], error) { - var vendors coredata.Vendors - - err := s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - return vendors.LoadByOrganizationID( - ctx, - conn, - s.scope, - organizationID, - cursor, - ) - }, - ) - - if err != nil { - return nil, err - } - - return page.NewPage(vendors, cursor), nil -} diff --git a/pkg/probo/list_task_evidences.go b/pkg/probo/list_task_evidences.go deleted file mode 100644 index 222a028f6..000000000 --- a/pkg/probo/list_task_evidences.go +++ /dev/null @@ -1,51 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "github.com/getprobo/probo/pkg/page" - "go.gearno.de/kit/pg" -) - -func (s Service) ListTaskEvidences( - ctx context.Context, - taskID gid.GID, - cursor *page.Cursor, -) (*page.Page[*coredata.Evidence], error) { - var evidences coredata.Evidences - - err := s.pg.WithConn( - ctx, - func(conn pg.Conn) error { - return evidences.LoadByTaskID( - ctx, - conn, - s.scope, - taskID, - cursor, - ) - }, - ) - - if err != nil { - return nil, err - } - - return page.NewPage(evidences, cursor), nil -} diff --git a/pkg/probo/create_organization.go b/pkg/probo/organization_service.go similarity index 71% rename from pkg/probo/create_organization.go rename to pkg/probo/organization_service.go index 6d305085f..38e426aee 100644 --- a/pkg/probo/create_organization.go +++ b/pkg/probo/organization_service.go @@ -25,32 +25,34 @@ import ( ) type ( + OrganizationService struct { + svc *TenantService + } + CreateOrganizationRequest struct { Name string } ) -func (s Service) CreateOrganization( +func (s OrganizationService) Create( ctx context.Context, req CreateOrganizationRequest, ) (*coredata.Organization, error) { - tenantID := gid.NewTenantID() - now := time.Now() - organizationID, err := gid.NewGID(tenantID, coredata.OrganizationEntityType) + organizationID, err := gid.NewGID(s.svc.scope.GetTenantID(), coredata.OrganizationEntityType) if err != nil { return nil, fmt.Errorf("cannot create organization global id: %w", err) } organization := &coredata.Organization{ ID: organizationID, - TenantID: tenantID, + TenantID: s.svc.scope.GetTenantID(), Name: req.Name, CreatedAt: now, UpdatedAt: now, } - err = s.pg.WithConn( + err = s.svc.pg.WithConn( ctx, func(conn pg.Conn) error { if err := organization.Insert(ctx, conn); err != nil { @@ -67,3 +69,28 @@ func (s Service) CreateOrganization( return organization, nil } + +func (s OrganizationService) Get( + ctx context.Context, + organizationID gid.GID, +) (*coredata.Organization, error) { + organization := &coredata.Organization{} + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return organization.LoadByID( + ctx, + conn, + s.svc.scope, + organizationID, + ) + }, + ) + + if err != nil { + return nil, err + } + + return organization, nil +} diff --git a/pkg/probo/people_service.go b/pkg/probo/people_service.go new file mode 100644 index 000000000..956e8de31 --- /dev/null +++ b/pkg/probo/people_service.go @@ -0,0 +1,180 @@ +// 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/coredata" + "github.com/getprobo/probo/pkg/gid" + "github.com/getprobo/probo/pkg/page" + "go.gearno.de/kit/pg" +) + +type ( + PeopleService struct { + svc *TenantService + } + + UpdatePeopleRequest struct { + ID gid.GID + ExpectedVersion int + Kind *coredata.PeopleKind + FullName *string + PrimaryEmailAddress *string + AdditionalEmailAddresses *[]string + } + + CreatePeopleRequest struct { + OrganizationID gid.GID + FullName string + PrimaryEmailAddress string + AdditionalEmailAddresses []string + Kind coredata.PeopleKind + } +) + +func (s PeopleService) Get( + ctx context.Context, + peopleID gid.GID, +) (*coredata.People, error) { + people := &coredata.People{} + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return people.LoadByID(ctx, conn, s.svc.scope, peopleID) + }, + ) + + if err != nil { + return nil, err + } + + return people, nil +} + +func (s PeopleService) ListForOrganizationID( + ctx context.Context, + organizationID gid.GID, + cursor *page.Cursor, +) (*page.Page[*coredata.People], error) { + var peoples coredata.Peoples + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return peoples.LoadByOrganizationID( + ctx, + conn, + s.svc.scope, + organizationID, + cursor, + ) + }, + ) + + if err != nil { + return nil, err + } + + return page.NewPage(peoples, cursor), nil +} + +func (s PeopleService) Update( + ctx context.Context, + req UpdatePeopleRequest, +) (*coredata.People, error) { + params := coredata.UpdatePeopleParams{ + ExpectedVersion: req.ExpectedVersion, + Kind: req.Kind, + FullName: req.FullName, + PrimaryEmailAddress: req.PrimaryEmailAddress, + AdditionalEmailAddresses: req.AdditionalEmailAddresses, + } + + people := &coredata.People{ID: req.ID} + + err := s.svc.pg.WithTx( + ctx, + func(conn pg.Conn) error { + return people.Update(ctx, conn, s.svc.scope, params) + }) + if err != nil { + return nil, err + } + + return people, nil +} + +func (s PeopleService) Create( + ctx context.Context, + req CreatePeopleRequest, +) (*coredata.People, error) { + now := time.Now() + peopleID, err := gid.NewGID(s.svc.scope.GetTenantID(), coredata.PeopleEntityType) + if err != nil { + return nil, fmt.Errorf("cannot create people global id: %w", err) + } + + organization := &coredata.Organization{} + people := &coredata.People{ + ID: peopleID, + OrganizationID: req.OrganizationID, + Kind: req.Kind, + FullName: req.FullName, + PrimaryEmailAddress: req.PrimaryEmailAddress, + AdditionalEmailAddresses: req.AdditionalEmailAddresses, + 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 := people.Insert(ctx, conn, s.svc.scope); err != nil { + return fmt.Errorf("cannot insert people: %w", err) + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + return people, nil +} + +func (s PeopleService) Delete( + ctx context.Context, + peopleID gid.GID, +) error { + people := coredata.People{ID: peopleID} + + return s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return people.Delete(ctx, conn, s.svc.scope) + }, + ) +} diff --git a/pkg/probo/policy_service.go b/pkg/probo/policy_service.go index 0ba918f42..e3f649977 100644 --- a/pkg/probo/policy_service.go +++ b/pkg/probo/policy_service.go @@ -12,7 +12,7 @@ import ( ) type PolicyService struct { - svc *Service + svc *TenantService } type ( @@ -142,7 +142,7 @@ func (s *PolicyService) Delete( ) } -func (s *PolicyService) ListByOrganization( +func (s *PolicyService) ListByOrganizationID( ctx context.Context, organizationID gid.GID, cursor *page.Cursor, diff --git a/pkg/probo/service.go b/pkg/probo/service.go index bcf612118..f09c2f96c 100644 --- a/pkg/probo/service.go +++ b/pkg/probo/service.go @@ -29,9 +29,23 @@ type ( pg *pg.Client s3 *s3.Client bucket string - scope coredata.Scoper + } - Policies *PolicyService + TenantService struct { + pg *pg.Client + s3 *s3.Client + bucket string + + scope coredata.Scoper + + Policies *PolicyService + Controls *ControlService + Evidences *EvidenceService + Frameworks *FrameworkService + Tasks *TaskService + Peoples *PeopleService + Organizations *OrganizationService + Vendors *VendorService } ) @@ -49,23 +63,27 @@ func NewService( pg: pgClient, s3: s3Client, bucket: bucket, - scope: coredata.NewNoScope(), } - svc.Policies = &PolicyService{svc: svc} - return svc, nil } -func (s *Service) WithTenant(tenantID gid.TenantID) *Service { - newSvc := &Service{ +func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService { + tenantService := &TenantService{ pg: s.pg, s3: s.s3, bucket: s.bucket, scope: coredata.NewScope(tenantID), } - newSvc.Policies = &PolicyService{svc: newSvc} + tenantService.Policies = &PolicyService{svc: tenantService} + tenantService.Controls = &ControlService{svc: tenantService} + tenantService.Evidences = &EvidenceService{svc: tenantService} + tenantService.Frameworks = &FrameworkService{svc: tenantService} + tenantService.Tasks = &TaskService{svc: tenantService} + tenantService.Peoples = &PeopleService{svc: tenantService} + tenantService.Organizations = &OrganizationService{svc: tenantService} + tenantService.Vendors = &VendorService{svc: tenantService} - return newSvc + return tenantService } diff --git a/pkg/probo/task_service.go b/pkg/probo/task_service.go new file mode 100644 index 000000000..fd67850dd --- /dev/null +++ b/pkg/probo/task_service.go @@ -0,0 +1,176 @@ +// 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/coredata" + "github.com/getprobo/probo/pkg/gid" + "github.com/getprobo/probo/pkg/page" + "go.gearno.de/kit/pg" +) + +type ( + TaskService struct { + svc *TenantService + } + + CreateTaskRequest struct { + ControlID gid.GID + Name string + ContentRef string + Description string + } + + UpdateTaskRequest struct { + ID gid.GID + ExpectedVersion int + Name *string + Description *string + State *coredata.TaskState + } +) + +func (s TaskService) Create( + ctx context.Context, + req CreateTaskRequest, +) (*coredata.Task, error) { + now := time.Now() + taskID, err := gid.NewGID(s.svc.scope.GetTenantID(), coredata.TaskEntityType) + if err != nil { + return nil, fmt.Errorf("cannot create task global id: %w", err) + } + + control := &coredata.Control{} + task := &coredata.Task{ + ID: taskID, + ControlID: req.ControlID, + Name: req.Name, + ContentRef: req.ContentRef, + State: coredata.TaskStateTodo, + Description: req.Description, + CreatedAt: now, + UpdatedAt: now, + } + + err = s.svc.pg.WithTx( + ctx, + func(conn pg.Conn) error { + if err := control.LoadByID(ctx, conn, s.svc.scope, req.ControlID); err != nil { + return fmt.Errorf("cannot laod control %q: %w", req.ControlID, err) + } + + if err := task.Insert(ctx, conn, s.svc.scope); err != nil { + return fmt.Errorf("cannot insert task: %w", err) + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + return task, nil +} + +func (s TaskService) Update( + ctx context.Context, + req UpdateTaskRequest, +) (*coredata.Task, error) { + params := coredata.UpdateTaskParams{ + ExpectedVersion: req.ExpectedVersion, + Name: req.Name, + Description: req.Description, + State: req.State, + } + + task := &coredata.Task{ID: req.ID} + + err := s.svc.pg.WithTx( + ctx, + func(conn pg.Conn) error { + return task.Update(ctx, conn, s.svc.scope, params) + }) + if err != nil { + return nil, err + } + + return task, nil +} + +func (s TaskService) ListForControlID( + ctx context.Context, + controlID gid.GID, + cursor *page.Cursor, +) (*page.Page[*coredata.Task], error) { + var tasks coredata.Tasks + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return tasks.LoadByControlID( + ctx, + conn, + s.svc.scope, + controlID, + cursor, + ) + }, + ) + + if err != nil { + return nil, err + } + + return page.NewPage(tasks, cursor), nil +} + +func (s TaskService) Delete( + ctx context.Context, + taskID gid.GID, +) error { + task := coredata.Task{ID: taskID} + return s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return task.Delete(ctx, conn, s.svc.scope) + }, + ) +} + +func (s TaskService) Get( + ctx context.Context, + taskID gid.GID, +) (*coredata.Task, error) { + task := &coredata.Task{} + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return task.LoadByID(ctx, conn, s.svc.scope, taskID) + }, + ) + + if err != nil { + return nil, err + } + + return task, nil +} diff --git a/pkg/probo/update_control.go b/pkg/probo/update_control.go deleted file mode 100644 index 0ef73b0df..000000000 --- a/pkg/probo/update_control.go +++ /dev/null @@ -1,58 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -type UpdateControlRequest struct { - ID gid.GID - ExpectedVersion int - Name *string - Description *string - Category *string - State *coredata.ControlState -} - -func (s Service) UpdateControl( - ctx context.Context, - req UpdateControlRequest, -) (*coredata.Control, error) { - params := coredata.UpdateControlParams{ - ExpectedVersion: req.ExpectedVersion, - Name: req.Name, - Description: req.Description, - Category: req.Category, - State: req.State, - } - - control := &coredata.Control{ID: req.ID} - - err := s.pg.WithTx( - ctx, - func(conn pg.Conn) error { - return control.Update(ctx, conn, s.scope, params) - }) - if err != nil { - return nil, err - } - - return control, nil -} diff --git a/pkg/probo/update_framework.go b/pkg/probo/update_framework.go deleted file mode 100644 index a3c31772c..000000000 --- a/pkg/probo/update_framework.go +++ /dev/null @@ -1,54 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -type UpdateFrameworkRequest struct { - ID gid.GID - ExpectedVersion int - Name *string - Description *string -} - -func (s Service) UpdateFramework( - ctx context.Context, - req UpdateFrameworkRequest, -) (*coredata.Framework, error) { - params := coredata.UpdateFrameworkParams{ - ExpectedVersion: req.ExpectedVersion, - Name: req.Name, - Description: req.Description, - } - - framework := &coredata.Framework{ID: req.ID} - - err := s.pg.WithTx( - ctx, - func(conn pg.Conn) error { - return framework.Update(ctx, conn, s.scope, params) - }) - if err != nil { - return nil, err - } - - return framework, nil -} diff --git a/pkg/probo/update_people.go b/pkg/probo/update_people.go deleted file mode 100644 index 43c6a319a..000000000 --- a/pkg/probo/update_people.go +++ /dev/null @@ -1,58 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -type UpdatePeopleRequest struct { - ID gid.GID - ExpectedVersion int - Kind *coredata.PeopleKind - FullName *string - PrimaryEmailAddress *string - AdditionalEmailAddresses *[]string -} - -func (s Service) UpdatePeople( - ctx context.Context, - req UpdatePeopleRequest, -) (*coredata.People, error) { - params := coredata.UpdatePeopleParams{ - ExpectedVersion: req.ExpectedVersion, - Kind: req.Kind, - FullName: req.FullName, - PrimaryEmailAddress: req.PrimaryEmailAddress, - AdditionalEmailAddresses: req.AdditionalEmailAddresses, - } - - people := &coredata.People{ID: req.ID} - - err := s.pg.WithTx( - ctx, - func(conn pg.Conn) error { - return people.Update(ctx, conn, s.scope, params) - }) - if err != nil { - return nil, err - } - - return people, nil -} diff --git a/pkg/probo/update_task.go b/pkg/probo/update_task.go deleted file mode 100644 index 6fde0faf2..000000000 --- a/pkg/probo/update_task.go +++ /dev/null @@ -1,56 +0,0 @@ -// 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" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -type UpdateTaskRequest struct { - ID gid.GID - ExpectedVersion int - Name *string - Description *string - State *coredata.TaskState -} - -func (s Service) UpdateTask( - ctx context.Context, - req UpdateTaskRequest, -) (*coredata.Task, error) { - params := coredata.UpdateTaskParams{ - ExpectedVersion: req.ExpectedVersion, - Name: req.Name, - Description: req.Description, - State: req.State, - } - - task := &coredata.Task{ID: req.ID} - - err := s.pg.WithTx( - ctx, - func(conn pg.Conn) error { - return task.Update(ctx, conn, s.scope, params) - }) - if err != nil { - return nil, err - } - - return task, nil -} diff --git a/pkg/probo/update_vendor.go b/pkg/probo/update_vendor.go deleted file mode 100644 index ff65eb55a..000000000 --- a/pkg/probo/update_vendor.go +++ /dev/null @@ -1,69 +0,0 @@ -// 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" - "time" - - "github.com/getprobo/probo/pkg/coredata" - "github.com/getprobo/probo/pkg/gid" - "go.gearno.de/kit/pg" -) - -type UpdateVendorRequest struct { - ID gid.GID - ExpectedVersion int - Name *string - Description *string - ServiceStartAt *time.Time - ServiceTerminationAt *time.Time - ServiceCriticality *coredata.ServiceCriticality - RiskTier *coredata.RiskTier - StatusPageURL *string - TermsOfServiceURL *string - PrivacyPolicyURL *string -} - -func (s Service) UpdateVendor( - ctx context.Context, - req UpdateVendorRequest, -) (*coredata.Vendor, error) { - params := coredata.UpdateVendorParams{ - ExpectedVersion: req.ExpectedVersion, - Name: req.Name, - Description: req.Description, - ServiceStartAt: req.ServiceStartAt, - ServiceTerminationAt: req.ServiceTerminationAt, - ServiceCriticality: req.ServiceCriticality, - RiskTier: req.RiskTier, - StatusPageURL: req.StatusPageURL, - TermsOfServiceURL: req.TermsOfServiceURL, - PrivacyPolicyURL: req.PrivacyPolicyURL, - } - - vendor := &coredata.Vendor{ID: req.ID} - - err := s.pg.WithTx( - ctx, - func(conn pg.Conn) error { - return vendor.Update(ctx, conn, s.scope, params) - }) - if err != nil { - return nil, err - } - - return vendor, nil -} diff --git a/pkg/probo/vendor_service.go b/pkg/probo/vendor_service.go new file mode 100644 index 000000000..340dfff46 --- /dev/null +++ b/pkg/probo/vendor_service.go @@ -0,0 +1,199 @@ +// 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/coredata" + "github.com/getprobo/probo/pkg/gid" + "github.com/getprobo/probo/pkg/page" + "go.gearno.de/kit/pg" +) + +type ( + VendorService struct { + svc *TenantService + } + + CreateVendorRequest struct { + OrganizationID gid.GID + Name string + Description string + ServiceStartAt time.Time + ServiceTerminationAt *time.Time + ServiceCriticality coredata.ServiceCriticality + RiskTier coredata.RiskTier + StatusPageURL *string + TermsOfServiceURL *string + PrivacyPolicyURL *string + } + + UpdateVendorRequest struct { + ID gid.GID + ExpectedVersion int + Name *string + Description *string + ServiceStartAt *time.Time + ServiceTerminationAt *time.Time + ServiceCriticality *coredata.ServiceCriticality + RiskTier *coredata.RiskTier + StatusPageURL *string + TermsOfServiceURL *string + PrivacyPolicyURL *string + } +) + +func (s VendorService) ListForOrganizationID( + ctx context.Context, + organizationID gid.GID, + cursor *page.Cursor, +) (*page.Page[*coredata.Vendor], error) { + var vendors coredata.Vendors + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return vendors.LoadByOrganizationID( + ctx, + conn, + s.svc.scope, + organizationID, + cursor, + ) + }, + ) + + if err != nil { + return nil, err + } + + return page.NewPage(vendors, cursor), nil +} + +func (s VendorService) Update( + ctx context.Context, + req UpdateVendorRequest, +) (*coredata.Vendor, error) { + params := coredata.UpdateVendorParams{ + ExpectedVersion: req.ExpectedVersion, + Name: req.Name, + Description: req.Description, + ServiceStartAt: req.ServiceStartAt, + ServiceTerminationAt: req.ServiceTerminationAt, + ServiceCriticality: req.ServiceCriticality, + RiskTier: req.RiskTier, + StatusPageURL: req.StatusPageURL, + TermsOfServiceURL: req.TermsOfServiceURL, + PrivacyPolicyURL: req.PrivacyPolicyURL, + } + + vendor := &coredata.Vendor{ID: req.ID} + + err := s.svc.pg.WithTx( + ctx, + func(conn pg.Conn) error { + return vendor.Update(ctx, conn, s.svc.scope, params) + }) + if err != nil { + return nil, err + } + + return vendor, nil +} + +func (s VendorService) Get( + ctx context.Context, + vendorID gid.GID, +) (*coredata.Vendor, error) { + vendor := &coredata.Vendor{} + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return vendor.LoadByID(ctx, conn, s.svc.scope, vendorID) + }, + ) + + if err != nil { + return nil, err + } + + return vendor, nil +} + +func (s VendorService) Delete( + ctx context.Context, + vendorID gid.GID, +) error { + vendor := coredata.Vendor{ID: vendorID} + return s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return vendor.Delete(ctx, conn, s.svc.scope) + }, + ) +} + +func (s VendorService) Create( + ctx context.Context, + req CreateVendorRequest, +) (*coredata.Vendor, error) { + now := time.Now() + vendorID, err := gid.NewGID(s.svc.scope.GetTenantID(), coredata.VendorEntityType) + if err != nil { + return nil, fmt.Errorf("cannot create vendor global id: %w", err) + } + + organization := &coredata.Organization{} + vendor := &coredata.Vendor{ + ID: vendorID, + OrganizationID: req.OrganizationID, + Name: req.Name, + CreatedAt: now, + UpdatedAt: now, + Description: req.Description, + ServiceStartAt: req.ServiceStartAt, + ServiceTerminationAt: req.ServiceTerminationAt, + ServiceCriticality: req.ServiceCriticality, + RiskTier: req.RiskTier, + StatusPageURL: req.StatusPageURL, + TermsOfServiceURL: req.TermsOfServiceURL, + PrivacyPolicyURL: req.PrivacyPolicyURL, + } + + 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 := vendor.Insert(ctx, conn, s.svc.scope); err != nil { + return fmt.Errorf("cannot insert vendor: %w", err) + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + return vendor, nil +} diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go index cb3673d38..4a3c281c3 100644 --- a/pkg/server/api/console/v1/v1_resolver.go +++ b/pkg/server/api/console/v1/v1_resolver.go @@ -23,7 +23,7 @@ func (r *controlResolver) Tasks(ctx context.Context, obj *types.Control, first * svc := r.proboSvc.WithTenant(obj.ID.TenantID()) cursor := types.NewCursor(first, after, last, before) - page, err := svc.ListControlTasks(ctx, obj.ID, cursor) + page, err := svc.Tasks.ListForControlID(ctx, obj.ID, cursor) if err != nil { return nil, fmt.Errorf("cannot list control tasks: %w", err) } @@ -35,7 +35,7 @@ func (r *controlResolver) Tasks(ctx context.Context, obj *types.Control, first * func (r *evidenceResolver) FileURL(ctx context.Context, obj *types.Evidence) (string, error) { svc := r.proboSvc.WithTenant(obj.ID.TenantID()) - fileURL, err := svc.GetEvidenceFileURL(ctx, obj.ID, 15*time.Minute) + fileURL, err := svc.Evidences.GenerateFileURL(ctx, obj.ID, 15*time.Minute) if err != nil { return "", fmt.Errorf("cannot generate file URL: %w", err) } @@ -48,7 +48,7 @@ func (r *frameworkResolver) Controls(ctx context.Context, obj *types.Framework, svc := r.proboSvc.WithTenant(obj.ID.TenantID()) cursor := types.NewCursor(first, after, last, before) - page, err := svc.ListFrameworkControls(ctx, obj.ID, cursor) + page, err := svc.Controls.ListForFrameworkID(ctx, obj.ID, cursor) if err != nil { return nil, fmt.Errorf("cannot list framework controls: %w", err) } @@ -60,7 +60,7 @@ func (r *frameworkResolver) Controls(ctx context.Context, obj *types.Framework, func (r *mutationResolver) CreateVendor(ctx context.Context, input types.CreateVendorInput) (*types.CreateVendorPayload, error) { svc := r.proboSvc.WithTenant(input.OrganizationID.TenantID()) - vendor, err := svc.CreateVendor(ctx, probo.CreateVendorRequest{ + vendor, err := svc.Vendors.Create(ctx, probo.CreateVendorRequest{ OrganizationID: input.OrganizationID, Name: input.Name, Description: input.Description, @@ -84,7 +84,7 @@ func (r *mutationResolver) CreateVendor(ctx context.Context, input types.CreateV func (r *mutationResolver) UpdateVendor(ctx context.Context, input types.UpdateVendorInput) (*types.UpdateVendorPayload, error) { svc := r.proboSvc.WithTenant(input.ID.TenantID()) - vendor, err := svc.UpdateVendor(ctx, probo.UpdateVendorRequest{ + vendor, err := svc.Vendors.Update(ctx, probo.UpdateVendorRequest{ ID: input.ID, ExpectedVersion: input.ExpectedVersion, Name: input.Name, @@ -110,7 +110,7 @@ func (r *mutationResolver) UpdateVendor(ctx context.Context, input types.UpdateV func (r *mutationResolver) DeleteVendor(ctx context.Context, input types.DeleteVendorInput) (*types.DeleteVendorPayload, error) { svc := r.proboSvc.WithTenant(input.VendorID.TenantID()) - err := svc.DeleteVendor(ctx, input.VendorID) + err := svc.Vendors.Delete(ctx, input.VendorID) if err != nil { return nil, fmt.Errorf("cannot delete vendor: %w", err) } @@ -124,7 +124,7 @@ func (r *mutationResolver) DeleteVendor(ctx context.Context, input types.DeleteV func (r *mutationResolver) CreatePeople(ctx context.Context, input types.CreatePeopleInput) (*types.CreatePeoplePayload, error) { svc := r.proboSvc.WithTenant(input.OrganizationID.TenantID()) - people, err := svc.CreatePeople(ctx, probo.CreatePeopleRequest{ + people, err := svc.Peoples.Create(ctx, probo.CreatePeopleRequest{ OrganizationID: input.OrganizationID, FullName: input.FullName, PrimaryEmailAddress: input.PrimaryEmailAddress, @@ -145,7 +145,7 @@ func (r *mutationResolver) CreatePeople(ctx context.Context, input types.CreateP func (r *mutationResolver) UpdatePeople(ctx context.Context, input types.UpdatePeopleInput) (*types.UpdatePeoplePayload, error) { svc := r.proboSvc.WithTenant(input.ID.TenantID()) - people, err := svc.UpdatePeople(ctx, probo.UpdatePeopleRequest{ + people, err := svc.Peoples.Update(ctx, probo.UpdatePeopleRequest{ ID: input.ID, ExpectedVersion: input.ExpectedVersion, FullName: input.FullName, @@ -166,7 +166,7 @@ func (r *mutationResolver) UpdatePeople(ctx context.Context, input types.UpdateP func (r *mutationResolver) DeletePeople(ctx context.Context, input types.DeletePeopleInput) (*types.DeletePeoplePayload, error) { svc := r.proboSvc.WithTenant(input.PeopleID.TenantID()) - err := svc.DeletePeople(ctx, input.PeopleID) + err := svc.Peoples.Delete(ctx, input.PeopleID) if err != nil { return nil, fmt.Errorf("cannot delete people: %w", err) } @@ -178,7 +178,9 @@ func (r *mutationResolver) DeletePeople(ctx context.Context, input types.DeleteP // CreateOrganization is the resolver for the createOrganization field. func (r *mutationResolver) CreateOrganization(ctx context.Context, input types.CreateOrganizationInput) (*types.CreateOrganizationPayload, error) { - organization, err := r.proboSvc.CreateOrganization(ctx, probo.CreateOrganizationRequest{ + svc := r.proboSvc.WithTenant(gid.NewTenantID()) + + organization, err := svc.Organizations.Create(ctx, probo.CreateOrganizationRequest{ Name: input.Name, }) if err != nil { @@ -204,7 +206,7 @@ func (r *mutationResolver) DeleteOrganization(ctx context.Context, input types.D func (r *mutationResolver) CreateTask(ctx context.Context, input types.CreateTaskInput) (*types.CreateTaskPayload, error) { svc := r.proboSvc.WithTenant(input.ControlID.TenantID()) - task, err := svc.CreateTask(ctx, probo.CreateTaskRequest{ + task, err := svc.Tasks.Create(ctx, probo.CreateTaskRequest{ ControlID: input.ControlID, Name: input.Name, Description: input.Description, @@ -222,7 +224,7 @@ func (r *mutationResolver) CreateTask(ctx context.Context, input types.CreateTas func (r *mutationResolver) UpdateTask(ctx context.Context, input types.UpdateTaskInput) (*types.UpdateTaskPayload, error) { svc := r.proboSvc.WithTenant(input.TaskID.TenantID()) - task, err := svc.UpdateTask(ctx, probo.UpdateTaskRequest{ + task, err := svc.Tasks.Update(ctx, probo.UpdateTaskRequest{ ID: input.TaskID, ExpectedVersion: input.ExpectedVersion, Name: input.Name, @@ -242,7 +244,7 @@ func (r *mutationResolver) UpdateTask(ctx context.Context, input types.UpdateTas func (r *mutationResolver) DeleteTask(ctx context.Context, input types.DeleteTaskInput) (*types.DeleteTaskPayload, error) { svc := r.proboSvc.WithTenant(input.TaskID.TenantID()) - err := svc.DeleteTask(ctx, input.TaskID) + err := svc.Tasks.Delete(ctx, input.TaskID) if err != nil { return nil, fmt.Errorf("cannot delete task: %w", err) } @@ -256,7 +258,7 @@ func (r *mutationResolver) DeleteTask(ctx context.Context, input types.DeleteTas func (r *mutationResolver) CreateFramework(ctx context.Context, input types.CreateFrameworkInput) (*types.CreateFrameworkPayload, error) { svc := r.proboSvc.WithTenant(input.OrganizationID.TenantID()) - framework, err := svc.CreateFramework(ctx, probo.CreateFrameworkRequest{ + framework, err := svc.Frameworks.Create(ctx, probo.CreateFrameworkRequest{ OrganizationID: input.OrganizationID, Name: input.Name, Description: input.Description, @@ -274,7 +276,7 @@ func (r *mutationResolver) CreateFramework(ctx context.Context, input types.Crea func (r *mutationResolver) CreateControl(ctx context.Context, input types.CreateControlInput) (*types.CreateControlPayload, error) { svc := r.proboSvc.WithTenant(input.FrameworkID.TenantID()) - control, err := svc.CreateControl(ctx, probo.CreateControlRequest{ + control, err := svc.Controls.Create(ctx, probo.CreateControlRequest{ FrameworkID: input.FrameworkID, Name: input.Name, Description: input.Description, @@ -293,7 +295,7 @@ func (r *mutationResolver) CreateControl(ctx context.Context, input types.Create func (r *mutationResolver) UpdateFramework(ctx context.Context, input types.UpdateFrameworkInput) (*types.UpdateFrameworkPayload, error) { svc := r.proboSvc.WithTenant(input.ID.TenantID()) - framework, err := svc.UpdateFramework(ctx, probo.UpdateFrameworkRequest{ + framework, err := svc.Frameworks.Update(ctx, probo.UpdateFrameworkRequest{ ID: input.ID, ExpectedVersion: input.ExpectedVersion, Name: input.Name, @@ -312,7 +314,7 @@ func (r *mutationResolver) UpdateFramework(ctx context.Context, input types.Upda func (r *mutationResolver) UpdateControl(ctx context.Context, input types.UpdateControlInput) (*types.UpdateControlPayload, error) { svc := r.proboSvc.WithTenant(input.ID.TenantID()) - control, err := svc.UpdateControl(ctx, probo.UpdateControlRequest{ + control, err := svc.Controls.Update(ctx, probo.UpdateControlRequest{ ID: input.ID, ExpectedVersion: input.ExpectedVersion, Name: input.Name, @@ -339,7 +341,7 @@ func (r *mutationResolver) UploadEvidence(ctx context.Context, input types.Uploa File: input.File.File, } - evidence, err := svc.CreateEvidence(ctx, req) + evidence, err := svc.Evidences.Create(ctx, req) if err != nil { return nil, fmt.Errorf("failed to create evidence: %w", err) } @@ -353,7 +355,7 @@ func (r *mutationResolver) UploadEvidence(ctx context.Context, input types.Uploa func (r *mutationResolver) DeleteEvidence(ctx context.Context, input types.DeleteEvidenceInput) (*types.DeleteEvidencePayload, error) { svc := r.proboSvc.WithTenant(input.EvidenceID.TenantID()) - err := svc.DeleteEvidence(ctx, input.EvidenceID) + err := svc.Evidences.Delete(ctx, input.EvidenceID) if err != nil { return nil, fmt.Errorf("failed to delete evidence: %w", err) } @@ -426,7 +428,7 @@ func (r *organizationResolver) Frameworks(ctx context.Context, obj *types.Organi cursor := types.NewCursor(first, after, last, before) - page, err := svc.ListOrganizationFrameworks(ctx, obj.ID, cursor) + page, err := svc.Frameworks.ListForOrganizationID(ctx, obj.ID, cursor) if err != nil { return nil, fmt.Errorf("cannot list organization frameworks: %w", err) } @@ -440,7 +442,7 @@ func (r *organizationResolver) Vendors(ctx context.Context, obj *types.Organizat cursor := types.NewCursor(first, after, last, before) - page, err := svc.ListOrganizationVendors(ctx, obj.ID, cursor) + page, err := svc.Vendors.ListForOrganizationID(ctx, obj.ID, cursor) if err != nil { return nil, fmt.Errorf("cannot list organization vendors: %w", err) } @@ -454,7 +456,7 @@ func (r *organizationResolver) Peoples(ctx context.Context, obj *types.Organizat cursor := types.NewCursor(first, after, last, before) - page, err := svc.ListOrganizationPeoples(ctx, obj.ID, cursor) + page, err := svc.Peoples.ListForOrganizationID(ctx, obj.ID, cursor) if err != nil { return nil, fmt.Errorf("cannot list organization peoples: %w", err) } @@ -467,7 +469,7 @@ func (r *organizationResolver) Policies(ctx context.Context, obj *types.Organiza svc := r.proboSvc.WithTenant(obj.ID.TenantID()) cursor := types.NewCursor(first, after, last, before) - page, err := svc.Policies.ListByOrganization(ctx, obj.ID, cursor) + page, err := svc.Policies.ListByOrganizationID(ctx, obj.ID, cursor) if err != nil { return nil, fmt.Errorf("cannot list organization policies: %w", err) } @@ -485,7 +487,7 @@ func (r *policyResolver) Owner(ctx context.Context, obj *types.Policy) (*types.P } // Get the owner - owner, err := svc.GetPeople(ctx, policy.OwnerID) + owner, err := svc.Peoples.Get(ctx, policy.OwnerID) if err != nil { return nil, fmt.Errorf("cannot get owner: %w", err) } @@ -499,49 +501,49 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error switch id.EntityType() { case coredata.OrganizationEntityType: - organization, err := svc.GetOrganization(ctx, id) + organization, err := svc.Organizations.Get(ctx, id) if err != nil { return nil, err } return types.NewOrganization(organization), nil case coredata.PeopleEntityType: - people, err := svc.GetPeople(ctx, id) + people, err := svc.Peoples.Get(ctx, id) if err != nil { return nil, err } return types.NewPeople(people), nil case coredata.VendorEntityType: - vendor, err := svc.GetVendor(ctx, id) + vendor, err := svc.Vendors.Get(ctx, id) if err != nil { return nil, err } return types.NewVendor(vendor), nil case coredata.FrameworkEntityType: - framework, err := svc.GetFramework(ctx, id) + framework, err := svc.Frameworks.Get(ctx, id) if err != nil { return nil, err } return types.NewFramework(framework), nil case coredata.ControlEntityType: - control, err := svc.GetControl(ctx, id) + control, err := svc.Controls.Get(ctx, id) if err != nil { return nil, err } return types.NewControl(control), nil case coredata.TaskEntityType: - task, err := svc.GetTask(ctx, id) + task, err := svc.Tasks.Get(ctx, id) if err != nil { return nil, err } return types.NewTask(task), nil case coredata.EvidenceEntityType: - evidence, err := svc.GetEvidence(ctx, id) + evidence, err := svc.Evidences.Get(ctx, id) if err != nil { return nil, err } @@ -570,9 +572,9 @@ func (r *taskResolver) Evidences(ctx context.Context, obj *types.Task, first *in svc := r.proboSvc.WithTenant(obj.ID.TenantID()) cursor := types.NewCursor(first, after, last, before) - page, err := svc.ListTaskEvidences(ctx, obj.ID, cursor) + page, err := svc.Evidences.ListForTaskID(ctx, obj.ID, cursor) if err != nil { - return nil, fmt.Errorf("cannot list organization frameworks: %w", err) + return nil, fmt.Errorf("cannot list task evidences: %w", err) } return types.NewEvidenceConnection(page), nil @@ -597,7 +599,9 @@ func (r *userResolver) Organizations(ctx context.Context, obj *types.User, first // Get the organization details for each organization ID var edges []*types.OrganizationEdge for _, organizationID := range organizationIDs { - organization, err := r.proboSvc.GetOrganization(ctx, organizationID) + svc := r.proboSvc.WithTenant(organizationID.TenantID()) + + organization, err := svc.Organizations.Get(ctx, organizationID) if err != nil { return nil, fmt.Errorf("failed to get organization details: %w", err) }