Files
probo/pkg/server/api/console/v1/evidence_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

195 lines
5.9 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/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"
)
// File is the resolver for the file field.
func (r *evidenceResolver) File(ctx context.Context, obj *types.Evidence) (*types.File, error) {
if _, err := r.authorize(ctx, obj.ID, probo.ActionFileGet); err != nil {
return nil, err
}
if obj.File == nil {
return nil, nil
}
loaders := dataloader.FromContext(ctx)
file, err := loaders.File.Load(ctx, obj.File.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 evidence file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewFile(file, r.fileManager), nil
}
// Task is the resolver for the task field.
func (r *evidenceResolver) Task(ctx context.Context, obj *types.Evidence) (*types.Task, error) {
if _, err := r.authorize(ctx, obj.ID, probo.ActionTaskGet); err != nil {
return nil, err
}
if obj.Task == nil {
r.logger.ErrorCtx(ctx, "evidence is not associated with a task")
return nil, gqlutils.Internal(ctx)
}
loaders := dataloader.FromContext(ctx)
task, err := loaders.Task.Load(ctx, obj.Task.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 task", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewTask(task), nil
}
// Measure is the resolver for the measure field.
func (r *evidenceResolver) Measure(ctx context.Context, obj *types.Evidence) (*types.Measure, error) {
if _, err := r.authorize(ctx, obj.ID, probo.ActionMeasureGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
measure, err := loaders.Measure.Load(ctx, obj.Measure.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 measure", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewMeasure(measure), nil
}
// Permission is the resolver for the permission field.
func (r *evidenceResolver) Permission(ctx context.Context, obj *types.Evidence, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *evidenceConnectionResolver) TotalCount(ctx context.Context, obj *types.EvidenceConnection) (int, error) {
scope, err := r.authorize(ctx, obj.ParentID, probo.ActionEvidenceList)
if err != nil {
return 0, err
}
switch obj.Resolver.(type) {
case *measureResolver:
count, err := r.probo.Evidences.CountForMeasureID(ctx, scope, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count measure evidence", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
case *taskResolver:
count, err := r.probo.Evidences.CountForTaskID(ctx, scope, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count task evidence", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver")
return 0, gqlutils.Internal(ctx)
}
// DeleteEvidence is the resolver for the deleteEvidence field.
func (r *mutationResolver) DeleteEvidence(ctx context.Context, input types.DeleteEvidenceInput) (*types.DeleteEvidencePayload, error) {
scope, err := r.authorize(ctx, input.EvidenceID, probo.ActionEvidenceDelete)
if err != nil {
return nil, err
}
if err := r.probo.Evidences.Delete(ctx, scope, input.EvidenceID); err != nil {
r.logger.ErrorCtx(ctx, "cannot delete evidence", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteEvidencePayload{
DeletedEvidenceID: input.EvidenceID,
}, nil
}
// UploadMeasureEvidence is the resolver for the uploadMeasureEvidence field.
func (r *mutationResolver) UploadMeasureEvidence(ctx context.Context, input types.UploadMeasureEvidenceInput) (*types.UploadMeasureEvidencePayload, error) {
scope, err := r.authorize(ctx, input.MeasureID, probo.ActionMeasureEvidenceUpload)
if err != nil {
return nil, err
}
evidence, err := r.probo.Evidences.UploadMeasureEvidence(
ctx, scope,
probo.UploadMeasureEvidenceRequest{
MeasureID: input.MeasureID,
File: probo.FileUpload{
Content: input.File.File,
Filename: input.File.Filename,
Size: input.File.Size,
ContentType: input.File.ContentType,
},
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot upload measure evidence", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UploadMeasureEvidencePayload{
EvidenceEdge: types.NewEvidenceEdge(evidence, coredata.EvidenceOrderFieldCreatedAt),
}, nil
}
// Evidence returns schema.EvidenceResolver implementation.
func (r *Resolver) Evidence() schema.EvidenceResolver { return &evidenceResolver{r} }
// EvidenceConnection returns schema.EvidenceConnectionResolver implementation.
func (r *Resolver) EvidenceConnection() schema.EvidenceConnectionResolver {
return &evidenceConnectionResolver{r}
}
type evidenceResolver struct{ *Resolver }
type evidenceConnectionResolver struct{ *Resolver }