Migrate audit reports to the files table
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
This commit is contained in:
@@ -8,7 +8,6 @@ package console_v1
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/vikstrous/dataloadgen"
|
||||
"go.gearno.de/kit/log"
|
||||
@@ -68,50 +67,30 @@ func (r *auditResolver) Framework(ctx context.Context, obj *types.Audit) (*types
|
||||
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) {
|
||||
// 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.Report == nil {
|
||||
if obj.ReportFile == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
loaders := dataloader.FromContext(ctx)
|
||||
|
||||
report, err := loaders.Report.Load(ctx, obj.Report.ID)
|
||||
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", log.Error(err))
|
||||
r.logger.ErrorCtx(ctx, "cannot load report file", 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) {
|
||||
scope, err := r.authorize(ctx, obj.ID, probo.ActionReportGetReportUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if obj.Report == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
url, err := r.probo.Audits.GenerateReportURL(ctx, scope, 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
|
||||
return types.NewFile(file), nil
|
||||
}
|
||||
|
||||
// Controls is the resolver for the controls field.
|
||||
@@ -431,7 +410,7 @@ func (r *mutationResolver) CreateAudit(ctx context.Context, input types.CreateAu
|
||||
},
|
||||
}
|
||||
|
||||
audit, err = r.probo.Audits.UploadReport(ctx, scope, uploadReq)
|
||||
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)
|
||||
@@ -514,7 +493,7 @@ func (r *mutationResolver) UploadAuditReport(ctx context.Context, input types.Up
|
||||
},
|
||||
}
|
||||
|
||||
audit, err := r.probo.Audits.UploadReport(ctx, scope, req)
|
||||
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)
|
||||
@@ -704,43 +683,6 @@ func (r *mutationResolver) PublishFindingList(ctx context.Context, input types.P
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DownloadURL is the resolver for the downloadUrl field.
|
||||
func (r *reportResolver) DownloadURL(ctx context.Context, obj *types.Report) (*string, error) {
|
||||
scope, err := r.authorize(ctx, obj.ID, probo.ActionReportDownloadUrlGet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
url, err := r.probo.Reports.GenerateDownloadURL(ctx, scope, 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) {
|
||||
scope, err := r.authorize(ctx, obj.ID, probo.ActionAuditGet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
audit, err := r.probo.Audits.GetByReportID(ctx, scope, 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} }
|
||||
|
||||
@@ -757,11 +699,7 @@ 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 }
|
||||
|
||||
@@ -279,16 +279,6 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error
|
||||
|
||||
return types.NewObligation(obligation), nil
|
||||
}
|
||||
case coredata.ReportEntityType:
|
||||
action = probo.ActionReportGet
|
||||
loadNode = func(ctx context.Context, scope *coredata.Scope, id gid.GID) (types.Node, error) {
|
||||
report, err := r.probo.Reports.Get(ctx, scope, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return types.NewReport(report), nil
|
||||
}
|
||||
case coredata.ProcessingActivityEntityType:
|
||||
action = probo.ActionProcessingActivityList
|
||||
loadNode = func(ctx context.Context, scope *coredata.Scope, id gid.GID) (types.Node, error) {
|
||||
|
||||
@@ -62,7 +62,6 @@ type (
|
||||
Measure *dataloadgen.Loader[gid.GID, *coredata.Measure]
|
||||
Task *dataloadgen.Loader[gid.GID, *coredata.Task]
|
||||
File *dataloadgen.Loader[gid.GID, *coredata.File]
|
||||
Report *dataloadgen.Loader[gid.GID, *coredata.Report]
|
||||
CookieBanner *dataloadgen.Loader[gid.GID, *coredata.CookieBanner]
|
||||
CookieCategory *dataloadgen.Loader[gid.GID, *coredata.CookieCategory]
|
||||
CommonTrackerPattern *dataloadgen.Loader[gid.GID, *coredata.CommonTrackerPattern]
|
||||
@@ -114,7 +113,6 @@ func (f *batchFetcher) newLoaders() *Loaders {
|
||||
Measure: dataloadgen.NewMappedLoader(f.fetchMeasures),
|
||||
Task: dataloadgen.NewMappedLoader(f.fetchTasks),
|
||||
File: dataloadgen.NewMappedLoader(f.fetchFiles),
|
||||
Report: dataloadgen.NewMappedLoader(f.fetchReports),
|
||||
CookieBanner: dataloadgen.NewMappedLoader(f.fetchCookieBanners),
|
||||
CookieCategory: dataloadgen.NewMappedLoader(f.fetchCookieCategories),
|
||||
CommonTrackerPattern: dataloadgen.NewMappedLoader(f.fetchCommonTrackerPatterns),
|
||||
@@ -286,22 +284,6 @@ func (f *batchFetcher) fetchFiles(ctx context.Context, keys []gid.GID) (map[gid.
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (f *batchFetcher) fetchReports(ctx context.Context, keys []gid.GID) (map[gid.GID]*coredata.Report, error) {
|
||||
scope := coredata.NewScopeFromObjectID(keys[0])
|
||||
|
||||
reports, err := f.probo.Reports.GetByIDs(ctx, scope, keys...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot batch load reports: %w", err)
|
||||
}
|
||||
|
||||
result := make(map[gid.GID]*coredata.Report, len(reports))
|
||||
for _, v := range reports {
|
||||
result[v.ID] = v
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (f *batchFetcher) fetchCookieBanners(ctx context.Context, keys []gid.GID) (map[gid.GID]*coredata.CookieBanner, error) {
|
||||
scope := coredata.NewScopeFromObjectID(keys[0])
|
||||
|
||||
|
||||
@@ -151,8 +151,7 @@ type Audit implements Node {
|
||||
framework: Framework @goField(forceResolver: true)
|
||||
validFrom: Datetime
|
||||
validUntil: Datetime
|
||||
report: Report @goField(forceResolver: true)
|
||||
reportUrl: String @goField(forceResolver: true)
|
||||
reportFile: File @goField(forceResolver: true)
|
||||
state: AuditState!
|
||||
|
||||
controls(
|
||||
@@ -208,20 +207,6 @@ type Finding implements Node {
|
||||
permission(action: String!): Boolean! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type Report implements Node {
|
||||
id: ID!
|
||||
objectKey: String!
|
||||
mimeType: String!
|
||||
filename: String!
|
||||
size: Int!
|
||||
downloadUrl: String @goField(forceResolver: true)
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
audit: Audit @goField(forceResolver: true)
|
||||
|
||||
permission(action: String!): Boolean! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type AuditConnection
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.AuditConnection"
|
||||
|
||||
@@ -334,7 +334,8 @@ type TrustCenterDocumentAccess
|
||||
id: ID!
|
||||
status: TrustCenterDocumentAccessStatus!
|
||||
document: Document @goField(forceResolver: true)
|
||||
report: Report @goField(forceResolver: true)
|
||||
reportFile: File @goField(forceResolver: true)
|
||||
audit: Audit @goField(forceResolver: true)
|
||||
trustCenterFile: TrustCenterFile @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
|
||||
@@ -1056,24 +1056,55 @@ func (r *trustCenterDocumentAccessResolver) Document(ctx context.Context, obj *t
|
||||
return types.NewDocument(document), nil
|
||||
}
|
||||
|
||||
// Report is the resolver for the report field.
|
||||
func (r *trustCenterDocumentAccessResolver) Report(ctx context.Context, obj *types.TrustCenterDocumentAccess) (*types.Report, error) {
|
||||
scope, err := r.authorize(ctx, obj.TrustCenterAccessID, probo.ActionReportGet)
|
||||
// ReportFile is the resolver for the reportFile field.
|
||||
func (r *trustCenterDocumentAccessResolver) ReportFile(ctx context.Context, obj *types.TrustCenterDocumentAccess) (*types.File, error) {
|
||||
if _, err := r.authorize(ctx, obj.ID, probo.ActionFileGet); 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, coredata.ErrResourceNotFound) || 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), nil
|
||||
}
|
||||
|
||||
// Audit is the resolver for the audit field.
|
||||
func (r *trustCenterDocumentAccessResolver) Audit(ctx context.Context, obj *types.TrustCenterDocumentAccess) (*types.Audit, error) {
|
||||
scope, err := r.authorize(ctx, obj.ID, probo.ActionAuditGet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if obj.ReportID == nil {
|
||||
if obj.ReportFileID == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
report, err := r.probo.Reports.Get(ctx, scope, *obj.ReportID)
|
||||
audit, err := r.probo.Audits.GetByReportFileID(ctx, scope, *obj.ReportFileID)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot load report", log.Error(err))
|
||||
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot load audit for report file", log.Error(err))
|
||||
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return types.NewReport(report), nil
|
||||
return types.NewAudit(audit), nil
|
||||
}
|
||||
|
||||
// TrustCenterFile is the resolver for the trustCenterFile field.
|
||||
|
||||
@@ -77,9 +77,9 @@ func NewAudit(a *coredata.Audit) *Audit {
|
||||
UpdatedAt: a.UpdatedAt,
|
||||
}
|
||||
|
||||
if a.ReportID != nil {
|
||||
node.Report = &Report{
|
||||
ID: *a.ReportID,
|
||||
if a.ReportFileID != nil {
|
||||
node.ReportFile = &File{
|
||||
ID: *a.ReportFileID,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// 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 types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
)
|
||||
|
||||
func NewReport(r *coredata.Report) *Report {
|
||||
return &Report{
|
||||
ID: r.ID,
|
||||
ObjectKey: r.ObjectKey,
|
||||
MimeType: r.MimeType,
|
||||
Filename: r.Filename,
|
||||
Size: int(r.Size),
|
||||
CreatedAt: r.CreatedAt,
|
||||
UpdatedAt: r.UpdatedAt,
|
||||
}
|
||||
}
|
||||
@@ -42,13 +42,13 @@ type (
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
TrustCenterAccess *TrustCenterAccess `json:"trustCenterAccess"`
|
||||
Document *Document `json:"document,omitempty"`
|
||||
Report *Report `json:"report,omitempty"`
|
||||
ReportFile *File `json:"reportFile,omitempty"`
|
||||
TrustCenterFile *TrustCenterFile `json:"trustCenterFile,omitempty"`
|
||||
|
||||
// Internal fields used by resolvers
|
||||
TrustCenterAccessID gid.GID `json:"-"`
|
||||
DocumentID *gid.GID `json:"-"`
|
||||
ReportID *gid.GID `json:"-"`
|
||||
ReportFileID *gid.GID `json:"-"`
|
||||
TrustCenterFileID *gid.GID `json:"-"`
|
||||
}
|
||||
)
|
||||
@@ -62,7 +62,7 @@ func NewTrustCenterDocumentAccess(tcda *coredata.TrustCenterDocumentAccess) *Tru
|
||||
UpdatedAt: tcda.UpdatedAt,
|
||||
TrustCenterAccessID: tcda.TrustCenterAccessID,
|
||||
DocumentID: tcda.DocumentID,
|
||||
ReportID: tcda.ReportID,
|
||||
ReportFileID: tcda.ReportFileID,
|
||||
TrustCenterFileID: tcda.TrustCenterFileID,
|
||||
}
|
||||
|
||||
@@ -72,9 +72,9 @@ func NewTrustCenterDocumentAccess(tcda *coredata.TrustCenterDocumentAccess) *Tru
|
||||
}
|
||||
}
|
||||
|
||||
if tcda.ReportID != nil {
|
||||
object.Report = &Report{
|
||||
ID: *tcda.ReportID,
|
||||
if tcda.ReportFileID != nil {
|
||||
object.ReportFile = &File{
|
||||
ID: *tcda.ReportFileID,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1468,16 +1468,16 @@ func (r *Resolver) GetAuditTool(ctx context.Context, req *mcp.CallToolRequest, i
|
||||
return nil, types.GetAuditOutput{}, fmt.Errorf("cannot get audit: %w", err)
|
||||
}
|
||||
|
||||
var report *coredata.Report
|
||||
if audit.ReportID != nil {
|
||||
report, err = prb.Reports.Get(ctx, scope, *audit.ReportID)
|
||||
var file *coredata.File
|
||||
if audit.ReportFileID != nil {
|
||||
file, err = prb.Files.Get(ctx, scope, *audit.ReportFileID)
|
||||
if err != nil {
|
||||
return nil, types.GetAuditOutput{}, fmt.Errorf("cannot get audit report: %w", err)
|
||||
return nil, types.GetAuditOutput{}, fmt.Errorf("cannot get audit report file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, types.GetAuditOutput{
|
||||
Audit: types.NewAudit(audit, report),
|
||||
Audit: types.NewAudit(audit, file),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1532,16 +1532,16 @@ func (r *Resolver) UpdateAuditTool(ctx context.Context, req *mcp.CallToolRequest
|
||||
return nil, types.UpdateAuditOutput{}, fmt.Errorf("cannot update audit: %w", err)
|
||||
}
|
||||
|
||||
var report *coredata.Report
|
||||
if audit.ReportID != nil {
|
||||
report, err = svc.Reports.Get(ctx, scope, *audit.ReportID)
|
||||
var file *coredata.File
|
||||
if audit.ReportFileID != nil {
|
||||
file, err = svc.Files.Get(ctx, scope, *audit.ReportFileID)
|
||||
if err != nil {
|
||||
return nil, types.UpdateAuditOutput{}, fmt.Errorf("cannot get audit report: %w", err)
|
||||
return nil, types.UpdateAuditOutput{}, fmt.Errorf("cannot get audit report file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, types.UpdateAuditOutput{
|
||||
Audit: types.NewAudit(audit, report),
|
||||
Audit: types.NewAudit(audit, file),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
func NewAudit(a *coredata.Audit, report *coredata.Report) *Audit {
|
||||
func NewAudit(a *coredata.Audit, file *coredata.File) *Audit {
|
||||
audit := &Audit{
|
||||
ID: a.ID,
|
||||
Name: a.Name,
|
||||
@@ -27,16 +27,16 @@ func NewAudit(a *coredata.Audit, report *coredata.Report) *Audit {
|
||||
FrameworkID: a.FrameworkID,
|
||||
State: a.State,
|
||||
TrustCenterVisibility: a.TrustCenterVisibility,
|
||||
HasReport: a.ReportID != nil,
|
||||
HasReport: a.ReportFileID != nil,
|
||||
ValidFrom: a.ValidFrom,
|
||||
ValidUntil: a.ValidUntil,
|
||||
CreatedAt: a.CreatedAt,
|
||||
UpdatedAt: a.UpdatedAt,
|
||||
}
|
||||
|
||||
if report != nil {
|
||||
audit.ReportFilename = &report.Filename
|
||||
audit.ReportMimeType = &report.MimeType
|
||||
if file != nil {
|
||||
audit.ReportFilename = &file.FileName
|
||||
audit.ReportMimeType = &file.MimeType
|
||||
}
|
||||
|
||||
return audit
|
||||
|
||||
@@ -227,7 +227,7 @@ func SlackHandler(slackSvc *slack.Service, slackSigningSecret string, logger *lo
|
||||
switch gID.EntityType() {
|
||||
case coredata.DocumentEntityType:
|
||||
documentIDs = []gid.GID{gID}
|
||||
case coredata.ReportEntityType:
|
||||
case coredata.FileEntityType:
|
||||
reportIDs = []gid.GID{gID}
|
||||
case coredata.TrustCenterFileEntityType:
|
||||
fileIDs = []gid.GID{gID}
|
||||
|
||||
@@ -83,21 +83,21 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error
|
||||
|
||||
return types.NewFramework(framework), nil
|
||||
|
||||
case coredata.ReportEntityType:
|
||||
case coredata.FileEntityType:
|
||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||
|
||||
report, err := trustService.Reports.Get(ctx, scope, trustCenter.OrganizationID, id)
|
||||
file, err := trustService.Reports.Get(ctx, scope, trustCenter.OrganizationID, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, trust.ErrReportNotFound) || errors.Is(err, coredata.ErrResourceNotFound) {
|
||||
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot get report", log.Error(err))
|
||||
r.logger.ErrorCtx(ctx, "cannot get audit report file", log.Error(err))
|
||||
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return types.NewReport(report), nil
|
||||
return types.NewAuditReport(file), nil
|
||||
|
||||
case coredata.AuditEntityType:
|
||||
audit, err := trustService.Audits.Get(ctx, scope, id)
|
||||
|
||||
@@ -114,9 +114,9 @@ type Framework implements Node @nda {
|
||||
darkLogoURL: String @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type Report implements Node @nda {
|
||||
type AuditReport implements Node @nda {
|
||||
id: ID!
|
||||
filename: String!
|
||||
fileName: String!
|
||||
isUserAuthorized: Boolean! @goField(forceResolver: true)
|
||||
access: DocumentAccess @goField(forceResolver: true)
|
||||
}
|
||||
@@ -125,7 +125,7 @@ type Audit implements Node @nda {
|
||||
id: ID!
|
||||
name: String
|
||||
framework: Framework! @goField(forceResolver: true)
|
||||
report: Report @goField(forceResolver: true)
|
||||
reportFile: AuditReport @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type AuditConnection @nda {
|
||||
|
||||
@@ -44,8 +44,8 @@ func (r *auditResolver) Framework(ctx context.Context, obj *types.Audit) (*types
|
||||
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) {
|
||||
// ReportFile is the resolver for the reportFile field.
|
||||
func (r *auditResolver) ReportFile(ctx context.Context, obj *types.Audit) (*types.AuditReport, error) {
|
||||
scope := coredata.NewScopeFromObjectID(obj.ID)
|
||||
trustService := r.trust
|
||||
|
||||
@@ -55,19 +55,105 @@ func (r *auditResolver) Report(ctx context.Context, obj *types.Audit) (*types.Re
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
if audit.ReportID == nil {
|
||||
if audit.ReportFileID == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||
|
||||
report, err := trustService.Reports.Get(ctx, scope, trustCenter.OrganizationID, *audit.ReportID)
|
||||
file, err := trustService.Reports.Get(ctx, scope, trustCenter.OrganizationID, *audit.ReportFileID)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot load report", log.Error(err))
|
||||
r.logger.ErrorCtx(ctx, "cannot load report file", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return types.NewReport(report), nil
|
||||
return types.NewAuditReport(file), nil
|
||||
}
|
||||
|
||||
// IsUserAuthorized is the resolver for the isUserAuthorized field.
|
||||
func (r *auditReportResolver) IsUserAuthorized(ctx context.Context, obj *types.AuditReport) (bool, error) {
|
||||
scope := coredata.NewScopeFromObjectID(obj.ID)
|
||||
trustService := r.trust
|
||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||
|
||||
audit, err := trustService.Audits.GetByReportFileID(ctx, scope, obj.ID)
|
||||
if err != nil {
|
||||
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot load audit for report file", log.Error(err))
|
||||
|
||||
return false, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
if audit.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
reportAccess, err := trustService.TrustCenterAccesses.GetReportFileAccess(ctx, scope,
|
||||
trustCenter.ID,
|
||||
identity.ID,
|
||||
obj.ID,
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, trust.ErrMembershipNotFound) ||
|
||||
errors.Is(err, trust.ErrUserNotFound) ||
|
||||
errors.Is(err, trust.ErrUserInactive) ||
|
||||
errors.Is(err, trust.ErrDocumentAccessNotFound) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot check report access", log.Error(err))
|
||||
|
||||
return false, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return reportAccess.Status == coredata.TrustCenterDocumentAccessStatusGranted, nil
|
||||
}
|
||||
|
||||
// Access is the resolver for the access field.
|
||||
func (r *auditReportResolver) Access(ctx context.Context, obj *types.AuditReport) (*types.DocumentAccess, error) {
|
||||
scope := coredata.NewScopeFromObjectID(obj.ID)
|
||||
trustService := r.trust
|
||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
access, err := trustService.TrustCenterAccesses.GetReportFileAccess(
|
||||
ctx, scope,
|
||||
trustCenter.ID,
|
||||
identity.ID,
|
||||
obj.ID,
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, trust.ErrMembershipNotFound) ||
|
||||
errors.Is(err, trust.ErrUserNotFound) ||
|
||||
errors.Is(err, trust.ErrDocumentAccessNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if errors.Is(err, trust.ErrUserInactive) {
|
||||
return nil, gqlutils.Forbidden(ctx, err)
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot get audit report access", log.Error(err))
|
||||
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return &types.DocumentAccess{
|
||||
ID: access.ID,
|
||||
Status: access.Status,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Framework is the resolver for the framework field on ComplianceFramework.
|
||||
@@ -294,7 +380,7 @@ func (r *mutationResolver) ExportReportPDF(ctx context.Context, input types.Expo
|
||||
trustService := r.trust
|
||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||
|
||||
audit, err := trustService.Audits.GetByReportID(ctx, scope, input.ReportID)
|
||||
audit, err := trustService.Audits.GetByReportFileID(ctx, scope, input.ReportID)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
@@ -317,7 +403,7 @@ func (r *mutationResolver) ExportReportPDF(ctx context.Context, input types.Expo
|
||||
return nil, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
|
||||
}
|
||||
|
||||
reportAccess, err := trustService.TrustCenterAccesses.GetReportAccess(
|
||||
reportAccess, err := trustService.TrustCenterAccesses.GetReportFileAccess(
|
||||
ctx, scope,
|
||||
trustCenter.ID,
|
||||
identity.ID,
|
||||
@@ -458,7 +544,7 @@ func (r *mutationResolver) RequestReportAccess(ctx context.Context, input types.
|
||||
scope := coredata.NewScopeFromObjectID(trustCenter.ID)
|
||||
trustService := r.trust
|
||||
|
||||
audit, err := trustService.Audits.GetByReportID(ctx, scope, input.ReportID)
|
||||
audit, err := trustService.Audits.GetByReportFileID(ctx, scope, input.ReportID)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
@@ -543,87 +629,6 @@ func (r *mutationResolver) RequestTrustCenterFileAccess(ctx context.Context, inp
|
||||
}, nil
|
||||
}
|
||||
|
||||
// IsUserAuthorized is the resolver for the isUserAuthorized field.
|
||||
func (r *reportResolver) IsUserAuthorized(ctx context.Context, obj *types.Report) (bool, error) {
|
||||
scope := coredata.NewScopeFromObjectID(obj.ID)
|
||||
trustService := r.trust
|
||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||
|
||||
audit, err := trustService.Audits.GetByReportID(ctx, scope, obj.ID)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot load document", log.Error(err))
|
||||
return false, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
if audit.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
reportAccess, err := trustService.TrustCenterAccesses.GetReportAccess(ctx, scope,
|
||||
trustCenter.ID,
|
||||
identity.ID,
|
||||
obj.ID,
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, trust.ErrMembershipNotFound) ||
|
||||
errors.Is(err, trust.ErrUserNotFound) ||
|
||||
errors.Is(err, trust.ErrUserInactive) ||
|
||||
errors.Is(err, trust.ErrDocumentAccessNotFound) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot check report access", log.Error(err))
|
||||
|
||||
return false, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return reportAccess.Status == coredata.TrustCenterDocumentAccessStatusGranted, nil
|
||||
}
|
||||
|
||||
// Access is the resolver for the access field.
|
||||
func (r *reportResolver) Access(ctx context.Context, obj *types.Report) (*types.DocumentAccess, error) {
|
||||
scope := coredata.NewScopeFromObjectID(obj.ID)
|
||||
trustService := r.trust
|
||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
return nil, nil // User is not authenticated, so no access requested
|
||||
}
|
||||
|
||||
access, err := trustService.TrustCenterAccesses.GetReportAccess(
|
||||
ctx, scope,
|
||||
trustCenter.ID,
|
||||
identity.ID,
|
||||
obj.ID,
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, trust.ErrMembershipNotFound) ||
|
||||
errors.Is(err, trust.ErrUserNotFound) ||
|
||||
errors.Is(err, trust.ErrDocumentAccessNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if errors.Is(err, trust.ErrUserInactive) {
|
||||
return nil, gqlutils.Forbidden(ctx, err)
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot get audit report access", log.Error(err))
|
||||
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return &types.DocumentAccess{
|
||||
ID: access.ID,
|
||||
Status: access.Status,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TotalCount is the resolver for the totalCount field.
|
||||
func (r *subprocessorConnectionResolver) TotalCount(ctx context.Context, obj *types.SubprocessorConnection) (int, error) {
|
||||
scope := coredata.NewScopeFromObjectID(obj.ParentID)
|
||||
@@ -987,6 +992,9 @@ func (r *trustCenterReferenceResolver) LogoURL(ctx context.Context, obj *types.T
|
||||
// Audit returns schema.AuditResolver implementation.
|
||||
func (r *Resolver) Audit() schema.AuditResolver { return &auditResolver{r} }
|
||||
|
||||
// AuditReport returns schema.AuditReportResolver implementation.
|
||||
func (r *Resolver) AuditReport() schema.AuditReportResolver { return &auditReportResolver{r} }
|
||||
|
||||
// ComplianceFramework returns schema.ComplianceFrameworkResolver implementation.
|
||||
func (r *Resolver) ComplianceFramework() schema.ComplianceFrameworkResolver {
|
||||
return &complianceFrameworkResolver{r}
|
||||
@@ -998,9 +1006,6 @@ func (r *Resolver) Document() schema.DocumentResolver { return &documentResolver
|
||||
// Framework returns schema.FrameworkResolver implementation.
|
||||
func (r *Resolver) Framework() schema.FrameworkResolver { return &frameworkResolver{r} }
|
||||
|
||||
// Report returns schema.ReportResolver implementation.
|
||||
func (r *Resolver) Report() schema.ReportResolver { return &reportResolver{r} }
|
||||
|
||||
// SubprocessorConnection returns schema.SubprocessorConnectionResolver implementation.
|
||||
func (r *Resolver) SubprocessorConnection() schema.SubprocessorConnectionResolver {
|
||||
return &subprocessorConnectionResolver{r}
|
||||
@@ -1020,10 +1025,10 @@ func (r *Resolver) TrustCenterReference() schema.TrustCenterReferenceResolver {
|
||||
}
|
||||
|
||||
type auditResolver struct{ *Resolver }
|
||||
type auditReportResolver struct{ *Resolver }
|
||||
type complianceFrameworkResolver struct{ *Resolver }
|
||||
type documentResolver struct{ *Resolver }
|
||||
type frameworkResolver struct{ *Resolver }
|
||||
type reportResolver struct{ *Resolver }
|
||||
type subprocessorConnectionResolver struct{ *Resolver }
|
||||
type trustCenterResolver struct{ *Resolver }
|
||||
type trustCenterFileResolver struct{ *Resolver }
|
||||
|
||||
@@ -18,9 +18,9 @@ import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
)
|
||||
|
||||
func NewReport(r *coredata.Report) *Report {
|
||||
return &Report{
|
||||
ID: r.ID,
|
||||
Filename: r.Filename,
|
||||
func NewAuditReport(f *coredata.File) *AuditReport {
|
||||
return &AuditReport{
|
||||
ID: f.ID,
|
||||
FileName: f.FileName,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user