Switch console file fields to File download URLs

Replace presigned URL string fields (logoUrl, fileUrl, ndaFileName,
etc.) with nested File references resolved through /api/files/v1/.
Update console Relay queries and e2e coverage accordingly.

Route NDA upload through filemanager.PutFile and return stable IAM
org logo URLs for consistency with the files API.

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
This commit is contained in:
Ludovic Vielle
2026-06-10 14:40:33 +02:00
parent 003fd2baf1
commit 3475dd0560
50 changed files with 545 additions and 386 deletions

View File

@@ -8,7 +8,6 @@ package console_v1
import (
"context"
"errors"
"fmt"
"slices"
"strings"
@@ -332,15 +331,7 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error
return nil, err
}
var file *coredata.File
if trustCenter.NonDisclosureAgreementFileID != nil {
file, err = r.probo.Files.Get(ctx, scope, *trustCenter.NonDisclosureAgreementFileID)
if err != nil {
return nil, fmt.Errorf("cannot get NDA file: %w", err)
}
}
return types.NewTrustCenter(trustCenter, file), nil
return types.NewTrustCenter(trustCenter), nil
}
case coredata.TrustCenterAccessEntityType:
action = probo.ActionTrustCenterAccessGet

View File

@@ -8,30 +8,22 @@ package console_v1
import (
"context"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/probo"
"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"
)
// LogoURL is the resolver for the logoUrl field.
func (r *commonThirdPartyResolver) LogoURL(ctx context.Context, obj *types.CommonThirdParty) (*string, error) {
// Logo is the resolver for the logo field.
func (r *commonThirdPartyResolver) Logo(ctx context.Context, obj *types.CommonThirdParty) (*types.File, error) {
if _, err := r.authorize(ctx, obj.ID, probo.ActionCommonThirdPartyGet); err != nil {
return nil, err
}
if obj.LogoFileID == nil {
if obj.Logo == nil {
return nil, nil
}
logoURL, err := r.thirdParty.GenerateLogoURL(ctx, *obj.LogoFileID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate common third party logo URL", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return logoURL, nil
return r.loadFile(ctx, obj.Logo.ID)
}
// CommonThirdParty returns schema.CommonThirdPartyResolver implementation.

View File

@@ -8,34 +8,23 @@ package console_v1
import (
"context"
"fmt"
"time"
"go.probo.inc/probo/pkg/probo"
"go.probo.inc/probo/pkg/server/api/console/v1/schema"
"go.probo.inc/probo/pkg/server/api/console/v1/types"
)
// CertificateFileURL is the resolver for the certificateFileUrl field.
func (r *electronicSignatureResolver) CertificateFileURL(ctx context.Context, obj *types.ElectronicSignature) (*string, error) {
// Certificate is the resolver for the certificate field.
func (r *electronicSignatureResolver) Certificate(ctx context.Context, obj *types.ElectronicSignature) (*types.File, error) {
if _, err := r.authorize(ctx, obj.ID, probo.ActionElectronicSignatureGet); err != nil {
return nil, err
}
signature, err := r.esign.GetSignatureByID(ctx, obj.ID)
if err != nil {
return nil, fmt.Errorf("cannot load signature: %w", err)
}
if signature.CertificateFileID == nil {
if obj.Certificate == nil {
return nil, nil
}
url, err := r.esign.GenerateCertificateFileURL(ctx, *signature.CertificateFileID, 1*time.Hour)
if err != nil {
return nil, fmt.Errorf("cannot generate certificate file URL: %w", err)
}
return &url, nil
return r.loadFile(ctx, obj.Certificate.ID)
}
// Events is the resolver for the events field.

View File

@@ -0,0 +1,44 @@
// Copyright (c) 2026 Probo Inc <hello@probo.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 console_v1
import (
"context"
"errors"
"github.com/vikstrous/dataloadgen"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/server/api/console/v1/dataloader"
"go.probo.inc/probo/pkg/server/api/console/v1/types"
"go.probo.inc/probo/pkg/server/gqlutils"
)
func (r *Resolver) loadFile(ctx context.Context, fileID gid.GID) (*types.File, error) {
loaders := dataloader.FromContext(ctx)
file, err := loaders.File.Load(ctx, fileID)
if err != nil {
if errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot load file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewFile(file, r.baseURL), nil
}

View File

@@ -1,38 +0,0 @@
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"
"time"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/probo"
"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"
)
// DownloadURL is the resolver for the downloadUrl field.
func (r *fileResolver) DownloadURL(ctx context.Context, obj *types.File) (string, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionFileGet)
if err != nil {
return "", err
}
downloadUrl, err := r.probo.Files.GenerateFileURL(ctx, scope, obj.ID, 60*time.Second)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate download URL", log.Error(err))
return "", gqlutils.Internal(ctx)
}
return downloadUrl, nil
}
// File returns schema.FileResolver implementation.
func (r *Resolver) File() schema.FileResolver { return &fileResolver{r} }
type fileResolver struct{ *Resolver }

View File

@@ -9,7 +9,6 @@ import (
"context"
"encoding/json"
"errors"
"time"
"github.com/vikstrous/dataloadgen"
"go.gearno.de/kit/log"
@@ -81,24 +80,30 @@ func (r *frameworkResolver) Controls(ctx context.Context, obj *types.Framework,
return types.NewControlConnection(page, r, obj.ID, controlFilter), nil
}
// LightLogoURL is the resolver for the lightLogoURL field.
func (r *frameworkResolver) LightLogoURL(ctx context.Context, obj *types.Framework) (*string, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionFrameworkGet)
if err != nil {
// LightLogo is the resolver for the lightLogo field.
func (r *frameworkResolver) LightLogo(ctx context.Context, obj *types.Framework) (*types.File, error) {
if _, err := r.authorize(ctx, obj.ID, probo.ActionFrameworkGet); err != nil {
return nil, err
}
return r.probo.Frameworks.GenerateLightLogoURL(ctx, scope, obj.ID, 1*time.Hour)
if obj.LightLogo == nil {
return nil, nil
}
return r.loadFile(ctx, obj.LightLogo.ID)
}
// DarkLogoURL is the resolver for the darkLogoURL field.
func (r *frameworkResolver) DarkLogoURL(ctx context.Context, obj *types.Framework) (*string, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionFrameworkGet)
if err != nil {
// DarkLogo is the resolver for the darkLogo field.
func (r *frameworkResolver) DarkLogo(ctx context.Context, obj *types.Framework) (*types.File, error) {
if _, err := r.authorize(ctx, obj.ID, probo.ActionFrameworkGet); err != nil {
return nil, err
}
return r.probo.Frameworks.GenerateDarkLogoURL(ctx, scope, obj.ID, 1*time.Hour)
if obj.DarkLogo == nil {
return nil, nil
}
return r.loadFile(ctx, obj.DarkLogo.ID)
}
// Permission is the resolver for the permission field.

View File

@@ -30,5 +30,5 @@ type CommonThirdParty
trustPageUrl: String
statusPageUrl: String
termsOfServiceUrl: String
logoUrl: String @goField(forceResolver: true)
logo: File @goField(forceResolver: true)
}

View File

@@ -129,7 +129,7 @@ type ElectronicSignature implements Node {
consentText: String!
lastError: String
signedAt: Datetime
certificateFileUrl: String @goField(forceResolver: true)
certificate: File @goField(forceResolver: true)
events: [ElectronicSignatureEvent!]! @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!

View File

@@ -3,7 +3,7 @@ type File {
mimeType: String!
fileName: String!
size: BigInt!
downloadUrl: String! @goField(forceResolver: true)
downloadUrl: String!
createdAt: Datetime!
updatedAt: Datetime!
}

View File

@@ -29,8 +29,8 @@ type Framework implements Node {
orderBy: ControlOrder
filter: ControlFilter
): ControlConnection! @goField(forceResolver: true)
lightLogoURL: String @goField(forceResolver: true)
darkLogoURL: String @goField(forceResolver: true)
lightLogo: File @goField(forceResolver: true)
darkLogo: File @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!

View File

@@ -90,8 +90,8 @@ type UpdateOrganizationContextPayload {
type Organization implements Node {
id: ID!
name: String!
logoUrl: String @goField(forceResolver: true)
horizontalLogoUrl: String @goField(forceResolver: true)
logo: File @goField(forceResolver: true)
horizontalLogo: File @goField(forceResolver: true)
description: String
websiteUrl: String

View File

@@ -342,9 +342,7 @@ type ThirdPartyBusinessAssociateAgreement implements Node {
thirdParty: ThirdParty! @goField(forceResolver: true)
validFrom: Datetime
validUntil: Datetime
fileName: String!
fileUrl: String! @goField(forceResolver: true)
fileSize: BigInt!
file: File! @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
@@ -380,9 +378,7 @@ type ThirdPartyDataPrivacyAgreement implements Node {
thirdParty: ThirdParty! @goField(forceResolver: true)
validFrom: Datetime
validUntil: Datetime
fileName: String!
fileUrl: String! @goField(forceResolver: true)
fileSize: BigInt!
file: File! @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!

View File

@@ -249,10 +249,9 @@ type TrustCenter implements Node
id: ID!
active: Boolean!
searchEngineIndexing: SearchEngineIndexing!
logoFileUrl: String @goField(forceResolver: true)
darkLogoFileUrl: String @goField(forceResolver: true)
ndaFileName: String
ndaFileUrl: String @goField(forceResolver: true)
logo: File @goField(forceResolver: true)
darkLogo: File @goField(forceResolver: true)
nda: File @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
organization: Organization! @goField(forceResolver: true)
@@ -368,7 +367,7 @@ type TrustCenterReference implements Node {
name: String!
description: String
websiteUrl: String!
logoUrl: String! @goField(forceResolver: true)
logo: File! @goField(forceResolver: true)
rank: Int!
createdAt: Datetime!
updatedAt: Datetime!
@@ -443,7 +442,7 @@ type TrustCenterFile implements Node {
id: ID!
name: String!
category: String!
fileUrl: String! @goField(forceResolver: true)
file: File! @goField(forceResolver: true)
trustCenterVisibility: TrustCenterVisibility!
createdAt: Datetime!
updatedAt: Datetime!

View File

@@ -9,7 +9,6 @@ import (
"context"
"errors"
"fmt"
"time"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/agentrun"
@@ -58,36 +57,30 @@ func (r *mutationResolver) UpdateOrganizationContext(ctx context.Context, input
}, nil
}
// LogoURL is the resolver for the logoUrl field.
func (r *organizationResolver) LogoURL(ctx context.Context, obj *types.Organization) (*string, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGetLogoUrl)
if err != nil {
// Logo is the resolver for the logo field.
func (r *organizationResolver) Logo(ctx context.Context, obj *types.Organization) (*types.File, error) {
if _, err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGetLogoUrl); err != nil {
return nil, err
}
logoURL, err := r.probo.Organizations.GenerateLogoURL(ctx, scope, obj.ID, 1*time.Hour)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate logo url", log.Error(err))
return nil, gqlutils.Internal(ctx)
if obj.Logo == nil {
return nil, nil
}
return logoURL, nil
return r.loadFile(ctx, obj.Logo.ID)
}
// HorizontalLogoURL is the resolver for the horizontalLogoUrl field.
func (r *organizationResolver) HorizontalLogoURL(ctx context.Context, obj *types.Organization) (*string, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGetHorizontalLogoUrl)
if err != nil {
// HorizontalLogo is the resolver for the horizontalLogo field.
func (r *organizationResolver) HorizontalLogo(ctx context.Context, obj *types.Organization) (*types.File, error) {
if _, err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGetHorizontalLogoUrl); err != nil {
return nil, err
}
horizontalLogoURL, err := r.probo.Organizations.GenerateHorizontalLogoURL(ctx, scope, obj.ID, 1*time.Hour)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate horizontal logo url", log.Error(err))
return nil, gqlutils.Internal(ctx)
if obj.HorizontalLogo == nil {
return nil, nil
}
return horizontalLogoURL, nil
return r.loadFile(ctx, obj.HorizontalLogo.ID)
}
// Context is the resolver for the context field.
@@ -1175,16 +1168,7 @@ func (r *organizationResolver) TrustCenter(ctx context.Context, obj *types.Organ
return nil, gqlutils.Internal(ctx)
}
var file *coredata.File
if trustCenter.NonDisclosureAgreementFileID != nil {
file, err = r.probo.Files.Get(ctx, scope, *trustCenter.NonDisclosureAgreementFileID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get NDA file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
}
return types.NewTrustCenter(trustCenter, file), nil
return types.NewTrustCenter(trustCenter), nil
}
// CustomDomain is the resolver for the customDomain field.

View File

@@ -9,7 +9,6 @@ import (
"context"
"errors"
"fmt"
"time"
pgx "github.com/jackc/pgx/v5"
"github.com/vikstrous/dataloadgen"
@@ -359,7 +358,7 @@ func (r *mutationResolver) UploadThirdPartyBusinessAssociateAgreement(ctx contex
return nil, err
}
thirdPartyBusinessAssociateAgreement, file, err := r.probo.ThirdPartyBusinessAssociateAgreements.Upload(
thirdPartyBusinessAssociateAgreement, _, err := r.probo.ThirdPartyBusinessAssociateAgreements.Upload(
ctx, scope,
input.ThirdPartyID,
&probo.ThirdPartyBusinessAssociateAgreementCreateRequest{
@@ -380,7 +379,7 @@ func (r *mutationResolver) UploadThirdPartyBusinessAssociateAgreement(ctx contex
}
return &types.UploadThirdPartyBusinessAssociateAgreementPayload{
ThirdPartyBusinessAssociateAgreement: types.NewThirdPartyBusinessAssociateAgreement(thirdPartyBusinessAssociateAgreement, file),
ThirdPartyBusinessAssociateAgreement: types.NewThirdPartyBusinessAssociateAgreement(thirdPartyBusinessAssociateAgreement),
}, nil
}
@@ -391,7 +390,7 @@ func (r *mutationResolver) UpdateThirdPartyBusinessAssociateAgreement(ctx contex
return nil, err
}
thirdPartyBusinessAssociateAgreement, file, err := r.probo.ThirdPartyBusinessAssociateAgreements.Update(
thirdPartyBusinessAssociateAgreement, _, err := r.probo.ThirdPartyBusinessAssociateAgreements.Update(
ctx, scope,
input.ThirdPartyID,
&probo.ThirdPartyBusinessAssociateAgreementUpdateRequest{
@@ -410,7 +409,7 @@ func (r *mutationResolver) UpdateThirdPartyBusinessAssociateAgreement(ctx contex
}
return &types.UpdateThirdPartyBusinessAssociateAgreementPayload{
ThirdPartyBusinessAssociateAgreement: types.NewThirdPartyBusinessAssociateAgreement(thirdPartyBusinessAssociateAgreement, file),
ThirdPartyBusinessAssociateAgreement: types.NewThirdPartyBusinessAssociateAgreement(thirdPartyBusinessAssociateAgreement),
}, nil
}
@@ -438,7 +437,7 @@ func (r *mutationResolver) UploadThirdPartyDataPrivacyAgreement(ctx context.Cont
return nil, err
}
thirdPartyDataPrivacyAgreement, file, err := r.probo.ThirdPartyDataPrivacyAgreements.Upload(
thirdPartyDataPrivacyAgreement, _, err := r.probo.ThirdPartyDataPrivacyAgreements.Upload(
ctx, scope,
input.ThirdPartyID,
&probo.ThirdPartyDataPrivacyAgreementCreateRequest{
@@ -459,7 +458,7 @@ func (r *mutationResolver) UploadThirdPartyDataPrivacyAgreement(ctx context.Cont
}
return &types.UploadThirdPartyDataPrivacyAgreementPayload{
ThirdPartyDataPrivacyAgreement: types.NewThirdPartyDataPrivacyAgreement(thirdPartyDataPrivacyAgreement, file),
ThirdPartyDataPrivacyAgreement: types.NewThirdPartyDataPrivacyAgreement(thirdPartyDataPrivacyAgreement),
}, nil
}
@@ -470,7 +469,7 @@ func (r *mutationResolver) UpdateThirdPartyDataPrivacyAgreement(ctx context.Cont
return nil, err
}
thirdPartyDataPrivacyAgreement, file, err := r.probo.ThirdPartyDataPrivacyAgreements.Update(
thirdPartyDataPrivacyAgreement, _, err := r.probo.ThirdPartyDataPrivacyAgreements.Update(
ctx, scope,
input.ThirdPartyID,
&probo.ThirdPartyDataPrivacyAgreementUpdateRequest{
@@ -489,7 +488,7 @@ func (r *mutationResolver) UpdateThirdPartyDataPrivacyAgreement(ctx context.Cont
}
return &types.UpdateThirdPartyDataPrivacyAgreementPayload{
ThirdPartyDataPrivacyAgreement: types.NewThirdPartyDataPrivacyAgreement(thirdPartyDataPrivacyAgreement, file),
ThirdPartyDataPrivacyAgreement: types.NewThirdPartyDataPrivacyAgreement(thirdPartyDataPrivacyAgreement),
}, nil
}
@@ -667,7 +666,7 @@ func (r *thirdPartyResolver) BusinessAssociateAgreement(ctx context.Context, obj
return nil, err
}
thirdPartyBusinessAssociateAgreement, file, err := r.probo.ThirdPartyBusinessAssociateAgreements.GetByThirdPartyID(ctx, scope, obj.ID)
thirdPartyBusinessAssociateAgreement, _, err := r.probo.ThirdPartyBusinessAssociateAgreements.GetByThirdPartyID(ctx, scope, obj.ID)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, nil
@@ -678,7 +677,7 @@ func (r *thirdPartyResolver) BusinessAssociateAgreement(ctx context.Context, obj
return nil, gqlutils.Internal(ctx)
}
return types.NewThirdPartyBusinessAssociateAgreement(thirdPartyBusinessAssociateAgreement, file), nil
return types.NewThirdPartyBusinessAssociateAgreement(thirdPartyBusinessAssociateAgreement), nil
}
// DataPrivacyAgreement is the resolver for the dataPrivacyAgreement field.
@@ -688,7 +687,7 @@ func (r *thirdPartyResolver) DataPrivacyAgreement(ctx context.Context, obj *type
return nil, err
}
thirdPartyDataPrivacyAgreement, file, err := r.probo.ThirdPartyDataPrivacyAgreements.GetByThirdPartyID(ctx, scope, obj.ID)
thirdPartyDataPrivacyAgreement, _, err := r.probo.ThirdPartyDataPrivacyAgreements.GetByThirdPartyID(ctx, scope, obj.ID)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, nil
@@ -699,7 +698,7 @@ func (r *thirdPartyResolver) DataPrivacyAgreement(ctx context.Context, obj *type
return nil, gqlutils.Internal(ctx)
}
return types.NewThirdPartyDataPrivacyAgreement(thirdPartyDataPrivacyAgreement, file), nil
return types.NewThirdPartyDataPrivacyAgreement(thirdPartyDataPrivacyAgreement), nil
}
// Contacts is the resolver for the contacts field.
@@ -994,20 +993,13 @@ func (r *thirdPartyBusinessAssociateAgreementResolver) ThirdParty(ctx context.Co
return types.NewThirdParty(thirdParty), nil
}
// FileURL is the resolver for the fileUrl field.
func (r *thirdPartyBusinessAssociateAgreementResolver) FileURL(ctx context.Context, obj *types.ThirdPartyBusinessAssociateAgreement) (string, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionFileGet)
if err != nil {
return "", err
// File is the resolver for the file field.
func (r *thirdPartyBusinessAssociateAgreementResolver) File(ctx context.Context, obj *types.ThirdPartyBusinessAssociateAgreement) (*types.File, error) {
if _, err := r.authorize(ctx, obj.ID, probo.ActionFileGet); err != nil {
return nil, err
}
fileURL, err := r.probo.ThirdPartyBusinessAssociateAgreements.GenerateFileURL(ctx, scope, obj.ID, 1*time.Hour)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate file URL", log.Error(err))
return "", gqlutils.Internal(ctx)
}
return fileURL, nil
return r.loadFile(ctx, obj.File.ID)
}
// Permission is the resolver for the permission field.
@@ -1185,20 +1177,13 @@ func (r *thirdPartyDataPrivacyAgreementResolver) ThirdParty(ctx context.Context,
return types.NewThirdParty(thirdParty), nil
}
// FileURL is the resolver for the fileUrl field.
func (r *thirdPartyDataPrivacyAgreementResolver) FileURL(ctx context.Context, obj *types.ThirdPartyDataPrivacyAgreement) (string, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionFileGet)
if err != nil {
return "", err
// File is the resolver for the file field.
func (r *thirdPartyDataPrivacyAgreementResolver) File(ctx context.Context, obj *types.ThirdPartyDataPrivacyAgreement) (*types.File, error) {
if _, err := r.authorize(ctx, obj.ID, probo.ActionFileGet); err != nil {
return nil, err
}
fileURL, err := r.probo.ThirdPartyDataPrivacyAgreements.GenerateFileURL(ctx, scope, obj.ID, 1*time.Hour)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate file URL", log.Error(err))
return "", gqlutils.Internal(ctx)
}
return fileURL, nil
return r.loadFile(ctx, obj.File.ID)
}
// Permission is the resolver for the permission field.

View File

@@ -9,7 +9,6 @@ import (
"context"
"errors"
"fmt"
"time"
"github.com/vikstrous/dataloadgen"
"go.gearno.de/kit/log"
@@ -63,7 +62,7 @@ func (r *mutationResolver) UpdateTrustCenter(ctx context.Context, input types.Up
return nil, err
}
trustCenter, file, err := r.probo.TrustCenters.Update(
trustCenter, _, err := r.probo.TrustCenters.Update(
ctx, scope,
&probo.UpdateTrustCenterRequest{
ID: input.TrustCenterID,
@@ -82,7 +81,7 @@ func (r *mutationResolver) UpdateTrustCenter(ctx context.Context, input types.Up
}
return &types.UpdateTrustCenterPayload{
TrustCenter: types.NewTrustCenter(trustCenter, file),
TrustCenter: types.NewTrustCenter(trustCenter),
}, nil
}
@@ -93,7 +92,7 @@ func (r *mutationResolver) UploadTrustCenterNda(ctx context.Context, input types
return nil, err
}
trustCenter, file, err := r.probo.TrustCenters.UploadNDA(
trustCenter, _, err := r.probo.TrustCenters.UploadNDA(
ctx, scope,
&probo.UploadTrustCenterNDARequest{
TrustCenterID: input.TrustCenterID,
@@ -112,7 +111,7 @@ func (r *mutationResolver) UploadTrustCenterNda(ctx context.Context, input types
}
return &types.UploadTrustCenterNDAPayload{
TrustCenter: types.NewTrustCenter(trustCenter, file),
TrustCenter: types.NewTrustCenter(trustCenter),
}, nil
}
@@ -123,14 +122,14 @@ func (r *mutationResolver) DeleteTrustCenterNda(ctx context.Context, input types
return nil, err
}
trustCenter, file, err := r.probo.TrustCenters.DeleteNDA(ctx, scope, input.TrustCenterID)
trustCenter, _, err := r.probo.TrustCenters.DeleteNDA(ctx, scope, input.TrustCenterID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete trust center NDA", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteTrustCenterNDAPayload{
TrustCenter: types.NewTrustCenter(trustCenter, file),
TrustCenter: types.NewTrustCenter(trustCenter),
}, nil
}
@@ -179,7 +178,7 @@ func (r *mutationResolver) UpdateTrustCenterBrand(ctx context.Context, input typ
}
}
trustCenter, file, err := r.probo.TrustCenters.UpdateTrustCenterBrand(ctx, scope, req)
trustCenter, _, err := r.probo.TrustCenters.UpdateTrustCenterBrand(ctx, scope, req)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
@@ -191,7 +190,7 @@ func (r *mutationResolver) UpdateTrustCenterBrand(ctx context.Context, input typ
}
return &types.UpdateTrustCenterBrandPayload{
TrustCenter: types.NewTrustCenter(trustCenter, file),
TrustCenter: types.NewTrustCenter(trustCenter),
}, nil
}
@@ -689,59 +688,45 @@ func (r *mutationResolver) DeleteCustomDomain(ctx context.Context, input types.D
}, nil
}
// LogoFileURL is the resolver for the logoFileUrl field.
func (r *trustCenterResolver) LogoFileURL(ctx context.Context, obj *types.TrustCenter) (*string, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterGet)
if err != nil {
// Logo is the resolver for the logo field.
func (r *trustCenterResolver) Logo(ctx context.Context, obj *types.TrustCenter) (*types.File, error) {
if _, err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterGet); err != nil {
return nil, err
}
logoURL, err := r.probo.TrustCenters.GenerateLogoURL(ctx, scope, obj.ID, 1*time.Hour)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate logo url", log.Error(err))
return nil, gqlutils.Internal(ctx)
if obj.Logo == nil {
return nil, nil
}
return logoURL, nil
return r.loadFile(ctx, obj.Logo.ID)
}
// DarkLogoFileURL is the resolver for the darkLogoFileUrl field.
func (r *trustCenterResolver) DarkLogoFileURL(ctx context.Context, obj *types.TrustCenter) (*string, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterGet)
if err != nil {
// DarkLogo is the resolver for the darkLogo field.
func (r *trustCenterResolver) DarkLogo(ctx context.Context, obj *types.TrustCenter) (*types.File, error) {
if _, err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterGet); err != nil {
return nil, err
}
logoURL, err := r.probo.TrustCenters.GenerateDarkLogoURL(ctx, scope, obj.ID, 1*time.Hour)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate logo url", log.Error(err))
return nil, gqlutils.Internal(ctx)
if obj.DarkLogo == nil {
return nil, nil
}
return logoURL, nil
return r.loadFile(ctx, obj.DarkLogo.ID)
}
// NdaFileURL is the resolver for the ndaFileUrl field.
func (r *trustCenterResolver) NdaFileURL(ctx context.Context, obj *types.TrustCenter) (*string, error) {
// Nda is the resolver for the nda field.
func (r *trustCenterResolver) Nda(ctx context.Context, obj *types.TrustCenter) (*types.File, error) {
hasPermission, err := r.Resolver.Permission(ctx, obj, probo.ActionTrustCenterGetNda)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot authorize", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if !hasPermission {
if !hasPermission || obj.Nda == nil {
return nil, nil
}
scope := coredata.NewScopeFromObjectID(obj.ID)
fileURL, err := r.probo.TrustCenters.GenerateNDAFileURL(ctx, scope, obj.ID, 15*time.Minute)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate NDA file URL", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return fileURL, nil
return r.loadFile(ctx, obj.Nda.ID)
}
// Organization is the resolver for the organization field.
@@ -1143,20 +1128,13 @@ func (r *trustCenterDocumentAccessConnectionResolver) TotalCount(ctx context.Con
return count, nil
}
// FileURL is the resolver for the fileUrl field.
func (r *trustCenterFileResolver) FileURL(ctx context.Context, obj *types.TrustCenterFile) (string, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterFileGetFileUrl)
if err != nil {
return "", err
// File is the resolver for the file field.
func (r *trustCenterFileResolver) File(ctx context.Context, obj *types.TrustCenterFile) (*types.File, error) {
if _, err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterFileGetFileUrl); err != nil {
return nil, err
}
fileURL, err := r.probo.TrustCenterFiles.GenerateFileURL(ctx, scope, obj.ID, 1*time.Hour)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate file URL", log.Error(err))
return "", gqlutils.Internal(ctx)
}
return fileURL, nil
return r.loadFile(ctx, obj.File.ID)
}
// Organization is the resolver for the organization field.
@@ -1207,20 +1185,13 @@ func (r *trustCenterFileConnectionResolver) TotalCount(ctx context.Context, obj
return count, nil
}
// LogoURL is the resolver for the logoUrl field.
func (r *trustCenterReferenceResolver) LogoURL(ctx context.Context, obj *types.TrustCenterReference) (string, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterReferenceGetLogoUrl)
if err != nil {
return "", err
// Logo is the resolver for the logo field.
func (r *trustCenterReferenceResolver) Logo(ctx context.Context, obj *types.TrustCenterReference) (*types.File, error) {
if _, err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterReferenceGetLogoUrl); err != nil {
return nil, err
}
fileURL, err := r.probo.TrustCenterReferences.GenerateLogoURL(ctx, scope, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate logo URL", log.Error(err))
return "", gqlutils.Internal(ctx)
}
return fileURL, nil
return r.loadFile(ctx, obj.Logo.ID)
}
// Permission is the resolver for the permission field.

View File

@@ -34,13 +34,13 @@ type CommonThirdParty struct {
TrustPageURL *string `json:"trustPageUrl,omitempty"`
StatusPageURL *string `json:"statusPageUrl,omitempty"`
TermsOfServiceURL *string `json:"termsOfServiceUrl,omitempty"`
LogoFileID *gid.GID `json:"logoFileId,omitempty"`
Logo *File `json:"logo,omitempty"`
}
func (CommonThirdParty) IsTrackerPatternThirdPartyLink() {}
func NewCommonThirdParty(c *coredata.CommonThirdParty) *CommonThirdParty {
return &CommonThirdParty{
party := &CommonThirdParty{
ID: c.ID,
Name: c.Name,
Category: c.Category,
@@ -55,6 +55,11 @@ func NewCommonThirdParty(c *coredata.CommonThirdParty) *CommonThirdParty {
TrustPageURL: c.TrustPageURL,
StatusPageURL: c.StatusPageURL,
TermsOfServiceURL: c.TermsOfServiceURL,
LogoFileID: c.LogoFileID,
}
if c.LogoFileID != nil {
party.Logo = &File{ID: *c.LogoFileID}
}
return party
}

View File

@@ -19,7 +19,7 @@ import (
)
func NewElectronicSignature(es *coredata.ElectronicSignature) *ElectronicSignature {
return &ElectronicSignature{
signature := &ElectronicSignature{
ID: es.ID,
Status: es.Status,
DocumentType: es.DocumentType,
@@ -29,6 +29,12 @@ func NewElectronicSignature(es *coredata.ElectronicSignature) *ElectronicSignatu
CreatedAt: es.CreatedAt,
UpdatedAt: es.UpdatedAt,
}
if es.CertificateFileID != nil {
signature.Certificate = &File{ID: *es.CertificateFileID}
}
return signature
}
func NewElectronicSignatureEvent(ev *coredata.ElectronicSignatureEvent) *ElectronicSignatureEvent {

View File

@@ -17,17 +17,11 @@ package types
import (
"go.probo.inc/probo/pkg/baseurl"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/filemanager"
)
func NewFile(r *coredata.File, base *baseurl.BaseURL) *File {
var path string
if r.Visibility == coredata.FileVisibilityPublic {
path = "/api/files/v1/public/" + r.ID.String()
} else {
path = "/api/files/v1/" + r.ID.String()
}
url := base.WithPath(path).MustString()
url := base.WithPath(filemanager.DownloadAPIPath(r)).MustString()
return &File{
ID: r.ID,

View File

@@ -61,7 +61,7 @@ func NewFrameworkEdge(f *coredata.Framework, orderBy coredata.FrameworkOrderFiel
}
func NewFramework(f *coredata.Framework) *Framework {
return &Framework{
framework := &Framework{
ID: f.ID,
Name: f.Name,
Organization: &Organization{
@@ -71,4 +71,14 @@ func NewFramework(f *coredata.Framework) *Framework {
CreatedAt: f.CreatedAt,
UpdatedAt: f.UpdatedAt,
}
if f.LightLogoFileID != nil {
framework.LightLogo = &File{ID: *f.LightLogoFileID}
}
if f.DarkLogoFileID != nil {
framework.DarkLogo = &File{ID: *f.DarkLogoFileID}
}
return framework
}

View File

@@ -34,6 +34,14 @@ func NewOrganization(o *coredata.Organization) *Organization {
UpdatedAt: o.UpdatedAt,
}
if o.LogoFileID != nil {
org.Logo = &File{ID: *o.LogoFileID}
}
if o.HorizontalLogoFileID != nil {
org.HorizontalLogo = &File{ID: *o.HorizontalLogoFileID}
}
if o.CustomDomainID != nil {
org.CustomDomain = &CustomDomain{
ID: *o.CustomDomainID,

View File

@@ -18,7 +18,7 @@ import (
"go.probo.inc/probo/pkg/coredata"
)
func NewThirdPartyBusinessAssociateAgreement(v *coredata.ThirdPartyBusinessAssociateAgreement, file *coredata.File) *ThirdPartyBusinessAssociateAgreement {
func NewThirdPartyBusinessAssociateAgreement(v *coredata.ThirdPartyBusinessAssociateAgreement) *ThirdPartyBusinessAssociateAgreement {
return &ThirdPartyBusinessAssociateAgreement{
ID: v.ID,
ThirdParty: &ThirdParty{
@@ -26,8 +26,7 @@ func NewThirdPartyBusinessAssociateAgreement(v *coredata.ThirdPartyBusinessAssoc
},
ValidFrom: v.ValidFrom,
ValidUntil: v.ValidUntil,
FileName: file.FileName,
FileSize: file.FileSize,
File: &File{ID: v.FileID},
CreatedAt: v.CreatedAt,
UpdatedAt: v.UpdatedAt,
}

View File

@@ -18,7 +18,7 @@ import (
"go.probo.inc/probo/pkg/coredata"
)
func NewThirdPartyDataPrivacyAgreement(v *coredata.ThirdPartyDataPrivacyAgreement, file *coredata.File) *ThirdPartyDataPrivacyAgreement {
func NewThirdPartyDataPrivacyAgreement(v *coredata.ThirdPartyDataPrivacyAgreement) *ThirdPartyDataPrivacyAgreement {
return &ThirdPartyDataPrivacyAgreement{
ID: v.ID,
ThirdParty: &ThirdParty{
@@ -26,8 +26,7 @@ func NewThirdPartyDataPrivacyAgreement(v *coredata.ThirdPartyDataPrivacyAgreemen
},
ValidFrom: v.ValidFrom,
ValidUntil: v.ValidUntil,
FileName: file.FileName,
FileSize: file.FileSize,
File: &File{ID: v.FileID},
CreatedAt: v.CreatedAt,
UpdatedAt: v.UpdatedAt,
}

View File

@@ -25,10 +25,9 @@ type TrustCenter struct {
ID gid.GID `json:"id"`
Active bool `json:"active"`
SearchEngineIndexing coredata.SearchEngineIndexing `json:"searchEngineIndexing"`
LogoFileURL *string `json:"logoFileUrl,omitempty"`
DarkLogoFileURL *string `json:"darkLogoFileUrl,omitempty"`
NdaFileName *string `json:"ndaFileName,omitempty"`
NdaFileURL *string `json:"ndaFileUrl,omitempty"`
Logo *File `json:"logo,omitempty"`
DarkLogo *File `json:"darkLogo,omitempty"`
Nda *File `json:"nda,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
Organization *Organization `json:"organization"`
@@ -43,21 +42,29 @@ type TrustCenter struct {
func (TrustCenter) IsNode() {}
func (t TrustCenter) GetID() gid.GID { return t.ID }
func NewTrustCenter(tc *coredata.TrustCenter, file *coredata.File) *TrustCenter {
var ndaFileName *string
if file != nil {
ndaFileName = &file.FileName
}
return &TrustCenter{
func NewTrustCenter(tc *coredata.TrustCenter) *TrustCenter {
trustCenter := &TrustCenter{
ID: tc.ID,
Organization: &Organization{
ID: tc.OrganizationID,
},
Active: tc.Active,
SearchEngineIndexing: tc.SearchEngineIndexing,
NdaFileName: ndaFileName,
CreatedAt: tc.CreatedAt,
UpdatedAt: tc.UpdatedAt,
}
if tc.LogoFileID != nil {
trustCenter.Logo = &File{ID: *tc.LogoFileID}
}
if tc.DarkLogoFileID != nil {
trustCenter.DarkLogo = &File{ID: *tc.DarkLogoFileID}
}
if tc.NonDisclosureAgreementFileID != nil {
trustCenter.Nda = &File{ID: *tc.NonDisclosureAgreementFileID}
}
return trustCenter
}

View File

@@ -34,6 +34,7 @@ func NewTrustCenterFile(tcf *coredata.TrustCenterFile) *TrustCenterFile {
ID: tcf.ID,
Name: tcf.Name,
Category: tcf.Category,
File: &File{ID: tcf.FileID},
TrustCenterVisibility: tcf.TrustCenterVisibility,
CreatedAt: tcf.CreatedAt,
UpdatedAt: tcf.UpdatedAt,

View File

@@ -35,6 +35,7 @@ func NewTrustCenterReference(tcc *coredata.TrustCenterReference) *TrustCenterRef
Name: tcc.Name,
Description: tcc.Description,
WebsiteURL: tcc.WebsiteURL,
Logo: &File{ID: tcc.LogoFileID},
Rank: tcc.Rank,
CreatedAt: tcc.CreatedAt,
UpdatedAt: tcc.UpdatedAt,