diff --git a/apps/trust/src/pages/DocumentPage.tsx b/apps/trust/src/pages/DocumentPage.tsx index 8d6898886..bc147ba11 100644 --- a/apps/trust/src/pages/DocumentPage.tsx +++ b/apps/trust/src/pages/DocumentPage.tsx @@ -45,7 +45,7 @@ import type { DocumentPageRequestReportAccessMutation } from "./__generated__/Do import type { DocumentPageRequestTrustCenterFileAccessMutation } from "./__generated__/DocumentPageRequestTrustCenterFileAccessMutation.graphql"; export const documentPageQuery = graphql` - query DocumentPageQuery($id: String!) { + query DocumentPageQuery($alias: String!) { currentTrustCenter { logo { downloadUrl @@ -54,7 +54,7 @@ export const documentPageQuery = graphql` downloadUrl } } - node(id: $id) @required(action: THROW) { + aliasedNode(alias: $alias) @required(action: THROW) { __typename ... on Document { id @@ -183,7 +183,7 @@ function extractMimeType(dataUri: string): string { return match?.[1] ?? "application/octet-stream"; } -function getNodeTitle(node: DocumentPageQueryType["response"]["node"]): string | undefined { +function getNodeTitle(node: DocumentPageQueryType["response"]["aliasedNode"]): string | undefined { switch (node.__typename) { case "Document": return node.title; @@ -196,7 +196,7 @@ function getNodeTitle(node: DocumentPageQueryType["response"]["node"]): string | } } -function getNodeId(node: DocumentPageQueryType["response"]["node"]): string | undefined { +function getNodeId(node: DocumentPageQueryType["response"]["aliasedNode"]): string | undefined { switch (node.__typename) { case "Document": case "TrustCenterFile": @@ -220,7 +220,7 @@ export function DocumentPage({ queryRef }: Props) { const data = usePreloadedQuery(documentPageQuery, queryRef); const trustCenter = data.currentTrustCenter; - const node = data.node; + const node = data.aliasedNode; if ( node.__typename !== "Document" diff --git a/apps/trust/src/pages/DocumentPageLoader.tsx b/apps/trust/src/pages/DocumentPageLoader.tsx index f69a559f4..760a3104c 100644 --- a/apps/trust/src/pages/DocumentPageLoader.tsx +++ b/apps/trust/src/pages/DocumentPageLoader.tsx @@ -27,7 +27,7 @@ function DocumentPageQueryLoader() { useEffect(() => { if (documentId) { - loadQuery({ id: documentId }); + loadQuery({ alias: documentId }); } }, [documentId, loadQuery]); diff --git a/pkg/server/api/trust/v1/base_resolvers.go b/pkg/server/api/trust/v1/base_resolvers.go index 6aba6f962..9cb508646 100644 --- a/pkg/server/api/trust/v1/base_resolvers.go +++ b/pkg/server/api/trust/v1/base_resolvers.go @@ -18,6 +18,7 @@ import ( "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. @@ -39,8 +40,125 @@ func (r *queryResolver) Viewer(ctx context.Context) (*types.Identity, error) { } // Node is the resolver for the node field. -func (r *queryResolver) Node(ctx context.Context, id string) (types.Node, error) { - resourceID, err := gid.ParseGID(id) +func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error) { + scope := coredata.NewScopeFromObjectID(id) + trustService := r.trust + + switch id.EntityType() { + case coredata.OrganizationEntityType: + organization, err := trustService.Organizations.Get(ctx, scope, id) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewOrganization(organization), nil + + case coredata.DocumentEntityType: + trustCenter := compliancepage.CompliancePageFromContext(ctx) + + document, err := trustService.Documents.Get(ctx, scope, trustCenter.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 { + r.logger.ErrorCtx(ctx, "cannot get framework", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewFramework(framework), nil + + case coredata.FileEntityType: + trustCenter := compliancepage.CompliancePageFromContext(ctx) + + 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 audit report file", log.Error(err)) + + return nil, gqlutils.Internal(ctx) + } + + return types.NewAuditReport(file), nil + + case coredata.AuditEntityType: + audit, err := trustService.Audits.Get(ctx, scope, id) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot get audit", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewAudit(audit), nil + + case coredata.ThirdPartyEntityType: + thirdParty, err := trustService.ThirdParties.Get(ctx, scope, id) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot get thirdParty", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewSubprocessor(thirdParty), nil + + case coredata.TrustCenterEntityType: + trustCenter, err := trustService.TrustCenters.Get(ctx, scope, id) + if err != nil { + 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 { + 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: + trustCenter := compliancepage.CompliancePageFromContext(ctx) + + trustCenterFile, err := trustService.TrustCenterFiles.Get(ctx, scope, trustCenter.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 + + 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 { trustCenter := compliancepage.CompliancePageFromContext(ctx) scope := coredata.NewScopeFromObjectID(trustCenter.ID) @@ -48,11 +166,11 @@ func (r *queryResolver) Node(ctx context.Context, id string) (types.Node, error) resourceID, err = r.resourceAlias.ResolveAlias( ctx, scope, - id, + alias, ) if err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { - return nil, gqlutils.NotFoundf(ctx, "node %q not found", id) + return nil, gqlutils.NotFoundf(ctx, "node %q not found", alias) } r.logger.ErrorCtx(ctx, "cannot resolve resource alias", log.Error(err)) @@ -61,7 +179,7 @@ func (r *queryResolver) Node(ctx context.Context, id string) (types.Node, error) } } - return r.nodeByGID(ctx, resourceID, id) + return r.Node(ctx, resourceID) } // CurrentTrustCenter is the resolver for the currentTrustCenter field. @@ -96,10 +214,13 @@ func (r *queryResolver) OidcProviders(ctx context.Context) ([]*types.OIDCProvide 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(), - }) + result = append( + result, + &types.OIDCProviderInfo{ + Name: name, + LoginURL: r.baseURL.WithPath("/api/connect/v1/oidc/" + name + "/login").MustString(), + }, + ) } return result, nil diff --git a/pkg/server/api/trust/v1/graphql/base.graphql b/pkg/server/api/trust/v1/graphql/base.graphql index c69e94524..d5f6d09ae 100644 --- a/pkg/server/api/trust/v1/graphql/base.graphql +++ b/pkg/server/api/trust/v1/graphql/base.graphql @@ -24,7 +24,8 @@ interface Node { type Query { viewer: Identity - node(id: String!): Node + node(id: ID!): Node + aliasedNode(alias: String!): Node currentTrustCenter: TrustCenter oidcProviders: [OIDCProviderInfo!]! @goField(forceResolver: true) diff --git a/pkg/server/api/trust/v1/node_resolver.go b/pkg/server/api/trust/v1/node_resolver.go deleted file mode 100644 index 008b55964..000000000 --- a/pkg/server/api/trust/v1/node_resolver.go +++ /dev/null @@ -1,148 +0,0 @@ -// 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 trust_v1 - -import ( - "context" - "errors" - - "go.gearno.de/kit/log" - "go.probo.inc/probo/pkg/coredata" - "go.probo.inc/probo/pkg/gid" - "go.probo.inc/probo/pkg/server/api/compliancepage" - "go.probo.inc/probo/pkg/server/api/trust/v1/types" - "go.probo.inc/probo/pkg/server/gqlutils" - "go.probo.inc/probo/pkg/trust" -) - -func (r *queryResolver) nodeByGID( - ctx context.Context, - id gid.GID, - notFoundLabel string, -) (types.Node, error) { - scope := coredata.NewScopeFromObjectID(id) - trustService := r.trust - - switch id.EntityType() { - case coredata.OrganizationEntityType: - organization, err := trustService.Organizations.Get(ctx, scope, id) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewOrganization(organization), nil - - case coredata.DocumentEntityType: - trustCenter := compliancepage.CompliancePageFromContext(ctx) - - document, err := trustService.Documents.Get(ctx, scope, trustCenter.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", notFoundLabel) - } - - if _, ok := errors.AsType[*trust.ErrDocumentArchived](err); ok { - return nil, gqlutils.NotFoundf(ctx, "node %q not found", notFoundLabel) - } - - 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 { - r.logger.ErrorCtx(ctx, "cannot get framework", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewFramework(framework), nil - - case coredata.FileEntityType: - trustCenter := compliancepage.CompliancePageFromContext(ctx) - - 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", notFoundLabel) - } - - r.logger.ErrorCtx(ctx, "cannot get audit report file", log.Error(err)) - - return nil, gqlutils.Internal(ctx) - } - - return types.NewAuditReport(file), nil - - case coredata.AuditEntityType: - audit, err := trustService.Audits.Get(ctx, scope, id) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot get audit", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewAudit(audit), nil - - case coredata.ThirdPartyEntityType: - thirdParty, err := trustService.ThirdParties.Get(ctx, scope, id) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot get thirdParty", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewSubprocessor(thirdParty), nil - - case coredata.TrustCenterEntityType: - trustCenter, err := trustService.TrustCenters.Get(ctx, scope, id) - if err != nil { - 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 { - 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: - trustCenter := compliancepage.CompliancePageFromContext(ctx) - - trustCenterFile, err := trustService.TrustCenterFiles.Get(ctx, scope, trustCenter.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", notFoundLabel) - } - - r.logger.ErrorCtx(ctx, "cannot get trust center file", log.Error(err)) - - return nil, gqlutils.Internal(ctx) - } - - return types.NewTrustCenterFile(trustCenterFile), nil - - default: - return nil, gqlutils.NotFoundf(ctx, "node %q not found", notFoundLabel) - } -}