Files
probo/pkg/server/api/console/v1/audit_resolvers.go
Ludovic Vielle eccef41767 Adopt File type for trust logos and MCP
Trust GraphQL and MCP still exposed presigned URL strings for
trust-center logos while console and connect already serve stable
File.downloadUrl paths. Phase 1 migrates the seven public logo
fields on trust GraphQL and the trust-center file references on MCP
to the shared File type; trust GraphQL NDA stays on fileUrl for a
follow-up.

Trust resolvers load public files through filemanager and map them
with types.NewFile. The trust app Relay queries and components now
read logo.downloadUrl. MCP specification, resolvers, and helpers
are updated in sync, including NDA on MCP where callers already
have file access.

filemanager is split into focused files and its URL surface is
narrowed to GenerateFileURL(file) for stable app URLs and
GeneratePresignedURL for S3 redirects. GetPublicFile remains the
DB entry point when only a file ID is known.

Add trust and MCP e2e coverage for public logo download URLs.

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-11 16:03:45 +02:00

706 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
}
// 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 }