From c50aa28364f3cb7e474c472b0eda180db985d779 Mon Sep 17 00:00:00 2001 From: Bryan Frimin Date: Mon, 22 Jun 2026 08:39:47 +0200 Subject: [PATCH] Expose alias field and set/remove mutations in console API Adds an alias field to Document, Audit, and TrustCenterFile types. Introduces setTrustCenterAlias and removeTrustCenterAlias mutations with proper authorization and error handling. Signed-off-by: Bryan Frimin --- pkg/server/api/console/v1/audit_resolvers.go | 10 ++ .../api/console/v1/document_resolvers.go | 10 ++ pkg/server/api/console/v1/gqlgen.yaml | 3 + .../api/console/v1/graphql/audit.graphql | 1 + .../api/console/v1/graphql/document.graphql | 1 + .../console/v1/graphql/trust_center.graphql | 30 +++++ .../api/console/v1/trust_center_resolvers.go | 106 ++++++++++++++++++ .../console/v1/types/trust_center_alias.go | 34 ++++++ 8 files changed, 195 insertions(+) create mode 100644 pkg/server/api/console/v1/types/trust_center_alias.go diff --git a/pkg/server/api/console/v1/audit_resolvers.go b/pkg/server/api/console/v1/audit_resolvers.go index 1fae3fb8c..55ec3f879 100644 --- a/pkg/server/api/console/v1/audit_resolvers.go +++ b/pkg/server/api/console/v1/audit_resolvers.go @@ -173,6 +173,16 @@ func (r *auditResolver) Findings(ctx context.Context, obj *types.Audit, first *i return types.NewFindingConnection(p, r, obj.ID, filter), nil } +// Alias is the resolver for the alias field. +func (r *auditResolver) Alias(ctx context.Context, obj *types.Audit) (*string, error) { + scope, err := r.authorize(ctx, obj.ID, probo.ActionAuditGet) + if err != nil { + return nil, err + } + + return r.probo.TrustCenterAliases.GetByResourceID(ctx, scope, obj.ID) +} + // 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) diff --git a/pkg/server/api/console/v1/document_resolvers.go b/pkg/server/api/console/v1/document_resolvers.go index 610fece43..2958295c8 100644 --- a/pkg/server/api/console/v1/document_resolvers.go +++ b/pkg/server/api/console/v1/document_resolvers.go @@ -27,6 +27,16 @@ import ( "go.probo.inc/probo/pkg/validator" ) +// Alias is the resolver for the alias field. +func (r *documentResolver) Alias(ctx context.Context, obj *types.Document) (*string, error) { + scope, err := r.authorize(ctx, obj.ID, probo.ActionDocumentGet) + if err != nil { + return nil, err + } + + return r.probo.TrustCenterAliases.GetByResourceID(ctx, scope, obj.ID) +} + // Organization is the resolver for the organization field. func (r *documentResolver) Organization(ctx context.Context, obj *types.Document) (*types.Organization, error) { if _, err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil { diff --git a/pkg/server/api/console/v1/gqlgen.yaml b/pkg/server/api/console/v1/gqlgen.yaml index a95ec7673..e2f5e7d9a 100644 --- a/pkg/server/api/console/v1/gqlgen.yaml +++ b/pkg/server/api/console/v1/gqlgen.yaml @@ -41,3 +41,6 @@ models: EmailAddr: model: - "go.probo.inc/probo/pkg/server/gqlutils/types/mail.AddrScalar" + TrustCenterAlias: + model: + - "go.probo.inc/probo/pkg/server/api/console/v1/types.TrustCenterAlias" diff --git a/pkg/server/api/console/v1/graphql/audit.graphql b/pkg/server/api/console/v1/graphql/audit.graphql index ad49a3000..25f414d41 100644 --- a/pkg/server/api/console/v1/graphql/audit.graphql +++ b/pkg/server/api/console/v1/graphql/audit.graphql @@ -173,6 +173,7 @@ type Audit implements Node { ): FindingConnection @goField(forceResolver: true) trustCenterVisibility: TrustCenterVisibility! + alias: String @goField(forceResolver: true) createdAt: Datetime! updatedAt: Datetime! diff --git a/pkg/server/api/console/v1/graphql/document.graphql b/pkg/server/api/console/v1/graphql/document.graphql index 988eacfdb..88a9d4f69 100644 --- a/pkg/server/api/console/v1/graphql/document.graphql +++ b/pkg/server/api/console/v1/graphql/document.graphql @@ -269,6 +269,7 @@ type Document implements Node { currentPublishedMajor: Int currentPublishedMinor: Int trustCenterVisibility: TrustCenterVisibility! + alias: String @goField(forceResolver: true) organization: Organization! @goField(forceResolver: true) versions( diff --git a/pkg/server/api/console/v1/graphql/trust_center.graphql b/pkg/server/api/console/v1/graphql/trust_center.graphql index fd66895be..2b24a5213 100644 --- a/pkg/server/api/console/v1/graphql/trust_center.graphql +++ b/pkg/server/api/console/v1/graphql/trust_center.graphql @@ -444,6 +444,7 @@ type TrustCenterFile implements Node { category: String! file: File! @goField(forceResolver: true) trustCenterVisibility: TrustCenterVisibility! + alias: String @goField(forceResolver: true) createdAt: Datetime! updatedAt: Datetime! organization: Organization! @goField(forceResolver: true) @@ -549,6 +550,12 @@ extend type Mutation { deleteCustomDomain( input: DeleteCustomDomainInput! ): DeleteCustomDomainPayload! + setTrustCenterAlias( + input: SetTrustCenterAliasInput! + ): SetTrustCenterAliasPayload! + removeTrustCenterAlias( + input: RemoveTrustCenterAliasInput! + ): RemoveTrustCenterAliasPayload! } input UpdateTrustCenterInput { @@ -758,3 +765,26 @@ type CreateCustomDomainPayload { type DeleteCustomDomainPayload { deletedCustomDomainId: ID! } + +input SetTrustCenterAliasInput { + resourceId: ID! + alias: String! +} + +input RemoveTrustCenterAliasInput { + resourceId: ID! +} + +type TrustCenterAlias { + resourceId: ID! + alias: String! + organization: Organization! @goField(forceResolver: true) +} + +type SetTrustCenterAliasPayload { + alias: TrustCenterAlias! +} + +type RemoveTrustCenterAliasPayload { + deletedResourceId: ID! +} diff --git a/pkg/server/api/console/v1/trust_center_resolvers.go b/pkg/server/api/console/v1/trust_center_resolvers.go index 2adabf7f7..abd867340 100644 --- a/pkg/server/api/console/v1/trust_center_resolvers.go +++ b/pkg/server/api/console/v1/trust_center_resolvers.go @@ -688,6 +688,75 @@ func (r *mutationResolver) DeleteCustomDomain(ctx context.Context, input types.D }, nil } +// SetTrustCenterAlias is the resolver for the setTrustCenterAlias field. +func (r *mutationResolver) SetTrustCenterAlias(ctx context.Context, input types.SetTrustCenterAliasInput) (*types.SetTrustCenterAliasPayload, error) { + scope, err := r.authorize(ctx, input.ResourceID, probo.ActionTrustCenterAliasSet) + if err != nil { + return nil, err + } + + alias, err := r.probo.TrustCenterAliases.Create( + ctx, + scope, + probo.CreateTrustCenterAliasRequest{ + ResourceID: input.ResourceID, + Alias: input.Alias, + }, + ) + if err != nil { + if errors.Is(err, coredata.ErrResourceAlreadyExists) { + return nil, gqlutils.Conflict(ctx, err) + } + + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { + return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) + } + + if _, ok := errors.AsType[*probo.ErrTrustCenterAliasAuditReportMissing](err); ok { + return nil, gqlutils.Invalid(ctx, err) + } + + if _, ok := errors.AsType[*probo.ErrTrustCenterAliasResourceInvalid](err); ok { + return nil, gqlutils.Invalid(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot set trust center alias", log.Error(err)) + + return nil, gqlutils.Internal(ctx) + } + + return &types.SetTrustCenterAliasPayload{ + Alias: types.NewTrustCenterAlias(input.ResourceID, alias), + }, nil +} + +// RemoveTrustCenterAlias is the resolver for the removeTrustCenterAlias field. +func (r *mutationResolver) RemoveTrustCenterAlias(ctx context.Context, input types.RemoveTrustCenterAliasInput) (*types.RemoveTrustCenterAliasPayload, error) { + scope, err := r.authorize(ctx, input.ResourceID, probo.ActionTrustCenterAliasRemove) + if err != nil { + return nil, err + } + + _, err = r.probo.TrustCenterAliases.Remove(ctx, scope, input.ResourceID) + if err != nil { + if _, ok := errors.AsType[*probo.ErrTrustCenterAliasAuditReportMissing](err); ok { + return nil, gqlutils.Invalid(ctx, err) + } + + if _, ok := errors.AsType[*probo.ErrTrustCenterAliasResourceInvalid](err); ok { + return nil, gqlutils.Invalid(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot remove trust center alias", log.Error(err)) + + return nil, gqlutils.Internal(ctx) + } + + return &types.RemoveTrustCenterAliasPayload{ + DeletedResourceID: input.ResourceID, + }, 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 { @@ -1016,6 +1085,27 @@ func (r *trustCenterAccessResolver) Permission(ctx context.Context, obj *types.T return r.Resolver.Permission(ctx, obj, action) } +// Organization is the resolver for the organization field. +func (r *trustCenterAliasResolver) Organization(ctx context.Context, obj *types.TrustCenterAlias) (*types.Organization, error) { + scope, err := r.authorize(ctx, obj.OrganizationID, probo.ActionOrganizationGet) + if err != nil { + return nil, err + } + + organization, err := r.probo.Organizations.Get(ctx, scope, obj.OrganizationID) + if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return nil, gqlutils.NotFound(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err)) + + return nil, gqlutils.Internal(ctx) + } + + return types.NewOrganization(organization), nil +} + // Document is the resolver for the document field. func (r *trustCenterDocumentAccessResolver) Document(ctx context.Context, obj *types.TrustCenterDocumentAccess) (*types.Document, error) { scope, err := r.authorize(ctx, obj.ID, probo.ActionDocumentGet) @@ -1137,6 +1227,16 @@ func (r *trustCenterFileResolver) File(ctx context.Context, obj *types.TrustCent return r.loadFile(ctx, obj.File.ID) } +// Alias is the resolver for the alias field. +func (r *trustCenterFileResolver) Alias(ctx context.Context, obj *types.TrustCenterFile) (*string, error) { + scope, err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterFileGet) + if err != nil { + return nil, err + } + + return r.probo.TrustCenterAliases.GetByResourceID(ctx, scope, obj.ID) +} + // Organization is the resolver for the organization field. func (r *trustCenterFileResolver) Organization(ctx context.Context, obj *types.TrustCenterFile) (*types.Organization, error) { scope, err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet) @@ -1236,6 +1336,11 @@ func (r *Resolver) TrustCenterAccess() schema.TrustCenterAccessResolver { return &trustCenterAccessResolver{r} } +// TrustCenterAlias returns schema.TrustCenterAliasResolver implementation. +func (r *Resolver) TrustCenterAlias() schema.TrustCenterAliasResolver { + return &trustCenterAliasResolver{r} +} + // TrustCenterDocumentAccess returns schema.TrustCenterDocumentAccessResolver implementation. func (r *Resolver) TrustCenterDocumentAccess() schema.TrustCenterDocumentAccessResolver { return &trustCenterDocumentAccessResolver{r} @@ -1271,6 +1376,7 @@ type complianceFrameworkResolver struct{ *Resolver } type customDomainResolver struct{ *Resolver } type trustCenterResolver struct{ *Resolver } type trustCenterAccessResolver struct{ *Resolver } +type trustCenterAliasResolver struct{ *Resolver } type trustCenterDocumentAccessResolver struct{ *Resolver } type trustCenterDocumentAccessConnectionResolver struct{ *Resolver } type trustCenterFileResolver struct{ *Resolver } diff --git a/pkg/server/api/console/v1/types/trust_center_alias.go b/pkg/server/api/console/v1/types/trust_center_alias.go new file mode 100644 index 000000000..8a298b2c4 --- /dev/null +++ b/pkg/server/api/console/v1/types/trust_center_alias.go @@ -0,0 +1,34 @@ +// Copyright (c) 2025-2026 Probo Inc . +// +// 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" + "go.probo.inc/probo/pkg/gid" +) + +type TrustCenterAlias struct { + ResourceID gid.GID `json:"resourceId"` + Alias string `json:"alias"` + OrganizationID gid.GID `json:"-"` +} + +func NewTrustCenterAlias(resourceID gid.GID, alias *coredata.TrustCenterAlias) *TrustCenterAlias { + return &TrustCenterAlias{ + ResourceID: resourceID, + Alias: alias.Alias, + OrganizationID: alias.OrganizationID, + } +}