790 lines
25 KiB
Go
790 lines
25 KiB
Go
package console_v1
|
|
|
|
// This file will be automatically regenerated based on the schema, any resolver
|
|
// implementations
|
|
// will be copied through when generating and any unknown code will be moved to the end.
|
|
// Code generated by github.com/99designs/gqlgen version v0.17.90
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/vikstrous/dataloadgen"
|
|
"go.gearno.de/kit/log"
|
|
"go.probo.inc/probo/pkg/coredata"
|
|
"go.probo.inc/probo/pkg/gid"
|
|
"go.probo.inc/probo/pkg/iam"
|
|
"go.probo.inc/probo/pkg/page"
|
|
"go.probo.inc/probo/pkg/probo"
|
|
"go.probo.inc/probo/pkg/server/api/console/v1/dataloader"
|
|
"go.probo.inc/probo/pkg/server/api/console/v1/schema"
|
|
"go.probo.inc/probo/pkg/server/api/console/v1/types"
|
|
"go.probo.inc/probo/pkg/server/gqlutils"
|
|
"go.probo.inc/probo/pkg/validator"
|
|
)
|
|
|
|
// Organization is the resolver for the organization field.
|
|
func (r *auditResolver) Organization(ctx context.Context, obj *types.Audit) (*types.Organization, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
loaders := dataloader.FromContext(ctx)
|
|
|
|
organization, err := loaders.Organization.Load(ctx, obj.Organization.ID)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
|
|
return nil, gqlutils.NotFound(ctx, err)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot load organization", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewOrganization(organization), nil
|
|
}
|
|
|
|
// Framework is the resolver for the framework field.
|
|
func (r *auditResolver) Framework(ctx context.Context, obj *types.Audit) (*types.Framework, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionFrameworkGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
loaders := dataloader.FromContext(ctx)
|
|
|
|
framework, err := loaders.Framework.Load(ctx, obj.Framework.ID)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
|
|
return nil, gqlutils.NotFound(ctx, err)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot load framework", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewFramework(framework), nil
|
|
}
|
|
|
|
// Report is the resolver for the report field.
|
|
func (r *auditResolver) Report(ctx context.Context, obj *types.Audit) (*types.Report, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionReportGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if obj.Report == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
loaders := dataloader.FromContext(ctx)
|
|
|
|
report, err := loaders.Report.Load(ctx, obj.Report.ID)
|
|
if err != nil {
|
|
if errors.Is(err, dataloadgen.ErrNotFound) {
|
|
return nil, gqlutils.NotFound(ctx, err)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot load report", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewReport(report), nil
|
|
}
|
|
|
|
// ReportURL is the resolver for the reportUrl field.
|
|
func (r *auditResolver) ReportURL(ctx context.Context, obj *types.Audit) (*string, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionReportGetReportUrl); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if obj.Report == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
url, err := prb.Audits.GenerateReportURL(ctx, obj.ID, 15*time.Minute)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot generate report URL", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return url, nil
|
|
}
|
|
|
|
// Controls is the resolver for the controls field.
|
|
func (r *auditResolver) Controls(ctx context.Context, obj *types.Audit, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionControlList); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
pageOrderBy := page.OrderBy[coredata.ControlOrderField]{
|
|
Field: coredata.ControlOrderFieldCreatedAt,
|
|
Direction: page.OrderDirectionDesc,
|
|
}
|
|
if orderBy != nil {
|
|
pageOrderBy = page.OrderBy[coredata.ControlOrderField]{
|
|
Field: orderBy.Field,
|
|
Direction: orderBy.Direction,
|
|
}
|
|
}
|
|
|
|
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
|
|
|
var controlFilter = coredata.NewControlFilter(nil)
|
|
if filter != nil {
|
|
controlFilter = coredata.NewControlFilter(filter.Query)
|
|
}
|
|
|
|
page, err := prb.Controls.ListForAuditID(ctx, obj.ID, cursor, controlFilter)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot list audit controls", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewControlConnection(page, r, obj.ID, controlFilter), nil
|
|
}
|
|
|
|
// Findings is the resolver for the findings field.
|
|
func (r *auditResolver) Findings(ctx context.Context, obj *types.Audit, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.FindingOrder, filter *types.FindingFilter) (*types.FindingConnection, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionFindingList); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
pageOrderBy := page.OrderBy[coredata.FindingOrderField]{
|
|
Field: coredata.FindingOrderFieldCreatedAt,
|
|
Direction: page.OrderDirectionDesc,
|
|
}
|
|
if orderBy != nil {
|
|
pageOrderBy = page.OrderBy[coredata.FindingOrderField]{
|
|
Field: orderBy.Field,
|
|
Direction: orderBy.Direction,
|
|
}
|
|
}
|
|
|
|
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
|
|
|
var (
|
|
kind *coredata.FindingKind
|
|
status *coredata.FindingStatus
|
|
priority *coredata.FindingPriority
|
|
ownerID *gid.GID
|
|
)
|
|
if filter != nil {
|
|
kind = filter.Kind
|
|
status = filter.Status
|
|
priority = filter.Priority
|
|
ownerID = filter.OwnerID
|
|
}
|
|
|
|
findingFilter := coredata.NewFindingFilter(kind, status, priority, ownerID)
|
|
|
|
p, err := prb.Findings.ListForAuditID(ctx, obj.ID, cursor, findingFilter)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot list audit findings", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewFindingConnection(p, r, obj.ID, filter), nil
|
|
}
|
|
|
|
// Permission is the resolver for the permission field.
|
|
func (r *auditResolver) Permission(ctx context.Context, obj *types.Audit, action string) (bool, error) {
|
|
return r.Resolver.Permission(ctx, obj, action)
|
|
}
|
|
|
|
// TotalCount is the resolver for the totalCount field.
|
|
func (r *auditConnectionResolver) TotalCount(ctx context.Context, obj *types.AuditConnection) (int, error) {
|
|
if err := r.authorize(ctx, obj.ParentID, probo.ActionAuditList); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ParentID.TenantID())
|
|
|
|
switch obj.Resolver.(type) {
|
|
case *organizationResolver:
|
|
count, err := prb.Audits.CountForOrganizationID(ctx, obj.ParentID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot count audits", log.Error(err))
|
|
return 0, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return count, nil
|
|
case *findingResolver:
|
|
count, err := prb.Audits.CountForFindingID(ctx, obj.ParentID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot count audits", log.Error(err))
|
|
return 0, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return count, nil
|
|
case *controlResolver:
|
|
count, err := prb.Audits.CountForControlID(ctx, obj.ParentID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot count audits", log.Error(err))
|
|
return 0, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return count, nil
|
|
default:
|
|
r.logger.ErrorCtx(ctx, "unsupported resolver", log.Any("resolver", obj.Resolver))
|
|
return 0, gqlutils.Internal(ctx)
|
|
}
|
|
}
|
|
|
|
// Organization is the resolver for the organization field.
|
|
func (r *findingResolver) Organization(ctx context.Context, obj *types.Finding) (*types.Organization, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
loaders := dataloader.FromContext(ctx)
|
|
|
|
organization, err := loaders.Organization.Load(ctx, obj.Organization.ID)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
|
|
return nil, gqlutils.NotFound(ctx, err)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get finding organization", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewOrganization(organization), nil
|
|
}
|
|
|
|
// Audits is the resolver for the audits field.
|
|
func (r *findingResolver) Audits(ctx context.Context, obj *types.Finding, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.AuditOrderBy) (*types.AuditConnection, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionAuditList); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
pageOrderBy := page.OrderBy[coredata.AuditOrderField]{
|
|
Field: coredata.AuditOrderFieldCreatedAt,
|
|
Direction: page.OrderDirectionDesc,
|
|
}
|
|
if orderBy != nil {
|
|
pageOrderBy = page.OrderBy[coredata.AuditOrderField]{
|
|
Field: orderBy.Field,
|
|
Direction: orderBy.Direction,
|
|
}
|
|
}
|
|
|
|
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
|
|
|
p, err := prb.Audits.ListForFindingID(ctx, obj.ID, cursor)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot list finding audits", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewAuditConnection(p, r, obj.ID), nil
|
|
}
|
|
|
|
// Owner is the resolver for the owner field.
|
|
func (r *findingResolver) Owner(ctx context.Context, obj *types.Finding) (*types.Profile, error) {
|
|
if obj.Owner == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
loaders := dataloader.FromContext(ctx)
|
|
|
|
owner, err := loaders.Profile.Load(ctx, obj.Owner.ID)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
|
|
return nil, gqlutils.NotFound(ctx, err)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get finding owner", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewProfile(owner), nil
|
|
}
|
|
|
|
// Risk is the resolver for the risk field.
|
|
func (r *findingResolver) Risk(ctx context.Context, obj *types.Finding) (*types.Risk, error) {
|
|
if obj.Risk == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionRiskGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
loaders := dataloader.FromContext(ctx)
|
|
|
|
risk, err := loaders.Risk.Load(ctx, obj.Risk.ID)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
|
|
return nil, gqlutils.NotFound(ctx, err)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get finding risk", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewRisk(risk), nil
|
|
}
|
|
|
|
// Permission is the resolver for the permission field.
|
|
func (r *findingResolver) Permission(ctx context.Context, obj *types.Finding, action string) (bool, error) {
|
|
return r.Resolver.Permission(ctx, obj, action)
|
|
}
|
|
|
|
// TotalCount is the resolver for the totalCount field.
|
|
func (r *findingConnectionResolver) TotalCount(ctx context.Context, obj *types.FindingConnection) (int, error) {
|
|
if err := r.authorize(ctx, obj.ParentID, probo.ActionFindingList); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ParentID.TenantID())
|
|
|
|
var (
|
|
kind *coredata.FindingKind
|
|
status *coredata.FindingStatus
|
|
priority *coredata.FindingPriority
|
|
ownerID *gid.GID
|
|
)
|
|
if obj.Filter != nil {
|
|
kind = obj.Filter.Kind
|
|
status = obj.Filter.Status
|
|
priority = obj.Filter.Priority
|
|
ownerID = obj.Filter.OwnerID
|
|
}
|
|
|
|
findingFilter := coredata.NewFindingFilter(kind, status, priority, ownerID)
|
|
|
|
switch obj.Resolver.(type) {
|
|
case *organizationResolver:
|
|
count, err := prb.Findings.CountForOrganizationID(ctx, obj.ParentID, findingFilter)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot count findings", log.Error(err))
|
|
return 0, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return count, nil
|
|
case *auditResolver:
|
|
count, err := prb.Findings.CountForAuditID(ctx, obj.ParentID, findingFilter)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot count findings", log.Error(err))
|
|
return 0, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return count, nil
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "unsupported resolver", log.Any("resolver", obj.Resolver))
|
|
|
|
return 0, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
// CreateAudit is the resolver for the createAudit field.
|
|
func (r *mutationResolver) CreateAudit(ctx context.Context, input types.CreateAuditInput) (*types.CreateAuditPayload, error) {
|
|
if err := r.authorize(ctx, input.OrganizationID, probo.ActionAuditCreate); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
|
|
|
|
req := probo.CreateAuditRequest{
|
|
OrganizationID: input.OrganizationID,
|
|
FrameworkID: input.FrameworkID,
|
|
Name: input.Name,
|
|
ValidFrom: input.ValidFrom,
|
|
ValidUntil: input.ValidUntil,
|
|
State: input.State,
|
|
TrustCenterVisibility: input.TrustCenterVisibility,
|
|
}
|
|
|
|
audit, err := prb.Audits.Create(ctx, &req)
|
|
if err != nil {
|
|
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
|
|
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot create audit", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
if input.File != nil {
|
|
uploadReq := probo.UploadAuditReportRequest{
|
|
AuditID: audit.ID,
|
|
File: probo.File{
|
|
Content: input.File.File,
|
|
Filename: input.File.Filename,
|
|
Size: input.File.Size,
|
|
ContentType: input.File.ContentType,
|
|
},
|
|
}
|
|
|
|
audit, err = prb.Audits.UploadReport(ctx, uploadReq)
|
|
if err != nil {
|
|
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
|
|
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot upload audit report", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
}
|
|
|
|
return &types.CreateAuditPayload{
|
|
AuditEdge: types.NewAuditEdge(audit, coredata.AuditOrderFieldCreatedAt),
|
|
}, nil
|
|
}
|
|
|
|
// UpdateAudit is the resolver for the updateAudit field.
|
|
func (r *mutationResolver) UpdateAudit(ctx context.Context, input types.UpdateAuditInput) (*types.UpdateAuditPayload, error) {
|
|
if err := r.authorize(ctx, input.ID, probo.ActionAuditUpdate); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.ID.TenantID())
|
|
|
|
req := probo.UpdateAuditRequest{
|
|
ID: input.ID,
|
|
Name: gqlutils.UnwrapOmittable(input.Name),
|
|
ValidFrom: input.ValidFrom,
|
|
ValidUntil: input.ValidUntil,
|
|
State: input.State,
|
|
TrustCenterVisibility: input.TrustCenterVisibility,
|
|
}
|
|
|
|
audit, err := prb.Audits.Update(ctx, &req)
|
|
if err != nil {
|
|
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
|
|
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot update audit", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.UpdateAuditPayload{
|
|
Audit: types.NewAudit(audit),
|
|
}, nil
|
|
}
|
|
|
|
// DeleteAudit is the resolver for the deleteAudit field.
|
|
func (r *mutationResolver) DeleteAudit(ctx context.Context, input types.DeleteAuditInput) (*types.DeleteAuditPayload, error) {
|
|
if err := r.authorize(ctx, input.AuditID, probo.ActionAuditDelete); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.AuditID.TenantID())
|
|
|
|
err := prb.Audits.Delete(ctx, input.AuditID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot delete audit", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.DeleteAuditPayload{
|
|
DeletedAuditID: &input.AuditID,
|
|
}, nil
|
|
}
|
|
|
|
// UploadAuditReport is the resolver for the uploadAuditReport field.
|
|
func (r *mutationResolver) UploadAuditReport(ctx context.Context, input types.UploadAuditReportInput) (*types.UploadAuditReportPayload, error) {
|
|
if err := r.authorize(ctx, input.AuditID, probo.ActionAuditReportUpload); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.AuditID.TenantID())
|
|
|
|
req := probo.UploadAuditReportRequest{
|
|
AuditID: input.AuditID,
|
|
File: probo.File{
|
|
Content: input.File.File,
|
|
Filename: input.File.Filename,
|
|
Size: input.File.Size,
|
|
ContentType: input.File.ContentType,
|
|
},
|
|
}
|
|
|
|
audit, err := prb.Audits.UploadReport(ctx, req)
|
|
if err != nil {
|
|
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
|
|
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot upload audit report", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.UploadAuditReportPayload{
|
|
Audit: types.NewAudit(audit),
|
|
}, nil
|
|
}
|
|
|
|
// DeleteAuditReport is the resolver for the deleteAuditReport field.
|
|
func (r *mutationResolver) DeleteAuditReport(ctx context.Context, input types.DeleteAuditReportInput) (*types.DeleteAuditReportPayload, error) {
|
|
if err := r.authorize(ctx, input.AuditID, probo.ActionAuditReportDelete); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.AuditID.TenantID())
|
|
|
|
audit, err := prb.Audits.DeleteReport(ctx, input.AuditID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot delete audit report", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.DeleteAuditReportPayload{
|
|
Audit: types.NewAudit(audit),
|
|
}, nil
|
|
}
|
|
|
|
// CreateFinding is the resolver for the createFinding field.
|
|
func (r *mutationResolver) CreateFinding(ctx context.Context, input types.CreateFindingInput) (*types.CreateFindingPayload, error) {
|
|
if err := r.authorize(ctx, input.OrganizationID, probo.ActionFindingCreate); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
|
|
|
|
req := probo.CreateFindingRequest{
|
|
OrganizationID: input.OrganizationID,
|
|
Kind: input.Kind,
|
|
Description: input.Description,
|
|
Source: input.Source,
|
|
IdentifiedOn: input.IdentifiedOn,
|
|
RootCause: input.RootCause,
|
|
CorrectiveAction: input.CorrectiveAction,
|
|
OwnerID: input.OwnerID,
|
|
DueDate: input.DueDate,
|
|
Status: &input.Status,
|
|
Priority: &input.Priority,
|
|
RiskID: input.RiskID,
|
|
EffectivenessCheck: input.EffectivenessCheck,
|
|
}
|
|
|
|
finding, err := prb.Findings.Create(ctx, &req)
|
|
if err != nil {
|
|
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
|
|
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot create finding", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.CreateFindingPayload{
|
|
FindingEdge: types.NewFindingEdge(finding, coredata.FindingOrderFieldCreatedAt),
|
|
}, nil
|
|
}
|
|
|
|
// UpdateFinding is the resolver for the updateFinding field.
|
|
func (r *mutationResolver) UpdateFinding(ctx context.Context, input types.UpdateFindingInput) (*types.UpdateFindingPayload, error) {
|
|
if err := r.authorize(ctx, input.ID, probo.ActionFindingUpdate); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.ID.TenantID())
|
|
|
|
req := probo.UpdateFindingRequest{
|
|
ID: input.ID,
|
|
Description: gqlutils.UnwrapOmittable(input.Description),
|
|
Source: gqlutils.UnwrapOmittable(input.Source),
|
|
IdentifiedOn: gqlutils.UnwrapOmittable(input.IdentifiedOn),
|
|
RootCause: gqlutils.UnwrapOmittable(input.RootCause),
|
|
CorrectiveAction: gqlutils.UnwrapOmittable(input.CorrectiveAction),
|
|
OwnerID: input.OwnerID,
|
|
DueDate: gqlutils.UnwrapOmittable(input.DueDate),
|
|
Status: input.Status,
|
|
Priority: input.Priority,
|
|
RiskID: gqlutils.UnwrapOmittable(input.RiskID),
|
|
EffectivenessCheck: gqlutils.UnwrapOmittable(input.EffectivenessCheck),
|
|
}
|
|
|
|
finding, err := prb.Findings.Update(ctx, &req)
|
|
if err != nil {
|
|
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
|
|
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot update finding", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.UpdateFindingPayload{
|
|
Finding: types.NewFinding(finding),
|
|
}, nil
|
|
}
|
|
|
|
// DeleteFinding is the resolver for the deleteFinding field.
|
|
func (r *mutationResolver) DeleteFinding(ctx context.Context, input types.DeleteFindingInput) (*types.DeleteFindingPayload, error) {
|
|
if err := r.authorize(ctx, input.FindingID, probo.ActionFindingDelete); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.FindingID.TenantID())
|
|
|
|
err := prb.Findings.Delete(ctx, input.FindingID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot delete finding", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.DeleteFindingPayload{
|
|
DeletedFindingID: &input.FindingID,
|
|
}, nil
|
|
}
|
|
|
|
// CreateFindingAuditMapping is the resolver for the createFindingAuditMapping field.
|
|
func (r *mutationResolver) CreateFindingAuditMapping(ctx context.Context, input types.CreateFindingAuditMappingInput) (*types.CreateFindingAuditMappingPayload, error) {
|
|
if err := r.authorize(ctx, input.FindingID, probo.ActionFindingAuditMappingCreate); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.FindingID.TenantID())
|
|
|
|
finding, audit, err := prb.Findings.CreateAuditMapping(ctx, input.FindingID, input.AuditID, input.ReferenceID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot create finding audit mapping", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.CreateFindingAuditMappingPayload{
|
|
FindingEdge: types.NewFindingEdge(finding, coredata.FindingOrderFieldCreatedAt),
|
|
AuditEdge: types.NewAuditEdge(audit, coredata.AuditOrderFieldCreatedAt),
|
|
}, nil
|
|
}
|
|
|
|
// DeleteFindingAuditMapping is the resolver for the deleteFindingAuditMapping field.
|
|
func (r *mutationResolver) DeleteFindingAuditMapping(ctx context.Context, input types.DeleteFindingAuditMappingInput) (*types.DeleteFindingAuditMappingPayload, error) {
|
|
if err := r.authorize(ctx, input.FindingID, probo.ActionFindingAuditMappingDelete); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.FindingID.TenantID())
|
|
|
|
finding, audit, err := prb.Findings.DeleteAuditMapping(ctx, input.FindingID, input.AuditID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot delete finding audit mapping", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.DeleteFindingAuditMappingPayload{
|
|
DeletedFindingID: &finding.ID,
|
|
DeletedAuditID: &audit.ID,
|
|
}, nil
|
|
}
|
|
|
|
// PublishFindingList is the resolver for the publishFindingList field.
|
|
func (r *mutationResolver) PublishFindingList(ctx context.Context, input types.PublishFindingListInput) (*types.PublishFindingListPayload, error) {
|
|
if err := r.authorize(ctx, input.OrganizationID, probo.ActionFindingPublish); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
|
|
|
|
document, documentVersion, err := prb.GeneratedDocuments.PublishFindingList(ctx, input.OrganizationID, input.ApproverIds, input.Minor)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceAlreadyExists) {
|
|
return nil, gqlutils.Conflict(ctx, err)
|
|
}
|
|
|
|
if errMinor, ok := errors.AsType[*probo.ErrCannotPublishMinorWithoutMajor](err); ok {
|
|
return nil, gqlutils.Invalid(ctx, errMinor)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot publish finding list", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.PublishFindingListPayload{
|
|
DocumentEdge: types.NewDocumentEdge(document, coredata.DocumentOrderFieldCreatedAt),
|
|
DocumentVersionEdge: types.NewDocumentVersionEdge(documentVersion, coredata.DocumentVersionOrderFieldCreatedAt),
|
|
}, nil
|
|
}
|
|
|
|
// DownloadURL is the resolver for the downloadUrl field.
|
|
func (r *reportResolver) DownloadURL(ctx context.Context, obj *types.Report) (*string, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionReportDownloadUrlGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
url, err := prb.Reports.GenerateDownloadURL(ctx, obj.ID, 15*time.Minute)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot generate download URL", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return url, nil
|
|
}
|
|
|
|
// Audit is the resolver for the audit field.
|
|
func (r *reportResolver) Audit(ctx context.Context, obj *types.Report) (*types.Audit, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionAuditGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
audit, err := prb.Audits.GetByReportID(ctx, obj.ID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot load audit for report", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewAudit(audit), nil
|
|
}
|
|
|
|
// Permission is the resolver for the permission field.
|
|
func (r *reportResolver) Permission(ctx context.Context, obj *types.Report, action string) (bool, error) {
|
|
return r.Resolver.Permission(ctx, obj, action)
|
|
}
|
|
|
|
// Audit returns schema.AuditResolver implementation.
|
|
func (r *Resolver) Audit() schema.AuditResolver { return &auditResolver{r} }
|
|
|
|
// AuditConnection returns schema.AuditConnectionResolver implementation.
|
|
func (r *Resolver) AuditConnection() schema.AuditConnectionResolver {
|
|
return &auditConnectionResolver{r}
|
|
}
|
|
|
|
// Finding returns schema.FindingResolver implementation.
|
|
func (r *Resolver) Finding() schema.FindingResolver { return &findingResolver{r} }
|
|
|
|
// FindingConnection returns schema.FindingConnectionResolver implementation.
|
|
func (r *Resolver) FindingConnection() schema.FindingConnectionResolver {
|
|
return &findingConnectionResolver{r}
|
|
}
|
|
|
|
// Report returns schema.ReportResolver implementation.
|
|
func (r *Resolver) Report() schema.ReportResolver { return &reportResolver{r} }
|
|
|
|
type auditResolver struct{ *Resolver }
|
|
type auditConnectionResolver struct{ *Resolver }
|
|
type findingResolver struct{ *Resolver }
|
|
type findingConnectionResolver struct{ *Resolver }
|
|
type reportResolver struct{ *Resolver }
|