Build the public Updates pages in the compliance portal: a cursor-paginated list of sent mailing-list updates and a detail view for a single update, replacing the previous stub page. Add a MailingListUpdate case to the trust API node resolver, guarded so only SENT updates belonging to the current trust center's mailing list are exposed, so the detail page can load an update by URL. Add a Prev/Next Pagination primitive to the v2 UI kit. Page numbers are omitted because cursor pagination cannot derive an ordinal page index; each arrow only shows when its page exists while keeping its slot reserved so a visible arrow never shifts position. Relocate the shared MailingListUpdateListItem to its own component folder and wrap each row in a link to the detail page, so both the home recent-updates section and the list navigate to detail. Signed-off-by: Émile Ré <emile@probo.com>
317 lines
9.7 KiB
Go
317 lines
9.7 KiB
Go
package trust_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.93
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
|
|
"go.gearno.de/kit/log"
|
|
"go.probo.inc/probo/pkg/coredata"
|
|
"go.probo.inc/probo/pkg/gid"
|
|
"go.probo.inc/probo/pkg/mailman"
|
|
"go.probo.inc/probo/pkg/server/api/authn"
|
|
"go.probo.inc/probo/pkg/server/api/compliancepage"
|
|
"go.probo.inc/probo/pkg/server/api/trust/v1/schema"
|
|
"go.probo.inc/probo/pkg/server/api/trust/v1/types"
|
|
"go.probo.inc/probo/pkg/server/gqlutils"
|
|
"go.probo.inc/probo/pkg/trust"
|
|
)
|
|
|
|
// Viewer is the resolver for the viewer field.
|
|
func (r *queryResolver) Viewer(ctx context.Context) (*types.Identity, error) {
|
|
identity := authn.IdentityFromContext(ctx)
|
|
|
|
if identity == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
return &types.Identity{
|
|
ID: identity.ID,
|
|
Email: identity.EmailAddress,
|
|
FullName: identity.FullName,
|
|
EmailVerified: identity.EmailAddressVerified,
|
|
CreatedAt: identity.CreatedAt,
|
|
UpdatedAt: identity.UpdatedAt,
|
|
}, nil
|
|
}
|
|
|
|
// Node is the resolver for the node field.
|
|
func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error) {
|
|
compliancePage := compliancepage.CompliancePageFromContext(ctx)
|
|
scope := coredata.NewScopeFromObjectID(compliancePage.OrganizationID)
|
|
trustService := r.trust
|
|
|
|
switch id.EntityType() {
|
|
case coredata.OrganizationEntityType:
|
|
organization, err := trustService.Organizations.Get(ctx, scope, id)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewOrganization(organization), nil
|
|
|
|
case coredata.DocumentEntityType:
|
|
document, err := trustService.Documents.Get(ctx, scope, compliancePage.OrganizationID, id)
|
|
if err != nil {
|
|
if errors.Is(err, trust.ErrDocumentNotFound) || errors.Is(err, trust.ErrDocumentNotVisible) || errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
|
}
|
|
|
|
if _, ok := errors.AsType[*trust.ErrDocumentArchived](err); ok {
|
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get document", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewDocument(document), nil
|
|
|
|
case coredata.FrameworkEntityType:
|
|
framework, err := trustService.Frameworks.Get(ctx, scope, id)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get framework", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewFramework(framework), nil
|
|
|
|
case coredata.FileEntityType:
|
|
file, err := trustService.Reports.Get(ctx, scope, compliancePage.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 audit report file", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
audit, err := trustService.Audits.GetByReportFileID(ctx, scope, id)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get audit for report file", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
if audit.TrustCenterVisibility == coredata.TrustCenterVisibilityNone {
|
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
|
}
|
|
|
|
return types.NewAuditReport(file), nil
|
|
|
|
case coredata.AuditEntityType:
|
|
audit, err := trustService.Audits.Get(ctx, scope, id)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get audit", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
if audit.TrustCenterVisibility == coredata.TrustCenterVisibilityNone {
|
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
|
}
|
|
|
|
return types.NewAudit(audit), nil
|
|
|
|
case coredata.ThirdPartyEntityType:
|
|
thirdParty, err := trustService.ThirdParties.Get(ctx, scope, id)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get thirdParty", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
if !thirdParty.ShowOnTrustCenter {
|
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
|
}
|
|
|
|
return types.NewSubprocessor(thirdParty), nil
|
|
|
|
case coredata.TrustCenterEntityType:
|
|
trustCenter, err := trustService.TrustCenters.Get(ctx, scope, id)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get trust center", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewTrustCenter(trustCenter), nil
|
|
|
|
case coredata.TrustCenterReferenceEntityType:
|
|
reference, err := trustService.TrustCenterReferences.Get(ctx, scope, id)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get trust center reference", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewTrustCenterReference(reference), nil
|
|
|
|
case coredata.TrustCenterFileEntityType:
|
|
trustCenterFile, err := trustService.TrustCenterFiles.Get(ctx, scope, compliancePage.OrganizationID, id)
|
|
if err != nil {
|
|
if errors.Is(err, trust.ErrTrustCenterFileNotFound) || errors.Is(err, trust.ErrTrustCenterFileNotVisible) {
|
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get trust center file", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewTrustCenterFile(trustCenterFile), nil
|
|
|
|
case coredata.MailingListUpdateEntityType:
|
|
update, err := r.mailman.GetMailingListUpdate(ctx, id)
|
|
if err != nil {
|
|
if errors.Is(err, mailman.ErrMailingListUpdateNotFound) || errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get mailing list update", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
if update.Status != coredata.MailingListUpdateStatusSent {
|
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
|
}
|
|
|
|
trustCenter, err := trustService.TrustCenters.Get(ctx, scope, compliancePage.ID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot get trust center", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
if trustCenter.MailingListID == nil || *trustCenter.MailingListID != update.MailingListID {
|
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
|
}
|
|
|
|
return types.NewMailingListUpdate(update), nil
|
|
|
|
default:
|
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
|
|
}
|
|
}
|
|
|
|
// AliasedNode is the resolver for the aliasedNode field.
|
|
func (r *queryResolver) AliasedNode(ctx context.Context, alias string) (types.Node, error) {
|
|
resourceID, err := gid.ParseGID(alias)
|
|
if err != nil {
|
|
compliancePage := compliancepage.CompliancePageFromContext(ctx)
|
|
scope := coredata.NewScopeFromObjectID(compliancePage.OrganizationID)
|
|
|
|
resourceID, err = r.resourceAlias.ResolveAlias(
|
|
ctx,
|
|
scope,
|
|
alias,
|
|
)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, gqlutils.NotFoundf(ctx, "node %q not found", alias)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot resolve resource alias", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
}
|
|
|
|
return r.Node(ctx, resourceID)
|
|
}
|
|
|
|
// CurrentTrustCenter is the resolver for the currentTrustCenter field.
|
|
func (r *queryResolver) CurrentTrustCenter(ctx context.Context) (*types.TrustCenter, error) {
|
|
compliancePage := compliancepage.CompliancePageFromContext(ctx)
|
|
|
|
scope := coredata.NewScopeFromObjectID(compliancePage.OrganizationID)
|
|
trustService := r.trust
|
|
|
|
org, err := trustService.Organizations.Get(ctx, scope, compliancePage.OrganizationID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
trustCenter, err := trustService.TrustCenters.Get(ctx, scope, compliancePage.ID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot get trust center", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
response := types.NewTrustCenter(trustCenter)
|
|
response.Organization = types.NewOrganization(org)
|
|
|
|
return response, nil
|
|
}
|
|
|
|
// OidcProviders is the resolver for the oidcProviders field.
|
|
func (r *queryResolver) OidcProviders(ctx context.Context) ([]*types.OIDCProviderInfo, error) {
|
|
providers := r.iam.OIDCService.EnabledProviders()
|
|
result := make([]*types.OIDCProviderInfo, 0, len(providers))
|
|
|
|
for _, p := range providers {
|
|
name := strings.ToLower(p.String())
|
|
result = append(
|
|
result,
|
|
&types.OIDCProviderInfo{
|
|
Name: name,
|
|
LoginURL: r.baseURL.WithPath("/api/connect/v1/oidc/" + name + "/login").MustString(),
|
|
},
|
|
)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// Mutation returns schema.MutationResolver implementation.
|
|
func (r *Resolver) Mutation() schema.MutationResolver { return &mutationResolver{r} }
|
|
|
|
// Query returns schema.QueryResolver implementation.
|
|
func (r *Resolver) Query() schema.QueryResolver { return &queryResolver{r} }
|
|
|
|
type (
|
|
mutationResolver struct{ *Resolver }
|
|
queryResolver struct{ *Resolver }
|
|
)
|