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 <bryan@probo.com>
This commit is contained in:
Bryan Frimin
2026-06-22 08:39:47 +02:00
parent 325465ab41
commit c50aa28364
8 changed files with 195 additions and 0 deletions

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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"

View File

@@ -173,6 +173,7 @@ type Audit implements Node {
): FindingConnection @goField(forceResolver: true)
trustCenterVisibility: TrustCenterVisibility!
alias: String @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!

View File

@@ -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(

View File

@@ -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!
}

View File

@@ -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 }

View File

@@ -0,0 +1,34 @@
// Copyright (c) 2025-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 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,
}
}