Files
probo/pkg/server/api/console/v1/audit_resolvers.go
Bryan Frimin 92b1264603 Fix alias resolver, field blur, and sitemap URLs
The audit alias resolver returned raw service errors. Log them
and return gqlutils.Internal like other resolvers in the file.

Remove-only users could edit the alias field to a new value that
was never saved. Reset local state when set permission is missing,
and catch mutation rejections on blur.

Sitemap generation appended audit report file IDs without
deduplication, which could emit duplicate document URLs when
multiple audits share the same report file.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-22 11:38:51 +02:00

722 lines
23 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"
"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
}
// ReportFile is the resolver for the reportFile field.
func (r *auditResolver) ReportFile(ctx context.Context, obj *types.Audit) (*types.File, error) {
if _, err := r.authorize(ctx, obj.ID, probo.ActionReportGet); err != nil {
return nil, err
}
if obj.ReportFile == nil {
return nil, nil
}
loaders := dataloader.FromContext(ctx)
file, err := loaders.File.Load(ctx, obj.ReportFile.ID)
if err != nil {
if errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot load report file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewFile(file, r.fileManager), 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) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionControlList)
if err != nil {
return nil, err
}
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 := r.probo.Controls.ListForAuditID(ctx, scope, 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) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionFindingList)
if err != nil {
return nil, err
}
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 := r.probo.Findings.ListForAuditID(ctx, scope, 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
}
// Alias is the resolver for the alias field.
func (r *auditResolver) Alias(ctx context.Context, obj *types.Audit) (*string, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionAuditGet)
if err != nil {
return nil, err
}
alias, err := r.probo.TrustCenterAliases.GetByResourceID(ctx, scope, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get audit alias", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return alias, 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) {
scope, err := r.authorize(ctx, obj.ParentID, probo.ActionAuditList)
if err != nil {
return 0, err
}
switch obj.Resolver.(type) {
case *organizationResolver:
count, err := r.probo.Audits.CountForOrganizationID(ctx, scope, 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 := r.probo.Audits.CountForFindingID(ctx, scope, 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 := r.probo.Audits.CountForControlID(ctx, scope, 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) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionAuditList)
if err != nil {
return nil, err
}
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 := r.probo.Audits.ListForFindingID(ctx, scope, 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) {
scope, err := r.authorize(ctx, obj.ParentID, probo.ActionFindingList)
if err != nil {
return 0, err
}
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 := r.probo.Findings.CountForOrganizationID(ctx, scope, 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 := r.probo.Findings.CountForAuditID(ctx, scope, 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) {
scope, err := r.authorize(ctx, input.OrganizationID, probo.ActionAuditCreate)
if err != nil {
return nil, err
}
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 := r.probo.Audits.Create(ctx, scope, &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 = r.probo.Audits.UploadReport(ctx, scope, &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) {
scope, err := r.authorize(ctx, input.ID, probo.ActionAuditUpdate)
if err != nil {
return nil, err
}
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 := r.probo.Audits.Update(ctx, scope, &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) {
scope, err := r.authorize(ctx, input.AuditID, probo.ActionAuditDelete)
if err != nil {
return nil, err
}
if err := r.probo.Audits.Delete(ctx, scope, input.AuditID); 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) {
scope, err := r.authorize(ctx, input.AuditID, probo.ActionAuditReportUpload)
if err != nil {
return nil, err
}
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 := r.probo.Audits.UploadReport(ctx, scope, &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) {
scope, err := r.authorize(ctx, input.AuditID, probo.ActionAuditReportDelete)
if err != nil {
return nil, err
}
audit, err := r.probo.Audits.DeleteReport(ctx, scope, 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) {
scope, err := r.authorize(ctx, input.OrganizationID, probo.ActionFindingCreate)
if err != nil {
return nil, err
}
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 := r.probo.Findings.Create(ctx, scope, &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) {
scope, err := r.authorize(ctx, input.ID, probo.ActionFindingUpdate)
if err != nil {
return nil, err
}
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 := r.probo.Findings.Update(ctx, scope, &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) {
scope, err := r.authorize(ctx, input.FindingID, probo.ActionFindingDelete)
if err != nil {
return nil, err
}
if err := r.probo.Findings.Delete(ctx, scope, input.FindingID); 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) {
scope, err := r.authorize(ctx, input.FindingID, probo.ActionFindingAuditMappingCreate)
if err != nil {
return nil, err
}
finding, audit, err := r.probo.Findings.CreateAuditMapping(ctx, scope, 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) {
scope, err := r.authorize(ctx, input.FindingID, probo.ActionFindingAuditMappingDelete)
if err != nil {
return nil, err
}
finding, audit, err := r.probo.Findings.DeleteAuditMapping(ctx, scope, 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) {
scope, err := r.authorize(ctx, input.OrganizationID, probo.ActionFindingPublish)
if err != nil {
return nil, err
}
document, documentVersion, err := r.probo.GeneratedDocuments.PublishFindingList(ctx, scope, input.OrganizationID, input.ApproverIds, input.Minor)
if err != nil {
if errors.Is(err, coredata.ErrResourceAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
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
}
// 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}
}
type auditResolver struct{ *Resolver }
type auditConnectionResolver struct{ *Resolver }
type findingResolver struct{ *Resolver }
type findingConnectionResolver struct{ *Resolver }