Move trust GraphQL API under complianceportal v1
Relocate the public trust center GraphQL surface, OAuth handlers, and SPA serving into the compliance portal API package and remove the legacy trust v1 server tree. Signed-off-by: Bryan Frimin <bryan@probo.com>
This commit is contained in:
231
pkg/server/api/complianceportal/v1/auth_resolvers.go
Normal file
231
pkg/server/api/complianceportal/v1/auth_resolvers.go
Normal file
@@ -0,0 +1,231 @@
|
||||
package complianceportal_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"
|
||||
|
||||
"go.gearno.de/kit/log"
|
||||
"go.probo.inc/probo/pkg/baseurl"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
"go.probo.inc/probo/pkg/saferedirect"
|
||||
"go.probo.inc/probo/pkg/server/api/authn"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal/v1/types"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
)
|
||||
|
||||
// SendMagicLink is the resolver for the sendMagicLink field.
|
||||
func (r *mutationResolver) SendMagicLink(ctx context.Context, input types.SendMagicLinkInput) (*types.SendMagicLinkPayload, error) {
|
||||
trustCenter := complianceportal.CompliancePageFromContext(ctx)
|
||||
|
||||
baseURL := complianceportal.CompliancePageBaseURLFromContext(ctx)
|
||||
|
||||
safeRedirect := saferedirect.New(saferedirect.StaticHosts(baseurl.MustParse(*baseURL).Host()))
|
||||
|
||||
if input.Continue != nil {
|
||||
_, ok := safeRedirect.Validate(ctx, *input.Continue)
|
||||
if !ok {
|
||||
return nil, gqlutils.Invalidf(ctx, "invalid continue URL")
|
||||
}
|
||||
}
|
||||
|
||||
req := &iam.SendMagicLinkRequest{
|
||||
Email: input.Email,
|
||||
CompliancePageID: &trustCenter.ID,
|
||||
OrganizationID: trustCenter.OrganizationID,
|
||||
URLPath: "verify-magic-link",
|
||||
Continue: input.Continue,
|
||||
}
|
||||
|
||||
if err := r.iam.AuthService.SendMagicLink(ctx, req); err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot send magic link", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// VerifyMagicLink is the resolver for the verifyMagicLink field.
|
||||
func (r *mutationResolver) VerifyMagicLink(ctx context.Context, input types.VerifyMagicLinkInput) (*types.VerifyMagicLinkPayload, error) {
|
||||
session := authn.SessionFromContext(ctx)
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
|
||||
email, err := r.iam.AuthService.GetMagicLinkEmail(ctx, input.Token)
|
||||
if err != nil {
|
||||
if _, ok := errors.AsType[*iam.ErrExpiredToken](err); ok {
|
||||
return nil, gqlutils.TokenExpired(ctx, err)
|
||||
}
|
||||
|
||||
if _, ok := errors.AsType[*iam.ErrInvalidToken](err); ok {
|
||||
return nil, gqlutils.Invalid(ctx, err)
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot get magic link email", log.Error(err))
|
||||
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
var continueURL *string
|
||||
|
||||
switch {
|
||||
case session == nil:
|
||||
var err error
|
||||
|
||||
identity, session, continueURL, err = r.iam.AuthService.OpenSessionWithMagicLink(ctx, input.Token)
|
||||
if err != nil {
|
||||
if _, ok := errors.AsType[*iam.ErrExpiredToken](err); ok {
|
||||
return nil, gqlutils.TokenExpired(ctx, err)
|
||||
}
|
||||
|
||||
if _, ok := errors.AsType[*iam.ErrTokenAlreadyUsed](err); ok {
|
||||
return nil, gqlutils.TokenAlreadyUsed(ctx, err)
|
||||
}
|
||||
|
||||
if _, ok := errors.AsType[*iam.ErrInvalidToken](err); ok {
|
||||
return nil, gqlutils.Invalid(ctx, err)
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot open session with magic link", log.Error(err))
|
||||
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
case identity.EmailAddress != email:
|
||||
if err := r.iam.SessionService.CloseSession(ctx, session.ID); err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot close session", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
identity, session, continueURL, err = r.iam.AuthService.OpenSessionWithMagicLink(ctx, input.Token)
|
||||
if err != nil {
|
||||
if _, ok := errors.AsType[*iam.ErrExpiredToken](err); ok {
|
||||
return nil, gqlutils.TokenExpired(ctx, err)
|
||||
}
|
||||
|
||||
if _, ok := errors.AsType[*iam.ErrTokenAlreadyUsed](err); ok {
|
||||
return nil, gqlutils.TokenAlreadyUsed(ctx, err)
|
||||
}
|
||||
|
||||
if _, ok := errors.AsType[*iam.ErrInvalidToken](err); ok {
|
||||
return nil, gqlutils.Invalid(ctx, err)
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot open session with magic link", log.Error(err))
|
||||
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
req := gqlutils.HTTPRequestFromContext(ctx)
|
||||
if req == nil {
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
host, ok := complianceportal.TrustedRequestHost(req)
|
||||
if !ok {
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
session.Data = coredata.SessionDataForHost(host)
|
||||
if err := r.iam.SessionService.UpdateSessionData(ctx, session.ID, session.Data); err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot bind session to host", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
trustCenter := complianceportal.CompliancePageFromContext(ctx)
|
||||
|
||||
if _, err := r.trust.ProvisionPortalMember(ctx, trustCenter.ID, identity.ID); err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot provision member", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
w := gqlutils.HTTPResponseWriterFromContext(ctx)
|
||||
r.sessionCookie.Set(w, session)
|
||||
|
||||
return &types.VerifyMagicLinkPayload{
|
||||
Continue: continueURL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateFullName is the resolver for the updateFullName field.
|
||||
func (r *mutationResolver) UpdateFullName(ctx context.Context, input types.UpdateFullNameInput) (*types.UpdateFullNamePayload, error) {
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
return nil, gqlutils.Unauthenticatedf(ctx, "authentication is required to request access")
|
||||
}
|
||||
|
||||
identity, err := r.iam.AccountService.UpdateIdentity(
|
||||
ctx,
|
||||
identity.ID,
|
||||
&iam.UpdateIdentityRequest{
|
||||
FullName: input.FullName,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot update identity", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
compliancePage := complianceportal.CompliancePageFromContext(ctx)
|
||||
|
||||
profile, err := r.iam.OrganizationService.GetProfileForIdentityAndOrganization(ctx, identity.ID, compliancePage.OrganizationID)
|
||||
if err != nil {
|
||||
// External trust-center visitors have no organization profile; updating
|
||||
// the identity's full name above is all that is needed for them.
|
||||
if _, ok := errors.AsType[*iam.ErrProfileNotFound](err); ok {
|
||||
return &types.UpdateFullNamePayload{Success: true}, nil
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot get profile", log.Error(err))
|
||||
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
if profile.Source == coredata.ProfileSourceManual {
|
||||
if _, err := r.iam.OrganizationService.UpdateUser(ctx, &iam.UpdateUserRequest{
|
||||
ID: profile.ID,
|
||||
FullName: identity.FullName,
|
||||
AdditionalEmailAddresses: profile.AdditionalEmailAddresses,
|
||||
Kind: profile.Kind,
|
||||
Position: profile.Position,
|
||||
ContractStartDate: &profile.ContractStartDate,
|
||||
ContractEndDate: &profile.ContractEndDate,
|
||||
}); err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot update profile", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
return &types.UpdateFullNamePayload{Success: true}, nil
|
||||
}
|
||||
|
||||
// SignOut is the resolver for the signOut field.
|
||||
func (r *mutationResolver) SignOut(ctx context.Context) (*types.SignOutPayload, error) {
|
||||
session := authn.SessionFromContext(ctx)
|
||||
|
||||
err := r.iam.SessionService.CloseSession(ctx, session.ID)
|
||||
if err != nil {
|
||||
_, notFound := errors.AsType[*iam.ErrSessionNotFound](err)
|
||||
|
||||
_, expired := errors.AsType[*iam.ErrSessionExpired](err)
|
||||
if !notFound && !expired {
|
||||
r.logger.ErrorCtx(ctx, "cannot close session", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
// Already closed or missing — still clear the cookie so the browser
|
||||
// drops the stale session on concurrent / retried logout.
|
||||
}
|
||||
|
||||
w := gqlutils.HTTPResponseWriterFromContext(ctx)
|
||||
r.sessionCookie.Clear(w)
|
||||
|
||||
return &types.SignOutPayload{Success: true}, nil
|
||||
}
|
||||
255
pkg/server/api/complianceportal/v1/base_resolvers.go
Normal file
255
pkg/server/api/complianceportal/v1/base_resolvers.go
Normal file
@@ -0,0 +1,255 @@
|
||||
package complianceportal_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"
|
||||
trust "go.probo.inc/probo/pkg/complianceportal/visitor"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
"go.probo.inc/probo/pkg/server/api/authn"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal/v1/schema"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal/v1/types"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
)
|
||||
|
||||
// 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) {
|
||||
scope := coredata.NewScopeFromObjectID(id)
|
||||
trustService := r.trust
|
||||
|
||||
switch id.EntityType() {
|
||||
case coredata.DocumentEntityType:
|
||||
trustCenter := complianceportal.CompliancePageFromContext(ctx)
|
||||
|
||||
document, err := trustService.GetDocument(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.GetFramework(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 := complianceportal.CompliancePageFromContext(ctx)
|
||||
|
||||
file, err := trustService.GetReport(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.GetAudit(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.GetThirdParty(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.GetPortal(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.GetPortalReference(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 := complianceportal.CompliancePageFromContext(ctx)
|
||||
|
||||
trustCenterFile, err := trustService.GetPortalFile(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 := complianceportal.CompliancePageFromContext(ctx)
|
||||
scope := coredata.NewScopeFromObjectID(trustCenter.ID)
|
||||
|
||||
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) {
|
||||
trustCenter := complianceportal.CompliancePageFromContext(ctx)
|
||||
|
||||
scope := coredata.NewScopeFromObjectID(trustCenter.ID)
|
||||
trustService := r.trust
|
||||
|
||||
var err error
|
||||
|
||||
trustCenter, err = trustService.GetPortal(ctx, scope, trustCenter.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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// MyRightsRequests is the resolver for the myRightsRequests field.
|
||||
func (r *queryResolver) MyRightsRequests(ctx context.Context, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.RightsRequestConnection, error) {
|
||||
pageOrderBy := page.OrderBy[coredata.RightsRequestOrderField]{
|
||||
Field: coredata.RightsRequestOrderFieldCreatedAt,
|
||||
Direction: page.OrderDirectionDesc,
|
||||
}
|
||||
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
||||
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
emptyPage := page.NewPage([]*coredata.RightsRequest{}, cursor)
|
||||
return types.NewRightsRequestConnection(emptyPage), nil
|
||||
}
|
||||
|
||||
compliancePage := compliancepage.CompliancePageFromContext(ctx)
|
||||
scope := coredata.NewScopeFromObjectID(compliancePage.OrganizationID)
|
||||
|
||||
result, err := r.trust.RightsRequests.ListForOrganizationIDAndContact(
|
||||
ctx,
|
||||
scope,
|
||||
compliancePage.OrganizationID,
|
||||
identity.EmailAddress.String(),
|
||||
cursor,
|
||||
)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot list rights requests", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return types.NewRightsRequestConnection(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 }
|
||||
)
|
||||
47
pkg/server/api/complianceportal/v1/file_loader.go
Normal file
47
pkg/server/api/complianceportal/v1/file_loader.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package complianceportal_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/complianceportal/v1/types"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
)
|
||||
|
||||
func (r *Resolver) loadPublicFile(ctx context.Context, fileID gid.GID) (*types.File, error) {
|
||||
file, err := r.fileManager.GetPublicFile(ctx, fileID)
|
||||
if err != nil {
|
||||
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||
return nil, gqlutils.NotFound(ctx, err)
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot load public file", log.Error(err))
|
||||
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return types.NewFile(file, r.fileManager), nil
|
||||
}
|
||||
38
pkg/server/api/complianceportal/v1/gqlgen.yaml
Normal file
38
pkg/server/api/complianceportal/v1/gqlgen.yaml
Normal file
@@ -0,0 +1,38 @@
|
||||
schema:
|
||||
- "graphql/*.graphql"
|
||||
- "../../../gqlutils/directives/authentication/schema.graphql"
|
||||
- "../../../gqlutils/directives/session/schema.graphql"
|
||||
|
||||
exec:
|
||||
filename: "schema/schema.go"
|
||||
package: "schema"
|
||||
|
||||
model:
|
||||
filename: "types/types.go"
|
||||
package: "types"
|
||||
|
||||
resolver:
|
||||
layout: "follow-schema"
|
||||
dir: "."
|
||||
package: "complianceportal_v1"
|
||||
filename_template: "{name}_resolvers.go"
|
||||
|
||||
autobind: []
|
||||
call_argument_directives_with_null: true
|
||||
|
||||
models:
|
||||
ID:
|
||||
model:
|
||||
- "go.probo.inc/probo/pkg/server/gqlutils/types/gid.GIDScalar"
|
||||
Datetime:
|
||||
model:
|
||||
- "github.com/99designs/gqlgen/graphql.Time"
|
||||
CursorKey:
|
||||
model:
|
||||
- "go.probo.inc/probo/pkg/server/gqlutils/types/cursor.CursorKeyScalar"
|
||||
BigInt:
|
||||
model:
|
||||
- "go.probo.inc/probo/pkg/server/gqlutils/types/bigint.BigIntScalar"
|
||||
EmailAddr:
|
||||
model:
|
||||
- "go.probo.inc/probo/pkg/server/gqlutils/types/mail.AddrScalar"
|
||||
38
pkg/server/api/complianceportal/v1/graphql/auth.graphql
Normal file
38
pkg/server/api/complianceportal/v1/graphql/auth.graphql
Normal file
@@ -0,0 +1,38 @@
|
||||
extend type Mutation {
|
||||
sendMagicLink(input: SendMagicLinkInput!): SendMagicLinkPayload
|
||||
@authentication(required: OPTIONAL)
|
||||
verifyMagicLink(input: VerifyMagicLinkInput!): VerifyMagicLinkPayload
|
||||
@authentication(required: OPTIONAL)
|
||||
updateFullName(input: UpdateFullNameInput!): UpdateFullNamePayload
|
||||
@authentication(required: PRESENT) @sessionOnly
|
||||
signOut: SignOutPayload! @authentication(required: PRESENT) @sessionOnly
|
||||
}
|
||||
|
||||
input SendMagicLinkInput {
|
||||
email: EmailAddr!
|
||||
continue: String
|
||||
}
|
||||
|
||||
type SendMagicLinkPayload {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
input VerifyMagicLinkInput {
|
||||
token: String!
|
||||
}
|
||||
|
||||
type VerifyMagicLinkPayload {
|
||||
continue: String
|
||||
}
|
||||
|
||||
input UpdateFullNameInput {
|
||||
fullName: String!
|
||||
}
|
||||
|
||||
type UpdateFullNamePayload {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type SignOutPayload {
|
||||
success: Boolean!
|
||||
}
|
||||
320
pkg/server/api/complianceportal/v1/graphql/base.graphql
Normal file
320
pkg/server/api/complianceportal/v1/graphql/base.graphql
Normal file
@@ -0,0 +1,320 @@
|
||||
directive @goField(
|
||||
forceResolver: Boolean
|
||||
name: String
|
||||
omittable: Boolean
|
||||
) on INPUT_FIELD_DEFINITION | FIELD_DEFINITION
|
||||
|
||||
directive @goModel(
|
||||
model: String
|
||||
models: [String!]
|
||||
) on OBJECT | INPUT_OBJECT | SCALAR | ENUM | INTERFACE | UNION
|
||||
|
||||
directive @goEnum(value: String) on ENUM_VALUE
|
||||
|
||||
directive @nda on FIELD_DEFINITION | OBJECT
|
||||
|
||||
scalar BigInt
|
||||
scalar CursorKey
|
||||
scalar Datetime
|
||||
scalar EmailAddr
|
||||
|
||||
interface Node {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
type Query {
|
||||
viewer: Identity
|
||||
node(id: ID!): Node
|
||||
aliasedNode(alias: String!): Node
|
||||
currentTrustCenter: TrustCenter
|
||||
oidcProviders: [OIDCProviderInfo!]!
|
||||
@goField(forceResolver: true)
|
||||
@authentication(required: OPTIONAL)
|
||||
|
||||
# The current viewer's own data subject requests for this trust center,
|
||||
# scoped by their verified email. Returns an empty connection for guests so
|
||||
# the portal can still render its empty state.
|
||||
myRightsRequests(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
): RightsRequestConnection! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type OIDCProviderInfo {
|
||||
name: String!
|
||||
loginURL: String!
|
||||
}
|
||||
|
||||
type Mutation
|
||||
|
||||
type Identity implements Node {
|
||||
id: ID!
|
||||
email: EmailAddr!
|
||||
fullName: String!
|
||||
emailVerified: Boolean!
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
type PageInfo {
|
||||
hasNextPage: Boolean!
|
||||
hasPreviousPage: Boolean!
|
||||
startCursor: CursorKey
|
||||
endCursor: CursorKey
|
||||
}
|
||||
|
||||
enum CountryCode
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.CountryCode") {
|
||||
GLOBAL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGlobal")
|
||||
AD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAD")
|
||||
AE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAE")
|
||||
AF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAF")
|
||||
AG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAG")
|
||||
AI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAI")
|
||||
AL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAL")
|
||||
AM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAM")
|
||||
AO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAO")
|
||||
AQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAQ")
|
||||
AR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAR")
|
||||
AS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAS")
|
||||
AT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAT")
|
||||
AU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAU")
|
||||
AW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAW")
|
||||
AX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAX")
|
||||
AZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAZ")
|
||||
BA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBA")
|
||||
BB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBB")
|
||||
BD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBD")
|
||||
BE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBE")
|
||||
BF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBF")
|
||||
BG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBG")
|
||||
BH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBH")
|
||||
BI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBI")
|
||||
BJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBJ")
|
||||
BL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBL")
|
||||
BM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBM")
|
||||
BN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBN")
|
||||
BO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBO")
|
||||
BQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBQ")
|
||||
BR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBR")
|
||||
BS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBS")
|
||||
BT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBT")
|
||||
BV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBV")
|
||||
BW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBW")
|
||||
BY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBY")
|
||||
BZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBZ")
|
||||
CA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCA")
|
||||
CC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCC")
|
||||
CD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCD")
|
||||
CF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCF")
|
||||
CG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCG")
|
||||
CH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCH")
|
||||
CI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCI")
|
||||
CK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCK")
|
||||
CL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCL")
|
||||
CM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCM")
|
||||
CN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCN")
|
||||
CO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCO")
|
||||
CR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCR")
|
||||
CU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCU")
|
||||
CV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCV")
|
||||
CW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCW")
|
||||
CX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCX")
|
||||
CY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCY")
|
||||
CZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCZ")
|
||||
DE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDE")
|
||||
DJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDJ")
|
||||
DK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDK")
|
||||
DM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDM")
|
||||
DO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDO")
|
||||
DZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDZ")
|
||||
EC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEC")
|
||||
EE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEE")
|
||||
EG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEG")
|
||||
EH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEH")
|
||||
ER @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeER")
|
||||
ES @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeES")
|
||||
ET @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeET")
|
||||
EU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEU")
|
||||
FI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFI")
|
||||
FJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFJ")
|
||||
FK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFK")
|
||||
FM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFM")
|
||||
FO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFO")
|
||||
FR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFR")
|
||||
GA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGA")
|
||||
GB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGB")
|
||||
GD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGD")
|
||||
GE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGE")
|
||||
GF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGF")
|
||||
GG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGG")
|
||||
GH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGH")
|
||||
GI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGI")
|
||||
GL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGL")
|
||||
GM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGM")
|
||||
GN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGN")
|
||||
GP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGP")
|
||||
GQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGQ")
|
||||
GR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGR")
|
||||
GT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGT")
|
||||
GU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGU")
|
||||
GW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGW")
|
||||
GY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGY")
|
||||
HK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHK")
|
||||
HM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHM")
|
||||
HN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHN")
|
||||
HR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHR")
|
||||
HT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHT")
|
||||
HU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHU")
|
||||
ID @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeID")
|
||||
IE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIE")
|
||||
IL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIL")
|
||||
IM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIM")
|
||||
IN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIN")
|
||||
IO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIO")
|
||||
IQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIQ")
|
||||
IR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIR")
|
||||
IS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIS")
|
||||
IT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIT")
|
||||
JE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJE")
|
||||
JM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJM")
|
||||
JO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJO")
|
||||
JP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJP")
|
||||
KE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKE")
|
||||
KG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKG")
|
||||
KH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKH")
|
||||
KI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKI")
|
||||
KM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKM")
|
||||
KN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKN")
|
||||
KP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKP")
|
||||
KR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKR")
|
||||
KW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKW")
|
||||
KY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKY")
|
||||
KZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKZ")
|
||||
LA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLA")
|
||||
LB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLB")
|
||||
LC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLC")
|
||||
LI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLI")
|
||||
LK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLK")
|
||||
LR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLR")
|
||||
LS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLS")
|
||||
LT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLT")
|
||||
LU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLU")
|
||||
LV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLV")
|
||||
LY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLY")
|
||||
MA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMA")
|
||||
MC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMC")
|
||||
MD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMD")
|
||||
ME @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeME")
|
||||
MF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMF")
|
||||
MG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMG")
|
||||
MH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMH")
|
||||
MK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMK")
|
||||
ML @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeML")
|
||||
MM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMM")
|
||||
MN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMN")
|
||||
MO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMO")
|
||||
MP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMP")
|
||||
MQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMQ")
|
||||
MR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMR")
|
||||
MS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMS")
|
||||
MT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMT")
|
||||
MU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMU")
|
||||
MV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMV")
|
||||
MW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMW")
|
||||
MX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMX")
|
||||
MY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMY")
|
||||
MZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMZ")
|
||||
NA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNA")
|
||||
NC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNC")
|
||||
NE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNE")
|
||||
NF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNF")
|
||||
NG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNG")
|
||||
NI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNI")
|
||||
NL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNL")
|
||||
NO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNO")
|
||||
NP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNP")
|
||||
NR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNR")
|
||||
NU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNU")
|
||||
NZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNZ")
|
||||
OM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeOM")
|
||||
PA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePA")
|
||||
PE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePE")
|
||||
PF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePF")
|
||||
PG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePG")
|
||||
PH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePH")
|
||||
PK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePK")
|
||||
PL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePL")
|
||||
PM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePM")
|
||||
PN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePN")
|
||||
PR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePR")
|
||||
PS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePS")
|
||||
PT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePT")
|
||||
PW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePW")
|
||||
PY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePY")
|
||||
QA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeQA")
|
||||
RE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRE")
|
||||
RO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRO")
|
||||
RS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRS")
|
||||
RU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRU")
|
||||
RW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRW")
|
||||
SA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSA")
|
||||
SB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSB")
|
||||
SC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSC")
|
||||
SD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSD")
|
||||
SE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSE")
|
||||
SG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSG")
|
||||
SH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSH")
|
||||
SI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSI")
|
||||
SJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSJ")
|
||||
SK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSK")
|
||||
SL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSL")
|
||||
SM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSM")
|
||||
SN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSN")
|
||||
SO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSO")
|
||||
SR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSR")
|
||||
SS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSS")
|
||||
ST @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeST")
|
||||
SV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSV")
|
||||
SX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSX")
|
||||
SY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSY")
|
||||
SZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSZ")
|
||||
TC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTC")
|
||||
TD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTD")
|
||||
TF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTF")
|
||||
TG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTG")
|
||||
TH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTH")
|
||||
TJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTJ")
|
||||
TK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTK")
|
||||
TL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTL")
|
||||
TM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTM")
|
||||
TN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTN")
|
||||
TO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTO")
|
||||
TR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTR")
|
||||
TT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTT")
|
||||
TV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTV")
|
||||
TW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTW")
|
||||
TZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTZ")
|
||||
UA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUA")
|
||||
UG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUG")
|
||||
UM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUM")
|
||||
US @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUS")
|
||||
UY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUY")
|
||||
UZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUZ")
|
||||
VA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVA")
|
||||
VC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVC")
|
||||
VE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVE")
|
||||
VG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVG")
|
||||
VI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVI")
|
||||
VN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVN")
|
||||
VU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVU")
|
||||
WF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeWF")
|
||||
WS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeWS")
|
||||
YE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeYE")
|
||||
YT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeYT")
|
||||
ZA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeZA")
|
||||
ZM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeZM")
|
||||
ZW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeZW")
|
||||
}
|
||||
10
pkg/server/api/complianceportal/v1/graphql/file.graphql
Normal file
10
pkg/server/api/complianceportal/v1/graphql/file.graphql
Normal file
@@ -0,0 +1,10 @@
|
||||
# Trust File: public assets use /api/files/v1/public/{id} (no auth).
|
||||
type File {
|
||||
id: ID!
|
||||
mimeType: String!
|
||||
fileName: String!
|
||||
size: BigInt!
|
||||
downloadUrl: String!
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
type MailingListUpdate implements Node {
|
||||
id: ID!
|
||||
title: String!
|
||||
body: String!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
type MailingListUpdateConnection {
|
||||
edges: [MailingListUpdateEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type MailingListUpdateEdge {
|
||||
cursor: CursorKey!
|
||||
node: MailingListUpdate!
|
||||
}
|
||||
|
||||
enum MailingListSubscriberStatus
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/coredata.MailingListSubscriberStatus"
|
||||
) {
|
||||
PENDING
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.MailingListSubscriberStatusPending"
|
||||
)
|
||||
CONFIRMED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.MailingListSubscriberStatusConfirmed"
|
||||
)
|
||||
}
|
||||
|
||||
type MailingListSubscriber implements Node {
|
||||
id: ID!
|
||||
fullName: String!
|
||||
email: EmailAddr!
|
||||
status: MailingListSubscriberStatus!
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
subscribeToMailingList: SubscribeToMailingListPayload! @authentication(required: PRESENT) @sessionOnly
|
||||
|
||||
unsubscribeFromMailingList: UnsubscribeFromMailingListPayload! @authentication(required: PRESENT) @sessionOnly
|
||||
}
|
||||
|
||||
type SubscribeToMailingListPayload {
|
||||
subscription: MailingListSubscriber!
|
||||
}
|
||||
|
||||
type UnsubscribeFromMailingListPayload {
|
||||
deletedMailingListSubscriberId: ID
|
||||
}
|
||||
149
pkg/server/api/complianceportal/v1/graphql/nda.graphql
Normal file
149
pkg/server/api/complianceportal/v1/graphql/nda.graphql
Normal file
@@ -0,0 +1,149 @@
|
||||
type NonDisclosureAgreement {
|
||||
fileName: String!
|
||||
fileUrl: String! @goField(forceResolver: true)
|
||||
viewerSignature: ElectronicSignature @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
enum ElectronicSignatureStatus
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatus"
|
||||
) {
|
||||
PENDING
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusPending"
|
||||
)
|
||||
ACCEPTED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusAccepted"
|
||||
)
|
||||
PROCESSING
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusProcessing"
|
||||
)
|
||||
COMPLETED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusCompleted"
|
||||
)
|
||||
FAILED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusFailed"
|
||||
)
|
||||
}
|
||||
|
||||
enum ElectronicSignatureDocumentType
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentType"
|
||||
) {
|
||||
NDA
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeNDA"
|
||||
)
|
||||
DPA
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeDPA"
|
||||
)
|
||||
MSA
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeMSA"
|
||||
)
|
||||
SOW
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeSOW"
|
||||
)
|
||||
SLA
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeSLA"
|
||||
)
|
||||
TOS
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeTOS"
|
||||
)
|
||||
PRIVACY_POLICY
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypePrivacyPolicy"
|
||||
)
|
||||
OTHER
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeOther"
|
||||
)
|
||||
}
|
||||
|
||||
enum ElectronicSignatureEventType
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventType"
|
||||
) {
|
||||
DOCUMENT_VIEWED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeDocumentViewed"
|
||||
)
|
||||
CONSENT_GIVEN
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeConsentGiven"
|
||||
)
|
||||
FULL_NAME_TYPED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeFullNameTyped"
|
||||
)
|
||||
SIGNATURE_ACCEPTED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeSignatureAccepted"
|
||||
)
|
||||
SIGNATURE_COMPLETED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeSignatureCompleted"
|
||||
)
|
||||
SEAL_COMPUTED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeSealComputed"
|
||||
)
|
||||
TIMESTAMP_REQUESTED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeTimestampRequested"
|
||||
)
|
||||
CERTIFICATE_GENERATED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeCertificateGenerated"
|
||||
)
|
||||
PROCESSING_ERROR
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeProcessingError"
|
||||
)
|
||||
}
|
||||
|
||||
type ElectronicSignature implements Node {
|
||||
id: ID!
|
||||
status: ElectronicSignatureStatus!
|
||||
documentType: ElectronicSignatureDocumentType!
|
||||
consentText: String!
|
||||
lastError: String
|
||||
signedAt: Datetime
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
acceptElectronicSignature(
|
||||
input: AcceptElectronicSignatureInput!
|
||||
): AcceptElectronicSignaturePayload @authentication(required: PRESENT) @sessionOnly
|
||||
|
||||
recordSigningEvent(
|
||||
input: RecordSigningEventInput!
|
||||
): RecordSigningEventPayload @authentication(required: PRESENT) @sessionOnly
|
||||
}
|
||||
|
||||
input AcceptElectronicSignatureInput {
|
||||
signatureId: ID!
|
||||
}
|
||||
|
||||
type AcceptElectronicSignaturePayload {
|
||||
signature: ElectronicSignature!
|
||||
}
|
||||
|
||||
input RecordSigningEventInput {
|
||||
signatureId: ID!
|
||||
eventType: ElectronicSignatureEventType!
|
||||
}
|
||||
|
||||
type RecordSigningEventPayload {
|
||||
success: Boolean!
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
enum RightsRequestType
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.RightsRequestType") {
|
||||
ACCESS @goEnum(value: "go.probo.inc/probo/pkg/coredata.RightsRequestTypeAccess")
|
||||
DELETION
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.RightsRequestTypeDeletion")
|
||||
RECTIFICATION
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestTypeRectification"
|
||||
)
|
||||
PORTABILITY
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestTypePortability"
|
||||
)
|
||||
OBJECTION
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.RightsRequestTypeObjection")
|
||||
COMPLAINT
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.RightsRequestTypeComplaint")
|
||||
}
|
||||
|
||||
enum RightsRequestState
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.RightsRequestState") {
|
||||
TODO @goEnum(value: "go.probo.inc/probo/pkg/coredata.RightsRequestStateTodo")
|
||||
IN_PROGRESS
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestStateInProgress"
|
||||
)
|
||||
DONE @goEnum(value: "go.probo.inc/probo/pkg/coredata.RightsRequestStateDone")
|
||||
REJECTED
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.RightsRequestStateRejected")
|
||||
}
|
||||
|
||||
type RightsRequest implements Node {
|
||||
id: ID!
|
||||
requestType: RightsRequestType!
|
||||
requestState: RightsRequestState!
|
||||
dataSubject: String
|
||||
contact: String
|
||||
details: String
|
||||
deadline: Datetime
|
||||
actionTaken: String
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
type RightsRequestConnection {
|
||||
edges: [RightsRequestEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type RightsRequestEdge {
|
||||
cursor: CursorKey!
|
||||
node: RightsRequest!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
# Submit a data subject request. Requires a verified viewer; the request is
|
||||
# attributed to the viewer's email, so no NDA gate applies.
|
||||
createRightsRequest(
|
||||
input: CreateRightsRequestInput!
|
||||
): CreateRightsRequestPayload! @authentication(required: PRESENT)
|
||||
}
|
||||
|
||||
input CreateRightsRequestInput {
|
||||
requestType: RightsRequestType!
|
||||
dataSubject: String
|
||||
details: String
|
||||
}
|
||||
|
||||
type CreateRightsRequestPayload {
|
||||
rightsRequestEdge: RightsRequestEdge!
|
||||
}
|
||||
544
pkg/server/api/complianceportal/v1/graphql/trust_center.graphql
Normal file
544
pkg/server/api/complianceportal/v1/graphql/trust_center.graphql
Normal file
@@ -0,0 +1,544 @@
|
||||
type TrustCenter implements Node {
|
||||
id: ID!
|
||||
active: Boolean!
|
||||
slug: String!
|
||||
logo: File @goField(forceResolver: true)
|
||||
darkLogo: File @goField(forceResolver: true)
|
||||
|
||||
description: String
|
||||
websiteUrl: String
|
||||
email: String
|
||||
headquarterAddress: String
|
||||
title: String!
|
||||
|
||||
nonDisclosureAgreement: NonDisclosureAgreement @goField(forceResolver: true)
|
||||
|
||||
viewerSubscription: MailingListSubscriber @goField(forceResolver: true)
|
||||
|
||||
documents(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
filter: TrustCenterVisibilityFilter
|
||||
): DocumentConnection! @goField(forceResolver: true)
|
||||
|
||||
audits(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
filter: TrustCenterVisibilityFilter
|
||||
): AuditConnection! @goField(forceResolver: true)
|
||||
|
||||
subprocessors(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
filter: SubprocessorFilter
|
||||
): SubprocessorConnection! @goField(forceResolver: true)
|
||||
|
||||
subprocessorCategories: [SubprocessorCategory!]!
|
||||
@goField(forceResolver: true)
|
||||
|
||||
subprocessorCountries: [CountryCode!]! @goField(forceResolver: true)
|
||||
|
||||
references(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
): TrustCenterReferenceConnection! @goField(forceResolver: true)
|
||||
|
||||
commitmentGroups(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
): CompliancePortalCommitmentGroupConnection! @goField(forceResolver: true)
|
||||
|
||||
trustCenterFiles(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
filter: TrustCenterVisibilityFilter
|
||||
): TrustCenterFileConnection! @goField(forceResolver: true)
|
||||
|
||||
complianceFrameworks(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
): ComplianceFrameworkConnection! @goField(forceResolver: true)
|
||||
|
||||
customLinks(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
): ComplianceCustomLinkConnection! @goField(forceResolver: true)
|
||||
|
||||
updates(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
): MailingListUpdateConnection! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
enum DocumentType
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.DocumentType") {
|
||||
OTHER @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeOther")
|
||||
GOVERNANCE
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeGovernance")
|
||||
POLICY @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypePolicy")
|
||||
PROCEDURE
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeProcedure")
|
||||
PLAN @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypePlan")
|
||||
REGISTER
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeRegister")
|
||||
RECORD @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeRecord")
|
||||
REPORT @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeReport")
|
||||
TEMPLATE
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeTemplate")
|
||||
STATEMENT_OF_APPLICABILITY
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.DocumentTypeStatementOfApplicability"
|
||||
)
|
||||
}
|
||||
|
||||
enum TrustCenterVisibility
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.TrustCenterVisibility") {
|
||||
PRIVATE
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.TrustCenterVisibilityPrivate")
|
||||
PUBLIC
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.TrustCenterVisibilityPublic")
|
||||
}
|
||||
|
||||
input TrustCenterVisibilityFilter {
|
||||
visibility: TrustCenterVisibility
|
||||
}
|
||||
|
||||
type Document implements Node @nda {
|
||||
id: ID!
|
||||
title: String!
|
||||
documentType: DocumentType!
|
||||
alias: String @goField(forceResolver: true)
|
||||
isUserAuthorized: Boolean! @goField(forceResolver: true)
|
||||
access: DocumentAccess @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type DocumentConnection @nda {
|
||||
edges: [DocumentEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type DocumentEdge @nda {
|
||||
cursor: CursorKey!
|
||||
node: Document!
|
||||
}
|
||||
|
||||
type Framework implements Node @nda {
|
||||
id: ID!
|
||||
name: String!
|
||||
lightLogo: File @goField(forceResolver: true)
|
||||
darkLogo: File @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type AuditReport implements Node @nda {
|
||||
id: ID!
|
||||
fileName: String!
|
||||
alias: String @goField(forceResolver: true)
|
||||
isUserAuthorized: Boolean! @goField(forceResolver: true)
|
||||
access: DocumentAccess @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type Audit implements Node @nda {
|
||||
id: ID!
|
||||
name: String
|
||||
framework: Framework! @goField(forceResolver: true)
|
||||
reportFile: AuditReport @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type AuditConnection @nda {
|
||||
edges: [AuditEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type AuditEdge @nda {
|
||||
cursor: CursorKey!
|
||||
node: Audit!
|
||||
}
|
||||
|
||||
type ComplianceFramework implements Node {
|
||||
id: ID!
|
||||
framework: Framework! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type ComplianceFrameworkConnection
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/complianceportal/v1/types.ComplianceFrameworkConnection"
|
||||
) {
|
||||
edges: [ComplianceFrameworkEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type ComplianceFrameworkEdge {
|
||||
cursor: CursorKey!
|
||||
node: ComplianceFramework!
|
||||
}
|
||||
|
||||
enum SubprocessorCategory
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategory") {
|
||||
ANALYTICS
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryAnalytics")
|
||||
CLOUD_MONITORING
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryCloudMonitoring"
|
||||
)
|
||||
CLOUD_PROVIDER
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryCloudProvider"
|
||||
)
|
||||
COLLABORATION
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryCollaboration"
|
||||
)
|
||||
CUSTOMER_SUPPORT
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryCustomerSupport"
|
||||
)
|
||||
DATA_STORAGE_AND_PROCESSING
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryDataStorageAndProcessing"
|
||||
)
|
||||
DOCUMENT_MANAGEMENT
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryDocumentManagement"
|
||||
)
|
||||
EMPLOYEE_MANAGEMENT
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryEmployeeManagement"
|
||||
)
|
||||
ENGINEERING
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryEngineering")
|
||||
FINANCE
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryFinance")
|
||||
IDENTITY_PROVIDER
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryIdentityProvider"
|
||||
)
|
||||
IT @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryIT")
|
||||
MARKETING
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryMarketing")
|
||||
OFFICE_OPERATIONS
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryOfficeOperations"
|
||||
)
|
||||
OTHER @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryOther")
|
||||
PASSWORD_MANAGEMENT
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryPasswordManagement"
|
||||
)
|
||||
PRODUCT_AND_DESIGN
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryProductAndDesign"
|
||||
)
|
||||
PROFESSIONAL_SERVICES
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryProfessionalServices"
|
||||
)
|
||||
RECRUITING
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryRecruiting")
|
||||
SALES @goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategorySales")
|
||||
SECURITY
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategorySecurity")
|
||||
VERSION_CONTROL
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ThirdPartyCategoryVersionControl"
|
||||
)
|
||||
}
|
||||
|
||||
type Subprocessor implements Node @nda {
|
||||
id: ID!
|
||||
name: String!
|
||||
description: String
|
||||
category: SubprocessorCategory!
|
||||
websiteUrl: String
|
||||
privacyPolicyUrl: String
|
||||
countries: [CountryCode!]!
|
||||
}
|
||||
|
||||
input SubprocessorFilter {
|
||||
query: String
|
||||
category: SubprocessorCategory
|
||||
country: CountryCode
|
||||
}
|
||||
|
||||
type SubprocessorConnection
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/complianceportal/v1/types.SubprocessorConnection"
|
||||
) @nda {
|
||||
edges: [SubprocessorEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
totalCount: Int! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type SubprocessorEdge @nda {
|
||||
cursor: CursorKey!
|
||||
node: Subprocessor!
|
||||
}
|
||||
|
||||
type TrustCenterReference implements Node @nda {
|
||||
id: ID!
|
||||
name: String!
|
||||
description: String
|
||||
websiteUrl: String!
|
||||
logo: File! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type TrustCenterReferenceConnection @nda {
|
||||
edges: [TrustCenterReferenceEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type TrustCenterReferenceEdge @nda {
|
||||
cursor: CursorKey!
|
||||
node: TrustCenterReference!
|
||||
}
|
||||
|
||||
enum CompliancePortalCommitmentIcon
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIcon") {
|
||||
LOCK_KEY
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconLockKey")
|
||||
EYE_SLASH
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconEyeSlash")
|
||||
FINGERPRINT
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconFingerprint")
|
||||
SHIELD_WARNING
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconShieldWarning")
|
||||
SHIELD_CHECK
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconShieldCheck")
|
||||
SIREN
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconSiren")
|
||||
KEY
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconKey")
|
||||
LOCK
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconLock")
|
||||
CLOUD
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconCloud")
|
||||
DATABASE
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconDatabase")
|
||||
GLOBE
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconGlobe")
|
||||
EYE
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconEye")
|
||||
USERS
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconUsers")
|
||||
CERTIFICATE
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconCertificate")
|
||||
GAVEL
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconGavel")
|
||||
HEARTBEAT
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconHeartbeat")
|
||||
BELL
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconBell")
|
||||
BUG
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconBug")
|
||||
CODE
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconCode")
|
||||
SERVER
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.CompliancePortalCommitmentIconServer")
|
||||
}
|
||||
|
||||
type CompliancePortalCommitmentGroup implements Node {
|
||||
id: ID!
|
||||
title: String!
|
||||
description: String!
|
||||
|
||||
commitments(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
): CompliancePortalCommitmentConnection! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type CompliancePortalCommitmentGroupConnection {
|
||||
edges: [CompliancePortalCommitmentGroupEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type CompliancePortalCommitmentGroupEdge {
|
||||
cursor: CursorKey!
|
||||
node: CompliancePortalCommitmentGroup!
|
||||
}
|
||||
|
||||
type CompliancePortalCommitment implements Node {
|
||||
id: ID!
|
||||
icon: CompliancePortalCommitmentIcon!
|
||||
eyebrow: String!
|
||||
title: String!
|
||||
description: String!
|
||||
}
|
||||
|
||||
type CompliancePortalCommitmentConnection {
|
||||
edges: [CompliancePortalCommitmentEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type CompliancePortalCommitmentEdge {
|
||||
cursor: CursorKey!
|
||||
node: CompliancePortalCommitment!
|
||||
}
|
||||
|
||||
type TrustCenterFile implements Node @nda {
|
||||
id: ID!
|
||||
name: String!
|
||||
category: String!
|
||||
alias: String @goField(forceResolver: true)
|
||||
isUserAuthorized: Boolean! @goField(forceResolver: true)
|
||||
access: DocumentAccess @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type TrustCenterFileConnection @nda {
|
||||
edges: [TrustCenterFileEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type TrustCenterFileEdge @nda {
|
||||
cursor: CursorKey!
|
||||
node: TrustCenterFile!
|
||||
}
|
||||
|
||||
type ComplianceCustomLink implements Node {
|
||||
id: ID!
|
||||
name: String!
|
||||
url: String!
|
||||
rank: Int!
|
||||
}
|
||||
|
||||
type ComplianceCustomLinkConnection {
|
||||
edges: [ComplianceCustomLinkEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type ComplianceCustomLinkEdge {
|
||||
cursor: CursorKey!
|
||||
node: ComplianceCustomLink!
|
||||
}
|
||||
|
||||
type TrustCenterAccess implements Node {
|
||||
id: ID!
|
||||
email: EmailAddr!
|
||||
name: String!
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
enum DocumentAccessStatus
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatus"
|
||||
) {
|
||||
REQUESTED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatusRequested"
|
||||
)
|
||||
GRANTED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatusGranted"
|
||||
)
|
||||
REJECTED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatusRejected"
|
||||
)
|
||||
REVOKED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatusRevoked"
|
||||
)
|
||||
}
|
||||
|
||||
type DocumentAccess implements Node {
|
||||
id: ID!
|
||||
status: DocumentAccessStatus!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
requestAllAccesses: RequestAccessesPayload! @authentication(required: PRESENT) @nda
|
||||
|
||||
exportDocumentPDF(input: ExportDocumentPDFInput!): ExportDocumentPDFPayload!
|
||||
@authentication(required: OPTIONAL) @nda
|
||||
|
||||
exportReportPDF(input: ExportReportPDFInput!): ExportReportPDFPayload!
|
||||
@authentication(required: OPTIONAL) @nda
|
||||
|
||||
exportTrustCenterFile(
|
||||
input: ExportTrustCenterFileInput!
|
||||
): ExportTrustCenterFilePayload! @authentication(required: OPTIONAL) @nda
|
||||
|
||||
requestDocumentAccess(
|
||||
input: RequestDocumentAccessInput!
|
||||
): RequestDocumentAccessPayload! @authentication(required: PRESENT) @nda
|
||||
|
||||
requestReportAccess(
|
||||
input: RequestReportAccessInput!
|
||||
): RequestReportAccessPayload! @authentication(required: PRESENT) @nda
|
||||
|
||||
requestTrustCenterFileAccess(
|
||||
input: RequestTrustCenterFileAccessInput!
|
||||
): RequestFileAccessPayload! @authentication(required: PRESENT) @nda
|
||||
}
|
||||
|
||||
type RequestDocumentAccessPayload {
|
||||
document: Document
|
||||
}
|
||||
|
||||
type RequestReportAccessPayload {
|
||||
audit: Audit
|
||||
}
|
||||
|
||||
type RequestFileAccessPayload {
|
||||
file: TrustCenterFile
|
||||
}
|
||||
|
||||
type RequestAccessesPayload {
|
||||
trustCenterAccess: TrustCenterAccess!
|
||||
}
|
||||
|
||||
input ExportDocumentPDFInput {
|
||||
documentId: ID!
|
||||
}
|
||||
|
||||
input ExportReportPDFInput {
|
||||
reportId: ID!
|
||||
}
|
||||
|
||||
input RequestDocumentAccessInput {
|
||||
documentId: ID!
|
||||
}
|
||||
|
||||
input RequestReportAccessInput {
|
||||
reportId: ID!
|
||||
}
|
||||
|
||||
input RequestTrustCenterFileAccessInput {
|
||||
trustCenterFileId: ID!
|
||||
}
|
||||
|
||||
input ExportTrustCenterFileInput {
|
||||
trustCenterFileId: ID!
|
||||
}
|
||||
|
||||
type ExportDocumentPDFPayload {
|
||||
data: String!
|
||||
}
|
||||
|
||||
type ExportReportPDFPayload {
|
||||
data: String!
|
||||
}
|
||||
|
||||
type ExportTrustCenterFilePayload {
|
||||
data: String!
|
||||
}
|
||||
78
pkg/server/api/complianceportal/v1/graphql_handler.go
Normal file
78
pkg/server/api/complianceportal/v1/graphql_handler.go
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package complianceportal_v1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"go.gearno.de/kit/log"
|
||||
"go.probo.inc/probo/pkg/baseurl"
|
||||
trust "go.probo.inc/probo/pkg/complianceportal/visitor"
|
||||
"go.probo.inc/probo/pkg/esign"
|
||||
"go.probo.inc/probo/pkg/filemanager"
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
"go.probo.inc/probo/pkg/mailman"
|
||||
"go.probo.inc/probo/pkg/resourcealias"
|
||||
"go.probo.inc/probo/pkg/securecookie"
|
||||
"go.probo.inc/probo/pkg/server/api/authn"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal/v1/schema"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils/directives/authentication"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils/directives/session"
|
||||
)
|
||||
|
||||
func NewGraphQLHandler(
|
||||
iamSvc *iam.Service,
|
||||
trustSvc *trust.Service,
|
||||
resourceAliasSvc *resourcealias.Service,
|
||||
fileManagerSvc *filemanager.Service,
|
||||
esignSvc *esign.Service,
|
||||
mailmanSvc *mailman.Service,
|
||||
logger *log.Logger,
|
||||
baseURL *baseurl.BaseURL,
|
||||
cookieConfig securecookie.Config,
|
||||
tokenSecret string,
|
||||
limits gqlutils.Limits,
|
||||
) http.Handler {
|
||||
config := schema.Config{
|
||||
Resolvers: &Resolver{
|
||||
iam: iamSvc,
|
||||
trust: trustSvc,
|
||||
resourceAlias: resourceAliasSvc,
|
||||
fileManager: fileManagerSvc,
|
||||
esign: esignSvc,
|
||||
mailman: mailmanSvc,
|
||||
logger: logger,
|
||||
baseURL: baseURL,
|
||||
sessionCookie: authn.NewCookie(&cookieConfig),
|
||||
},
|
||||
Directives: schema.DirectiveRoot{
|
||||
Nda: newNDADirective(logger, trustSvc, esignSvc),
|
||||
Authentication: authentication.Directive,
|
||||
SessionOnly: session.Directive,
|
||||
},
|
||||
}
|
||||
|
||||
es := schema.NewExecutableSchema(config)
|
||||
gqlh := gqlutils.NewHandler(es, logger, limits)
|
||||
|
||||
return gqlh
|
||||
}
|
||||
87
pkg/server/api/complianceportal/v1/mailing_list_resolvers.go
Normal file
87
pkg/server/api/complianceportal/v1/mailing_list_resolvers.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package complianceportal_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"
|
||||
|
||||
"go.gearno.de/kit/log"
|
||||
"go.probo.inc/probo/pkg/mailman"
|
||||
"go.probo.inc/probo/pkg/server/api/authn"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal/v1/types"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
"go.probo.inc/probo/pkg/validator"
|
||||
)
|
||||
|
||||
// SubscribeToMailingList is the resolver for the subscribeToMailingList field.
|
||||
func (r *mutationResolver) SubscribeToMailingList(ctx context.Context) (*types.SubscribeToMailingListPayload, error) {
|
||||
trustCenter := complianceportal.CompliancePageFromContext(ctx)
|
||||
if trustCenter.MailingListID == nil {
|
||||
return nil, gqlutils.NotFoundf(ctx, "mailing list not found")
|
||||
}
|
||||
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
|
||||
subscriber, err := r.mailman.CreateSubscriber(
|
||||
ctx,
|
||||
&mailman.CreateSubscriberRequest{
|
||||
MailingListID: *trustCenter.MailingListID,
|
||||
Email: identity.EmailAddress,
|
||||
FullName: identity.FullName,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
|
||||
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
|
||||
}
|
||||
|
||||
if errors.Is(err, mailman.ErrSubscriberAlreadyExist) {
|
||||
return nil, gqlutils.Conflictf(ctx, "already subscribed to this mailing list")
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot subscribe to mailing list", log.Error(err))
|
||||
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return &types.SubscribeToMailingListPayload{
|
||||
Subscription: types.NewMailingListSubscriber(subscriber),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UnsubscribeFromMailingList is the resolver for the unsubscribeFromMailingList field.
|
||||
func (r *mutationResolver) UnsubscribeFromMailingList(ctx context.Context) (*types.UnsubscribeFromMailingListPayload, error) {
|
||||
trustCenter := complianceportal.CompliancePageFromContext(ctx)
|
||||
if trustCenter.MailingListID == nil {
|
||||
return nil, gqlutils.NotFoundf(ctx, "mailing list not found")
|
||||
}
|
||||
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
|
||||
subscriber, err := r.mailman.GetSubscriber(ctx, *trustCenter.MailingListID, identity.EmailAddress)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot get mailing list subscription", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
if subscriber == nil {
|
||||
return nil, gqlutils.NotFoundf(ctx, "not subscribed to this mailing list")
|
||||
}
|
||||
|
||||
if err := r.mailman.DeleteSubscriber(ctx, subscriber.ID); err != nil {
|
||||
if errors.Is(err, mailman.ErrSubscriberNotFound) {
|
||||
return nil, gqlutils.NotFoundf(ctx, "not subscribed to this mailing list")
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot unsubscribe from mailing list", log.Error(err))
|
||||
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return &types.UnsubscribeFromMailingListPayload{DeletedMailingListSubscriberID: &subscriber.ID}, nil
|
||||
}
|
||||
163
pkg/server/api/complianceportal/v1/mux.go
Normal file
163
pkg/server/api/complianceportal/v1/mux.go
Normal file
@@ -0,0 +1,163 @@
|
||||
// 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 complianceportal_v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"go.gearno.de/kit/httpserver"
|
||||
"go.gearno.de/kit/log"
|
||||
"go.gearno.de/x/ref"
|
||||
"go.probo.inc/probo/pkg/baseurl"
|
||||
visitor "go.probo.inc/probo/pkg/complianceportal/visitor"
|
||||
"go.probo.inc/probo/pkg/esign"
|
||||
"go.probo.inc/probo/pkg/filemanager"
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
"go.probo.inc/probo/pkg/mailman"
|
||||
"go.probo.inc/probo/pkg/resourcealias"
|
||||
"go.probo.inc/probo/pkg/securecookie"
|
||||
"go.probo.inc/probo/pkg/server"
|
||||
"go.probo.inc/probo/pkg/server/api/authn"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
)
|
||||
|
||||
type MuxConfig struct {
|
||||
BaseURL *baseurl.BaseURL
|
||||
ExtraHeaderFields map[string]string
|
||||
Logger *log.Logger
|
||||
IAM *iam.Service
|
||||
Visitor *visitor.Service
|
||||
ResourceAlias *resourcealias.Service
|
||||
File *filemanager.Service
|
||||
ESign *esign.Service
|
||||
Mailman *mailman.Service
|
||||
Cookie securecookie.Config
|
||||
TokenSecret string
|
||||
GraphQLLimits gqlutils.Limits
|
||||
}
|
||||
|
||||
func NewMux(cfg MuxConfig) (http.Handler, error) {
|
||||
webServer, err := NewServer(compliancePageHeadData(cfg.BaseURL))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := chi.NewRouter()
|
||||
|
||||
r.Use(complianceportal.NewSNIMiddleware(cfg.Visitor))
|
||||
r.Use(server.NewSecurityHeadersMiddleware(cfg.ExtraHeaderFields))
|
||||
|
||||
markdownHandler := complianceportal.NewHandler(cfg.Visitor)
|
||||
|
||||
r.Get("/llms.txt", markdownHandler.HandleLLMsTxt)
|
||||
r.Get("/robots.txt", markdownHandler.HandleRobotsTxt)
|
||||
r.Get("/sitemap.xml", markdownHandler.HandleSitemap)
|
||||
|
||||
allowedHost := func(ctx context.Context, host string) bool {
|
||||
_, err := cfg.Visitor.GetPortalByDomainName(ctx, host)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
oauthInitiateHandler := NewOAuthInitiateHandler(
|
||||
cfg.BaseURL,
|
||||
cfg.Visitor,
|
||||
allowedHost,
|
||||
cfg.Logger,
|
||||
)
|
||||
oauthCallbackHandler := NewOAuthCallbackHandler(
|
||||
cfg.IAM,
|
||||
cfg.Visitor,
|
||||
cfg.Cookie,
|
||||
allowedHost,
|
||||
cfg.Logger,
|
||||
)
|
||||
|
||||
graphqlHandler := NewGraphQLHandler(
|
||||
cfg.IAM,
|
||||
cfg.Visitor,
|
||||
cfg.ResourceAlias,
|
||||
cfg.File,
|
||||
cfg.ESign,
|
||||
cfg.Mailman,
|
||||
cfg.Logger,
|
||||
cfg.BaseURL,
|
||||
cfg.Cookie,
|
||||
cfg.TokenSecret,
|
||||
cfg.GraphQLLimits,
|
||||
)
|
||||
|
||||
r.Group(
|
||||
func(r chi.Router) {
|
||||
r.Use(complianceportal.NewCompliancePagePresenceMiddleware())
|
||||
|
||||
r.Method(http.MethodGet, complianceportal.CIMDMetadataPath, NewOAuthClientMetadataHandler())
|
||||
r.Method(http.MethodGet, complianceportal.OAuthInitiatePath, oauthInitiateHandler)
|
||||
r.Method(http.MethodGet, complianceportal.OAuthCallbackPath, oauthCallbackHandler)
|
||||
|
||||
r.Group(
|
||||
func(r chi.Router) {
|
||||
r.Use(authn.NewSessionMiddleware(cfg.IAM, cfg.Cookie))
|
||||
r.Use(complianceportal.NewSessionHostMiddleware(cfg.Cookie))
|
||||
r.Use(complianceportal.NewMemberProvisioningMiddleware(cfg.Visitor, cfg.Logger))
|
||||
r.Handle(complianceportal.GraphQLPath, graphqlHandler)
|
||||
},
|
||||
)
|
||||
|
||||
r.Handle("/*", webServer)
|
||||
r.NotFound(handleCustomDomain404)
|
||||
},
|
||||
)
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func handleCustomDomain404(w http.ResponseWriter, r *http.Request) {
|
||||
httpserver.RenderError(w, http.StatusNotFound, errors.New("not found"))
|
||||
}
|
||||
|
||||
func compliancePageHeadData(baseURL *baseurl.BaseURL) HeadDataFunc {
|
||||
return func(r *http.Request) HeadData {
|
||||
tc := complianceportal.CompliancePageFromContext(r.Context())
|
||||
if tc == nil {
|
||||
return HeadData{Title: "Compliance Page"}
|
||||
}
|
||||
|
||||
compliancePageBaseURL := complianceportal.CompliancePageBaseURLFromContext(r.Context())
|
||||
|
||||
description := tc.Title + " Compliance Page"
|
||||
if tc.Description != nil && *tc.Description != "" {
|
||||
description = *tc.Description
|
||||
}
|
||||
|
||||
headData := HeadData{
|
||||
Title: tc.Title,
|
||||
Description: description,
|
||||
OGURL: ref.UnrefOrZero(compliancePageBaseURL),
|
||||
}
|
||||
|
||||
if tc.LogoFileID != nil {
|
||||
faviconURL, err := baseURL.WithPath("/api/files/v1/public/" + tc.LogoFileID.String()).String()
|
||||
if err == nil {
|
||||
headData.FaviconURL = faviconURL
|
||||
}
|
||||
}
|
||||
|
||||
return headData
|
||||
}
|
||||
}
|
||||
82
pkg/server/api/complianceportal/v1/nda_directive.go
Normal file
82
pkg/server/api/complianceportal/v1/nda_directive.go
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package complianceportal_v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
"go.gearno.de/kit/log"
|
||||
trust "go.probo.inc/probo/pkg/complianceportal/visitor"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/esign"
|
||||
"go.probo.inc/probo/pkg/server/api/authn"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
)
|
||||
|
||||
func newNDADirective(
|
||||
logger *log.Logger,
|
||||
trustSvc *trust.Service,
|
||||
esignSvc *esign.Service,
|
||||
) func(ctx context.Context, obj any, next graphql.Resolver) (any, error) {
|
||||
return func(ctx context.Context, obj any, next graphql.Resolver) (any, error) {
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
compliancePage := complianceportal.CompliancePageFromContext(ctx)
|
||||
if compliancePage == nil {
|
||||
logger.ErrorCtx(ctx, "cannot get compliance page from context")
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
membership, err := trustSvc.GetPortalMembership(ctx, compliancePage.ID, identity.ID)
|
||||
if err != nil {
|
||||
logger.ErrorCtx(ctx, "cannot get compliance page membership", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
if membership.ElectronicSignatureID == nil {
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
scope := coredata.NewScopeFromObjectID(compliancePage.OrganizationID)
|
||||
|
||||
sig, err := esignSvc.GetSignatureByID(ctx, scope, *membership.ElectronicSignatureID)
|
||||
if err != nil {
|
||||
logger.ErrorCtx(ctx, "cannot get NDA signature", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
// We need full name before user signs NDA
|
||||
if identity.FullName == "" {
|
||||
return nil, gqlutils.FullNameRequiredf(ctx, "full name is required")
|
||||
}
|
||||
|
||||
if sig.Status != coredata.ElectronicSignatureStatusCompleted {
|
||||
return nil, gqlutils.NDASignatureRequiredf(ctx, "NDA signature required")
|
||||
}
|
||||
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
||||
156
pkg/server/api/complianceportal/v1/nda_resolvers.go
Normal file
156
pkg/server/api/complianceportal/v1/nda_resolvers.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package complianceportal_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"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"go.gearno.de/kit/log"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/esign"
|
||||
"go.probo.inc/probo/pkg/server/api/authn"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal/v1/schema"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal/v1/types"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
)
|
||||
|
||||
// AcceptElectronicSignature is the resolver for the acceptElectronicSignature field.
|
||||
func (r *mutationResolver) AcceptElectronicSignature(ctx context.Context, input types.AcceptElectronicSignatureInput) (*types.AcceptElectronicSignaturePayload, error) {
|
||||
var (
|
||||
identity = authn.IdentityFromContext(ctx)
|
||||
httpReq = gqlutils.HTTPRequestFromContext(ctx)
|
||||
trustCenter = complianceportal.CompliancePageFromContext(ctx)
|
||||
)
|
||||
|
||||
signerIP, _, _ := net.SplitHostPort(httpReq.RemoteAddr)
|
||||
if signerIP == "" {
|
||||
signerIP = httpReq.RemoteAddr
|
||||
}
|
||||
|
||||
scope := coredata.NewScopeFromObjectID(trustCenter.ID)
|
||||
|
||||
signature, err := r.esign.AcceptSignature(
|
||||
ctx,
|
||||
scope,
|
||||
&esign.AcceptSignatureRequest{
|
||||
SignatureID: input.SignatureID,
|
||||
SignerFullName: identity.FullName,
|
||||
SignerEmail: identity.EmailAddress,
|
||||
SignerIPAddr: signerIP,
|
||||
SignerUA: httpReq.UserAgent(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot accept electronic signature", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return &types.AcceptElectronicSignaturePayload{
|
||||
Signature: types.NewElectronicSignature(signature),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RecordSigningEvent is the resolver for the recordSigningEvent field.
|
||||
func (r *mutationResolver) RecordSigningEvent(ctx context.Context, input types.RecordSigningEventInput) (*types.RecordSigningEventPayload, error) {
|
||||
var (
|
||||
identity = authn.IdentityFromContext(ctx)
|
||||
httpReq = gqlutils.HTTPRequestFromContext(ctx)
|
||||
trustCenter = complianceportal.CompliancePageFromContext(ctx)
|
||||
)
|
||||
|
||||
actorIP, _, _ := net.SplitHostPort(httpReq.RemoteAddr)
|
||||
if actorIP == "" {
|
||||
actorIP = httpReq.RemoteAddr
|
||||
}
|
||||
|
||||
scope := coredata.NewScopeFromObjectID(trustCenter.ID)
|
||||
|
||||
if err := r.esign.RecordEvent(
|
||||
ctx,
|
||||
scope,
|
||||
&esign.RecordEventRequest{
|
||||
SignatureID: input.SignatureID,
|
||||
EventType: input.EventType,
|
||||
EventSource: coredata.ElectronicSignatureEventSourceClient,
|
||||
ActorEmail: identity.EmailAddress,
|
||||
ActorIPAddr: actorIP,
|
||||
ActorUA: httpReq.UserAgent(),
|
||||
},
|
||||
); err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot record signing event", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return &types.RecordSigningEventPayload{Success: true}, nil
|
||||
}
|
||||
|
||||
// FileURL is the resolver for the fileUrl field.
|
||||
func (r *nonDisclosureAgreementResolver) FileURL(ctx context.Context, obj *types.NonDisclosureAgreement) (string, error) {
|
||||
trustCenter := complianceportal.CompliancePageFromContext(ctx)
|
||||
|
||||
if identity := authn.IdentityFromContext(ctx); identity != nil && r.esign != nil {
|
||||
scope := coredata.NewScopeFromObjectID(trustCenter.ID)
|
||||
trustService := r.trust
|
||||
|
||||
access, err := trustService.GetPortalAccess(ctx, scope, trustCenter.ID, identity.ID)
|
||||
if err == nil && access.ElectronicSignatureID != nil {
|
||||
fileURL, err := r.esign.GenerateSignatureFileURL(ctx, *access.ElectronicSignatureID, 15*time.Minute)
|
||||
if err == nil {
|
||||
return fileURL, nil
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot generate signature file URL, falling back to original NDA", log.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
scope := coredata.NewScopeFromObjectID(trustCenter.ID)
|
||||
trustService := r.trust
|
||||
|
||||
fileURL, err := trustService.GeneratePortalNDAFileURL(ctx, scope, trustCenter.ID, 15*time.Minute)
|
||||
if err != nil {
|
||||
return "", gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return fileURL, nil
|
||||
}
|
||||
|
||||
// ViewerSignature is the resolver for the viewerSignature field.
|
||||
func (r *nonDisclosureAgreementResolver) ViewerSignature(ctx context.Context, obj *types.NonDisclosureAgreement) (*types.ElectronicSignature, error) {
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
trustCenter := complianceportal.CompliancePageFromContext(ctx)
|
||||
scope := coredata.NewScopeFromObjectID(trustCenter.ID)
|
||||
trustService := r.trust
|
||||
|
||||
access, err := trustService.GetPortalAccess(ctx, scope, trustCenter.ID, identity.ID)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if access.ElectronicSignatureID == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
sig, err := r.esign.GetSignatureByID(ctx, scope, *access.ElectronicSignatureID)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return types.NewElectronicSignature(sig), nil
|
||||
}
|
||||
|
||||
// NonDisclosureAgreement returns schema.NonDisclosureAgreementResolver implementation.
|
||||
func (r *Resolver) NonDisclosureAgreement() schema.NonDisclosureAgreementResolver {
|
||||
return &nonDisclosureAgreementResolver{r}
|
||||
}
|
||||
|
||||
type nonDisclosureAgreementResolver struct{ *Resolver }
|
||||
173
pkg/server/api/complianceportal/v1/oauth_callback_handler.go
Normal file
173
pkg/server/api/complianceportal/v1/oauth_callback_handler.go
Normal file
@@ -0,0 +1,173 @@
|
||||
// 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 complianceportal_v1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"go.gearno.de/kit/httpserver"
|
||||
"go.gearno.de/kit/log"
|
||||
"go.probo.inc/probo/pkg/complianceportal/visitor"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
"go.probo.inc/probo/pkg/iam/oauth2"
|
||||
"go.probo.inc/probo/pkg/saferedirect"
|
||||
"go.probo.inc/probo/pkg/securecookie"
|
||||
"go.probo.inc/probo/pkg/server/api/authn"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal"
|
||||
)
|
||||
|
||||
var (
|
||||
errNotFound = errors.New("not found")
|
||||
errInvalidContinueURL = errors.New("invalid continue URL")
|
||||
errInternal = errors.New("internal server error")
|
||||
errInvalidOAuthRequest = errors.New("invalid oauth request")
|
||||
)
|
||||
|
||||
type OAuthCallbackHandler struct {
|
||||
iam *iam.Service
|
||||
visitor *visitor.Service
|
||||
sessionCookie *authn.Cookie
|
||||
safeRedirect *saferedirect.SafeRedirect
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func NewOAuthCallbackHandler(
|
||||
iamSvc *iam.Service,
|
||||
visitorSvc *visitor.Service,
|
||||
cookieConfig securecookie.Config,
|
||||
allowedHost saferedirect.AllowedHostFunc,
|
||||
logger *log.Logger,
|
||||
) *OAuthCallbackHandler {
|
||||
return &OAuthCallbackHandler{
|
||||
iam: iamSvc,
|
||||
visitor: visitorSvc,
|
||||
sessionCookie: authn.NewCookie(&cookieConfig),
|
||||
safeRedirect: saferedirect.New(allowedHost),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *OAuthCallbackHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
if oauthErr := r.URL.Query().Get("error"); oauthErr != "" {
|
||||
h.logger.WarnCtx(
|
||||
ctx,
|
||||
"oauth callback returned error",
|
||||
log.String("error", oauthErr),
|
||||
log.String("error_description", r.URL.Query().Get("error_description")),
|
||||
)
|
||||
httpserver.RenderError(w, http.StatusBadRequest, errInvalidOAuthRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
code := r.URL.Query().Get("code")
|
||||
stateToken := r.URL.Query().Get("state")
|
||||
if code == "" || stateToken == "" {
|
||||
httpserver.RenderError(w, http.StatusBadRequest, errInvalidOAuthRequest)
|
||||
return
|
||||
}
|
||||
|
||||
state, err := h.visitor.ConsumeOAuthState(ctx, stateToken)
|
||||
if err != nil {
|
||||
h.logger.WarnCtx(ctx, "invalid oauth state", log.Error(err))
|
||||
httpserver.RenderError(w, http.StatusBadRequest, errInvalidOAuthRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
portal := complianceportal.CompliancePageFromContext(ctx)
|
||||
portalBaseURL := complianceportal.CompliancePageBaseURLFromContext(ctx)
|
||||
if portal == nil || portalBaseURL == nil {
|
||||
httpserver.RenderError(w, http.StatusNotFound, errNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
clientID, err := complianceportal.CIMDClientIDURL(*portalBaseURL)
|
||||
if err != nil {
|
||||
h.logger.ErrorCtx(ctx, "cannot build cimd client_id", log.Error(err))
|
||||
httpserver.RenderError(w, http.StatusInternalServerError, errInternal)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
redirectURI, err := complianceportal.OAuthCallbackURL(*portalBaseURL)
|
||||
if err != nil {
|
||||
h.logger.ErrorCtx(ctx, "cannot build oauth redirect_uri", log.Error(err))
|
||||
httpserver.RenderError(w, http.StatusInternalServerError, errInternal)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
tokenResult, err := h.iam.OAuth2ServerService.ExchangeAuthorizationCode(
|
||||
ctx,
|
||||
clientID,
|
||||
code,
|
||||
redirectURI,
|
||||
state.CodeVerifier,
|
||||
)
|
||||
if err != nil {
|
||||
h.logger.WarnCtx(ctx, "cannot exchange authorization code", log.Error(err))
|
||||
httpserver.RenderError(w, http.StatusBadRequest, errInvalidOAuthRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
identityID, err := oauth2.ParseIDTokenIdentity(tokenResult.IDToken, state.Nonce)
|
||||
if err != nil {
|
||||
h.logger.WarnCtx(ctx, "cannot validate id token", log.Error(err))
|
||||
httpserver.RenderError(w, http.StatusBadRequest, errInvalidOAuthRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
host, ok := complianceportal.TrustedRequestHost(r)
|
||||
if !ok {
|
||||
httpserver.RenderError(w, http.StatusBadRequest, errInvalidOAuthRequest)
|
||||
return
|
||||
}
|
||||
|
||||
session, err := h.iam.AuthService.OpenRootSession(
|
||||
ctx,
|
||||
identityID,
|
||||
coredata.AuthMethodOIDC,
|
||||
coredata.SessionDataForHost(host),
|
||||
)
|
||||
if err != nil {
|
||||
h.logger.ErrorCtx(ctx, "cannot open session", log.Error(err))
|
||||
httpserver.RenderError(w, http.StatusInternalServerError, errInternal)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := h.visitor.ProvisionPortalMember(ctx, portal.ID, identityID); err != nil {
|
||||
h.logger.ErrorCtx(ctx, "cannot provision portal member", log.Error(err))
|
||||
httpserver.RenderError(w, http.StatusInternalServerError, errInternal)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
h.sessionCookie.Set(w, session)
|
||||
|
||||
continueURL := state.ContinueURL
|
||||
if continueURL == "" {
|
||||
continueURL = "/"
|
||||
}
|
||||
|
||||
h.safeRedirect.Redirect(w, r, continueURL, "/", http.StatusFound)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// 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 complianceportal_v1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"go.gearno.de/kit/httpserver"
|
||||
portal "go.probo.inc/probo/pkg/complianceportal"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal"
|
||||
)
|
||||
|
||||
type oauthClientMetadataHandler struct{}
|
||||
|
||||
func NewOAuthClientMetadataHandler() http.Handler {
|
||||
return &oauthClientMetadataHandler{}
|
||||
}
|
||||
|
||||
func (h *oauthClientMetadataHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
compliancePage := complianceportal.CompliancePageFromContext(r.Context())
|
||||
baseURL := complianceportal.CompliancePageBaseURLFromContext(r.Context())
|
||||
|
||||
if compliancePage == nil || baseURL == nil {
|
||||
httpserver.RenderError(w, http.StatusNotFound, errNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
doc, err := portal.BuildClientMetadataDocument(compliancePage, *baseURL)
|
||||
if err != nil {
|
||||
httpserver.RenderError(w, http.StatusInternalServerError, errInternal)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Cache-Control", "public, max-age=300")
|
||||
_ = json.NewEncoder(w).Encode(doc)
|
||||
}
|
||||
116
pkg/server/api/complianceportal/v1/oauth_initiate_handler.go
Normal file
116
pkg/server/api/complianceportal/v1/oauth_initiate_handler.go
Normal file
@@ -0,0 +1,116 @@
|
||||
// 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 complianceportal_v1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"go.gearno.de/kit/httpclient"
|
||||
"go.gearno.de/kit/httpserver"
|
||||
"go.gearno.de/kit/log"
|
||||
"go.probo.inc/probo/pkg/baseurl"
|
||||
"go.probo.inc/probo/pkg/complianceportal/visitor"
|
||||
"go.probo.inc/probo/pkg/iam/oauth2"
|
||||
"go.probo.inc/probo/pkg/saferedirect"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal"
|
||||
)
|
||||
|
||||
type OAuthInitiateHandler struct {
|
||||
proboBaseURL *baseurl.BaseURL
|
||||
visitor *visitor.Service
|
||||
safeRedirect *saferedirect.SafeRedirect
|
||||
httpClient *http.Client
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func NewOAuthInitiateHandler(
|
||||
proboBaseURL *baseurl.BaseURL,
|
||||
visitorSvc *visitor.Service,
|
||||
allowedHost saferedirect.AllowedHostFunc,
|
||||
logger *log.Logger,
|
||||
) *OAuthInitiateHandler {
|
||||
return &OAuthInitiateHandler{
|
||||
proboBaseURL: proboBaseURL,
|
||||
visitor: visitorSvc,
|
||||
safeRedirect: saferedirect.New(allowedHost),
|
||||
httpClient: httpclient.DefaultClient(
|
||||
httpclient.WithLogger(logger),
|
||||
httpclient.WithSSRFProtection(),
|
||||
),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *OAuthInitiateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
portalBaseURL := complianceportal.CompliancePageBaseURLFromContext(ctx)
|
||||
if portalBaseURL == nil {
|
||||
httpserver.RenderError(w, http.StatusNotFound, errNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
continueURL := r.URL.Query().Get("continue")
|
||||
if continueURL == "" {
|
||||
continueURL = "/overview"
|
||||
}
|
||||
|
||||
safeContinue, ok := h.safeRedirect.Validate(ctx, continueURL)
|
||||
if !ok {
|
||||
httpserver.RenderError(w, http.StatusBadRequest, errInvalidContinueURL)
|
||||
return
|
||||
}
|
||||
|
||||
clientID, err := complianceportal.CIMDClientIDURL(*portalBaseURL)
|
||||
if err != nil {
|
||||
h.logger.ErrorCtx(ctx, "cannot build cimd client_id", log.Error(err))
|
||||
httpserver.RenderError(w, http.StatusInternalServerError, errInternal)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
redirectURI, err := complianceportal.OAuthCallbackURL(*portalBaseURL)
|
||||
if err != nil {
|
||||
h.logger.ErrorCtx(ctx, "cannot build oauth redirect_uri", log.Error(err))
|
||||
httpserver.RenderError(w, http.StatusInternalServerError, errInternal)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
metadata, err := oauth2.FetchServerMetadata(ctx, h.httpClient, h.proboBaseURL.String())
|
||||
if err != nil {
|
||||
h.logger.ErrorCtx(ctx, "cannot fetch discovery metadata", log.Error(err))
|
||||
httpserver.RenderError(w, http.StatusInternalServerError, errInternal)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
authorizeURL, err := h.visitor.InitiateOAuthAuthorizeURL(
|
||||
ctx,
|
||||
metadata.AuthorizationEndpoint.String(),
|
||||
clientID,
|
||||
redirectURI,
|
||||
[]string{complianceportal.VisitorOAuthScope},
|
||||
safeContinue,
|
||||
)
|
||||
if err != nil {
|
||||
h.logger.ErrorCtx(ctx, "cannot initiate oauth authorize", log.Error(err))
|
||||
httpserver.RenderError(w, http.StatusInternalServerError, errInternal)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(w, r, authorizeURL, http.StatusFound)
|
||||
}
|
||||
56
pkg/server/api/complianceportal/v1/resolver.go
Normal file
56
pkg/server/api/complianceportal/v1/resolver.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// 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.
|
||||
|
||||
//go:generate go run github.com/99designs/gqlgen generate
|
||||
|
||||
package complianceportal_v1
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.gearno.de/kit/log"
|
||||
"go.probo.inc/probo/pkg/baseurl"
|
||||
trust "go.probo.inc/probo/pkg/complianceportal/visitor"
|
||||
"go.probo.inc/probo/pkg/esign"
|
||||
"go.probo.inc/probo/pkg/filemanager"
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
"go.probo.inc/probo/pkg/mailman"
|
||||
"go.probo.inc/probo/pkg/resourcealias"
|
||||
"go.probo.inc/probo/pkg/server/api/authn"
|
||||
)
|
||||
|
||||
type (
|
||||
TrustAuthConfig struct {
|
||||
CookieName string
|
||||
CookieDomain string
|
||||
CookieDuration time.Duration
|
||||
TokenDuration time.Duration
|
||||
ReportURLDuration time.Duration
|
||||
Scope string
|
||||
TokenType string
|
||||
CookieSecure bool
|
||||
}
|
||||
|
||||
Resolver struct {
|
||||
trust *trust.Service
|
||||
resourceAlias *resourcealias.Service
|
||||
fileManager *filemanager.Service
|
||||
esign *esign.Service
|
||||
mailman *mailman.Service
|
||||
logger *log.Logger
|
||||
iam *iam.Service
|
||||
sessionCookie *authn.Cookie
|
||||
baseURL *baseurl.BaseURL
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package complianceportal_v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.gearno.de/kit/log"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
)
|
||||
|
||||
func (r *Resolver) ResourceAliasResolver(
|
||||
ctx context.Context,
|
||||
storageResourceID gid.GID,
|
||||
) (*string, error) {
|
||||
trustCenter := complianceportal.CompliancePageFromContext(ctx)
|
||||
scope := coredata.NewScopeFromObjectID(trustCenter.ID)
|
||||
|
||||
alias, err := r.resourceAlias.GetByResourceID(ctx, scope, storageResourceID)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot load resource alias", log.Error(err))
|
||||
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return alias, nil
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package complianceportal_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"
|
||||
|
||||
"go.gearno.de/kit/log"
|
||||
trust "go.probo.inc/probo/pkg/complianceportal/visitor"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/server/api/authn"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal"
|
||||
"go.probo.inc/probo/pkg/server/api/complianceportal/v1/types"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
)
|
||||
|
||||
// CreateRightsRequest is the resolver for the createRightsRequest field.
|
||||
func (r *mutationResolver) CreateRightsRequest(ctx context.Context, input types.CreateRightsRequestInput) (*types.CreateRightsRequestPayload, error) {
|
||||
identity := authn.IdentityFromContext(ctx)
|
||||
if identity == nil || !identity.EmailAddressVerified {
|
||||
// The request is attributed to the viewer's email, so the email must be
|
||||
// verified — an authenticated-but-unverified identity is not enough.
|
||||
return nil, gqlutils.Unauthenticatedf(ctx, "a verified email is required to submit a request")
|
||||
}
|
||||
|
||||
compliancePage := complianceportal.CompliancePageFromContext(ctx)
|
||||
scope := coredata.NewScopeFromObjectID(compliancePage.OrganizationID)
|
||||
|
||||
rightsRequest, err := r.trust.CreateRightsRequest(
|
||||
ctx,
|
||||
scope,
|
||||
&trust.CreateRightsRequest{
|
||||
OrganizationID: compliancePage.OrganizationID,
|
||||
RequestType: input.RequestType,
|
||||
DataSubject: input.DataSubject,
|
||||
Contact: identity.EmailAddress.String(),
|
||||
Details: input.Details,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot create rights request", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return &types.CreateRightsRequestPayload{
|
||||
RightsRequestEdge: types.NewRightsRequestEdge(
|
||||
rightsRequest,
|
||||
coredata.RightsRequestOrderFieldCreatedAt,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
3
pkg/server/api/complianceportal/v1/schema/.gitignore
vendored
Normal file
3
pkg/server/api/complianceportal/v1/schema/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*
|
||||
!.gitignore
|
||||
!doc.go
|
||||
23
pkg/server/api/complianceportal/v1/schema/doc.go
Normal file
23
pkg/server/api/complianceportal/v1/schema/doc.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// Package schema contains the generated GraphQL executable schema for the
|
||||
// Trust API.
|
||||
package schema
|
||||
100
pkg/server/api/complianceportal/v1/spa.go
Normal file
100
pkg/server/api/complianceportal/v1/spa.go
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package complianceportal_v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
|
||||
complianceportalstatics "go.probo.inc/probo/apps/compliance-portal"
|
||||
"go.probo.inc/probo/pkg/server/statichandler"
|
||||
)
|
||||
|
||||
type (
|
||||
HeadData struct {
|
||||
Title string
|
||||
Description string
|
||||
OGURL string
|
||||
FaviconURL string
|
||||
}
|
||||
|
||||
HeadDataFunc func(r *http.Request) HeadData
|
||||
|
||||
Server struct {
|
||||
*statichandler.Server
|
||||
}
|
||||
)
|
||||
|
||||
func NewServer(headDataFunc HeadDataFunc) (*Server, error) {
|
||||
renderer, err := buildIndexRenderer(headDataFunc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gzipOptions := statichandler.GzipOptions{
|
||||
EnableFileTypeCheck: true,
|
||||
FileTypes: []string{".js", ".css", ".html"},
|
||||
}
|
||||
|
||||
spaServer, err := statichandler.NewServer(
|
||||
complianceportalstatics.StaticFiles,
|
||||
"dist",
|
||||
gzipOptions,
|
||||
statichandler.WithFileRenderer("/index.html", renderer),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Server{Server: spaServer}, nil
|
||||
}
|
||||
|
||||
func buildIndexRenderer(headDataFunc HeadDataFunc) (statichandler.FileRenderer, error) {
|
||||
subFS, err := fs.Sub(complianceportalstatics.StaticFiles, "dist")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot open dist: %w", err)
|
||||
}
|
||||
|
||||
indexBytes, err := fs.ReadFile(subFS, "index.html")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot read index.html: %w", err)
|
||||
}
|
||||
|
||||
tmpl, err := template.New("index").Parse(string(indexBytes))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse index.html template: %w", err)
|
||||
}
|
||||
|
||||
return func(w io.Writer, r *http.Request) error {
|
||||
return tmpl.Execute(w, headDataFunc(r))
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
s.Server.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (s *Server) ServeSPA(w http.ResponseWriter, r *http.Request) {
|
||||
s.Server.ServeSPA(w, r)
|
||||
}
|
||||
1124
pkg/server/api/complianceportal/v1/trust_center_resolvers.go
Normal file
1124
pkg/server/api/complianceportal/v1/trust_center_resolvers.go
Normal file
File diff suppressed because it is too large
Load Diff
54
pkg/server/api/complianceportal/v1/types/audit.go
Normal file
54
pkg/server/api/complianceportal/v1/types/audit.go
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
func NewAuditConnection(
|
||||
p *page.Page[*coredata.Audit, coredata.AuditOrderField],
|
||||
) *AuditConnection {
|
||||
edges := make([]*AuditEdge, len(p.Data))
|
||||
for i, audit := range p.Data {
|
||||
edges[i] = NewAuditEdge(audit, p.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &AuditConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(p),
|
||||
}
|
||||
}
|
||||
|
||||
func NewAudit(a *coredata.Audit) *Audit {
|
||||
return &Audit{
|
||||
ID: a.ID,
|
||||
Name: a.Name,
|
||||
}
|
||||
}
|
||||
|
||||
func NewAuditEdge(a *coredata.Audit, orderField coredata.AuditOrderField) *AuditEdge {
|
||||
return &AuditEdge{
|
||||
Node: NewAudit(a),
|
||||
Cursor: a.CursorKey(orderField),
|
||||
}
|
||||
}
|
||||
32
pkg/server/api/complianceportal/v1/types/audit_report.go
Normal file
32
pkg/server/api/complianceportal/v1/types/audit_report.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
)
|
||||
|
||||
func NewAuditReport(f *coredata.File) *AuditReport {
|
||||
return &AuditReport{
|
||||
ID: f.ID,
|
||||
FileName: f.FileName,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
func NewComplianceCustomLink(c *coredata.ComplianceCustomLink) *ComplianceCustomLink {
|
||||
return &ComplianceCustomLink{
|
||||
ID: c.ID,
|
||||
Name: c.Name,
|
||||
URL: c.URL,
|
||||
Rank: c.Rank,
|
||||
}
|
||||
}
|
||||
|
||||
func NewComplianceCustomLinkConnection(
|
||||
p *page.Page[*coredata.ComplianceCustomLink, coredata.ComplianceCustomLinkOrderField],
|
||||
) *ComplianceCustomLinkConnection {
|
||||
edges := make([]*ComplianceCustomLinkEdge, len(p.Data))
|
||||
|
||||
for i, item := range p.Data {
|
||||
edges[i] = NewComplianceCustomLinkEdge(item, p.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &ComplianceCustomLinkConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(p),
|
||||
}
|
||||
}
|
||||
|
||||
func NewComplianceCustomLinkEdge(c *coredata.ComplianceCustomLink, orderBy coredata.ComplianceCustomLinkOrderField) *ComplianceCustomLinkEdge {
|
||||
return &ComplianceCustomLinkEdge{
|
||||
Cursor: c.CursorKey(orderBy),
|
||||
Node: NewComplianceCustomLink(c),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
type ComplianceFrameworkConnection struct {
|
||||
Edges []*ComplianceFrameworkEdge `json:"edges"`
|
||||
PageInfo *PageInfo `json:"pageInfo"`
|
||||
}
|
||||
|
||||
func NewComplianceFramework(cf *coredata.ComplianceFramework) *ComplianceFramework {
|
||||
return &ComplianceFramework{
|
||||
ID: cf.ID,
|
||||
}
|
||||
}
|
||||
|
||||
func NewComplianceFrameworkEdge(cf *coredata.ComplianceFramework) *ComplianceFrameworkEdge {
|
||||
return &ComplianceFrameworkEdge{
|
||||
Cursor: cf.CursorKey(coredata.ComplianceFrameworkOrderFieldRank),
|
||||
Node: NewComplianceFramework(cf),
|
||||
}
|
||||
}
|
||||
|
||||
func NewComplianceFrameworkConnection(
|
||||
p *page.Page[*coredata.ComplianceFramework, coredata.ComplianceFrameworkOrderField],
|
||||
) *ComplianceFrameworkConnection {
|
||||
edges := make([]*ComplianceFrameworkEdge, len(p.Data))
|
||||
for i, cf := range p.Data {
|
||||
edges[i] = NewComplianceFrameworkEdge(cf)
|
||||
}
|
||||
|
||||
return &ComplianceFrameworkConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(p),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
func NewCompliancePortalCommitmentGroup(g *coredata.CompliancePortalCommitmentGroup) *CompliancePortalCommitmentGroup {
|
||||
return &CompliancePortalCommitmentGroup{
|
||||
ID: g.ID,
|
||||
Title: g.Title,
|
||||
Description: g.Description,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCompliancePortalCommitmentGroupConnection(
|
||||
p *page.Page[*coredata.CompliancePortalCommitmentGroup, coredata.CompliancePortalCommitmentGroupOrderField],
|
||||
) *CompliancePortalCommitmentGroupConnection {
|
||||
edges := make([]*CompliancePortalCommitmentGroupEdge, len(p.Data))
|
||||
|
||||
for i, item := range p.Data {
|
||||
edges[i] = NewCompliancePortalCommitmentGroupEdge(item, p.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &CompliancePortalCommitmentGroupConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(p),
|
||||
}
|
||||
}
|
||||
|
||||
func NewCompliancePortalCommitmentGroupEdge(
|
||||
g *coredata.CompliancePortalCommitmentGroup,
|
||||
orderBy coredata.CompliancePortalCommitmentGroupOrderField,
|
||||
) *CompliancePortalCommitmentGroupEdge {
|
||||
return &CompliancePortalCommitmentGroupEdge{
|
||||
Cursor: g.CursorKey(orderBy),
|
||||
Node: NewCompliancePortalCommitmentGroup(g),
|
||||
}
|
||||
}
|
||||
|
||||
func NewCompliancePortalCommitment(c *coredata.CompliancePortalCommitment) *CompliancePortalCommitment {
|
||||
return &CompliancePortalCommitment{
|
||||
ID: c.ID,
|
||||
Icon: c.Icon,
|
||||
Eyebrow: c.Eyebrow,
|
||||
Title: c.Title,
|
||||
Description: c.Description,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCompliancePortalCommitmentConnection(
|
||||
p *page.Page[*coredata.CompliancePortalCommitment, coredata.CompliancePortalCommitmentOrderField],
|
||||
) *CompliancePortalCommitmentConnection {
|
||||
edges := make([]*CompliancePortalCommitmentEdge, len(p.Data))
|
||||
|
||||
for i, item := range p.Data {
|
||||
edges[i] = NewCompliancePortalCommitmentEdge(item, p.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &CompliancePortalCommitmentConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(p),
|
||||
}
|
||||
}
|
||||
|
||||
func NewCompliancePortalCommitmentEdge(
|
||||
c *coredata.CompliancePortalCommitment,
|
||||
orderBy coredata.CompliancePortalCommitmentOrderField,
|
||||
) *CompliancePortalCommitmentEdge {
|
||||
return &CompliancePortalCommitmentEdge{
|
||||
Cursor: c.CursorKey(orderBy),
|
||||
Node: NewCompliancePortalCommitment(c),
|
||||
}
|
||||
}
|
||||
36
pkg/server/api/complianceportal/v1/types/cursorkey.go
Normal file
36
pkg/server/api/complianceportal/v1/types/cursorkey.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils/types/cursor"
|
||||
)
|
||||
|
||||
func NewCursor[O page.OrderField](
|
||||
first *int,
|
||||
after *page.CursorKey,
|
||||
last *int,
|
||||
before *page.CursorKey,
|
||||
orderBy page.OrderBy[O],
|
||||
) *page.Cursor[O] {
|
||||
return cursor.NewCursor(first, after, last, before, orderBy)
|
||||
}
|
||||
55
pkg/server/api/complianceportal/v1/types/document.go
Normal file
55
pkg/server/api/complianceportal/v1/types/document.go
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
func NewDocumentConnection(
|
||||
p *page.Page[*coredata.Document, coredata.DocumentOrderField],
|
||||
) *DocumentConnection {
|
||||
edges := make([]*DocumentEdge, len(p.Data))
|
||||
for i, document := range p.Data {
|
||||
edges[i] = NewDocumentEdge(document, p.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &DocumentConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(p),
|
||||
}
|
||||
}
|
||||
|
||||
func NewDocument(d *coredata.Document) *Document {
|
||||
return &Document{
|
||||
ID: d.ID,
|
||||
Title: d.Title,
|
||||
DocumentType: d.DocumentType,
|
||||
}
|
||||
}
|
||||
|
||||
func NewDocumentEdge(d *coredata.Document, orderField coredata.DocumentOrderField) *DocumentEdge {
|
||||
return &DocumentEdge{
|
||||
Node: NewDocument(d),
|
||||
Cursor: d.CursorKey(orderField),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
)
|
||||
|
||||
func NewElectronicSignature(es *coredata.ElectronicSignature) *ElectronicSignature {
|
||||
return &ElectronicSignature{
|
||||
ID: es.ID,
|
||||
Status: es.Status,
|
||||
DocumentType: es.DocumentType,
|
||||
ConsentText: es.ConsentText,
|
||||
LastError: es.LastError,
|
||||
SignedAt: es.SignedAt,
|
||||
CreatedAt: es.CreatedAt,
|
||||
UpdatedAt: es.UpdatedAt,
|
||||
}
|
||||
}
|
||||
38
pkg/server/api/complianceportal/v1/types/file.go
Normal file
38
pkg/server/api/complianceportal/v1/types/file.go
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/filemanager"
|
||||
)
|
||||
|
||||
func NewFile(r *coredata.File, files *filemanager.Service) *File {
|
||||
return &File{
|
||||
ID: r.ID,
|
||||
MimeType: r.MimeType,
|
||||
FileName: r.FileName,
|
||||
Size: r.FileSize,
|
||||
DownloadURL: files.GenerateFileURL(r),
|
||||
CreatedAt: r.CreatedAt,
|
||||
UpdatedAt: r.UpdatedAt,
|
||||
}
|
||||
}
|
||||
32
pkg/server/api/complianceportal/v1/types/framework.go
Normal file
32
pkg/server/api/complianceportal/v1/types/framework.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
)
|
||||
|
||||
func NewFramework(f *coredata.Framework) *Framework {
|
||||
return &Framework{
|
||||
ID: f.ID,
|
||||
Name: f.Name,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
)
|
||||
|
||||
func NewMailingListSubscriber(s *coredata.MailingListSubscriber) *MailingListSubscriber {
|
||||
return &MailingListSubscriber{
|
||||
ID: s.ID,
|
||||
FullName: s.FullName,
|
||||
Email: s.Email,
|
||||
Status: s.Status,
|
||||
CreatedAt: s.CreatedAt,
|
||||
UpdatedAt: s.UpdatedAt,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
func NewMailingListUpdate(mlu *coredata.MailingListUpdate) *MailingListUpdate {
|
||||
return &MailingListUpdate{
|
||||
ID: mlu.ID,
|
||||
Title: mlu.Title,
|
||||
Body: mlu.Body,
|
||||
UpdatedAt: mlu.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func NewMailingListUpdateEdge(mlu *coredata.MailingListUpdate) *MailingListUpdateEdge {
|
||||
return &MailingListUpdateEdge{
|
||||
Cursor: mlu.CursorKey(coredata.MailingListUpdateOrderFieldUpdatedAt),
|
||||
Node: NewMailingListUpdate(mlu),
|
||||
}
|
||||
}
|
||||
|
||||
func NewMailingListUpdateConnection(
|
||||
p *page.Page[*coredata.MailingListUpdate, coredata.MailingListUpdateOrderField],
|
||||
) *MailingListUpdateConnection {
|
||||
edges := make([]*MailingListUpdateEdge, len(p.Data))
|
||||
for i, mlu := range p.Data {
|
||||
edges[i] = NewMailingListUpdateEdge(mlu)
|
||||
}
|
||||
|
||||
return &MailingListUpdateConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(p),
|
||||
}
|
||||
}
|
||||
37
pkg/server/api/complianceportal/v1/types/pageinfo.go
Normal file
37
pkg/server/api/complianceportal/v1/types/pageinfo.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils/types/pageinfo"
|
||||
)
|
||||
|
||||
func NewPageInfo[T page.Paginable[O], O page.OrderField](p *page.Page[T, O]) *PageInfo {
|
||||
data := pageinfo.NewPageInfo(p)
|
||||
|
||||
return &PageInfo{
|
||||
HasNextPage: data.HasNextPage,
|
||||
HasPreviousPage: data.HasPreviousPage,
|
||||
StartCursor: data.StartCursor,
|
||||
EndCursor: data.EndCursor,
|
||||
}
|
||||
}
|
||||
66
pkg/server/api/complianceportal/v1/types/rights_request.go
Normal file
66
pkg/server/api/complianceportal/v1/types/rights_request.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
func NewRightsRequest(rr *coredata.RightsRequest) *RightsRequest {
|
||||
return &RightsRequest{
|
||||
ID: rr.ID,
|
||||
RequestType: rr.RequestType,
|
||||
RequestState: rr.RequestState,
|
||||
DataSubject: rr.DataSubject,
|
||||
Contact: rr.Contact,
|
||||
Details: rr.Details,
|
||||
Deadline: rr.Deadline,
|
||||
ActionTaken: rr.ActionTaken,
|
||||
CreatedAt: rr.CreatedAt,
|
||||
UpdatedAt: rr.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func NewRightsRequestEdge(
|
||||
rr *coredata.RightsRequest,
|
||||
orderBy coredata.RightsRequestOrderField,
|
||||
) *RightsRequestEdge {
|
||||
return &RightsRequestEdge{
|
||||
Cursor: rr.CursorKey(orderBy),
|
||||
Node: NewRightsRequest(rr),
|
||||
}
|
||||
}
|
||||
|
||||
func NewRightsRequestConnection(
|
||||
p *page.Page[*coredata.RightsRequest, coredata.RightsRequestOrderField],
|
||||
) *RightsRequestConnection {
|
||||
edges := make([]*RightsRequestEdge, len(p.Data))
|
||||
|
||||
for i, item := range p.Data {
|
||||
edges[i] = NewRightsRequestEdge(item, p.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &RightsRequestConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(p),
|
||||
}
|
||||
}
|
||||
79
pkg/server/api/complianceportal/v1/types/third_party.go
Normal file
79
pkg/server/api/complianceportal/v1/types/third_party.go
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
type (
|
||||
SubprocessorConnection struct {
|
||||
TotalCount int
|
||||
Edges []*SubprocessorEdge
|
||||
PageInfo PageInfo
|
||||
|
||||
Resolver any
|
||||
ParentID gid.GID
|
||||
Filter *coredata.ThirdPartyFilter
|
||||
}
|
||||
)
|
||||
|
||||
func NewSubprocessorConnection(
|
||||
p *page.Page[*coredata.ThirdParty, coredata.ThirdPartyOrderField],
|
||||
parentType any,
|
||||
parentID gid.GID,
|
||||
filter *coredata.ThirdPartyFilter,
|
||||
) *SubprocessorConnection {
|
||||
edges := make([]*SubprocessorEdge, len(p.Data))
|
||||
for i, thirdParty := range p.Data {
|
||||
edges[i] = NewSubprocessorEdge(thirdParty, p.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &SubprocessorConnection{
|
||||
Edges: edges,
|
||||
PageInfo: *NewPageInfo(p),
|
||||
|
||||
Resolver: parentType,
|
||||
ParentID: parentID,
|
||||
Filter: filter,
|
||||
}
|
||||
}
|
||||
|
||||
func NewSubprocessor(v *coredata.ThirdParty) *Subprocessor {
|
||||
return &Subprocessor{
|
||||
ID: v.ID,
|
||||
Name: v.Name,
|
||||
Description: v.Description,
|
||||
Category: v.Category,
|
||||
WebsiteURL: v.WebsiteURL,
|
||||
PrivacyPolicyURL: v.PrivacyPolicyURL,
|
||||
Countries: []coredata.CountryCode(v.Countries),
|
||||
}
|
||||
}
|
||||
|
||||
func NewSubprocessorEdge(v *coredata.ThirdParty, orderField coredata.ThirdPartyOrderField) *SubprocessorEdge {
|
||||
return &SubprocessorEdge{
|
||||
Node: NewSubprocessor(v),
|
||||
Cursor: v.CursorKey(orderField),
|
||||
}
|
||||
}
|
||||
44
pkg/server/api/complianceportal/v1/types/trust_center.go
Normal file
44
pkg/server/api/complianceportal/v1/types/trust_center.go
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
)
|
||||
|
||||
func NewTrustCenter(tc *coredata.TrustCenter) *TrustCenter {
|
||||
return &TrustCenter{
|
||||
ID: tc.ID,
|
||||
Active: tc.Active,
|
||||
Slug: tc.Slug,
|
||||
Title: tc.Title,
|
||||
Description: tc.Description,
|
||||
WebsiteURL: tc.WebsiteURL,
|
||||
Email: tc.Email,
|
||||
HeadquarterAddress: tc.HeadquarterAddress,
|
||||
}
|
||||
}
|
||||
|
||||
func NewNonDisclosureAgreement(file *coredata.File) *NonDisclosureAgreement {
|
||||
return &NonDisclosureAgreement{
|
||||
FileName: file.FileName,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
func NewTrustCenterFileConnection(
|
||||
p *page.Page[*coredata.TrustCenterFile, coredata.TrustCenterFileOrderField],
|
||||
) *TrustCenterFileConnection {
|
||||
edges := make([]*TrustCenterFileEdge, len(p.Data))
|
||||
for i, trustCenterFile := range p.Data {
|
||||
edges[i] = NewTrustCenterFileEdge(trustCenterFile, p.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &TrustCenterFileConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(p),
|
||||
}
|
||||
}
|
||||
|
||||
func NewTrustCenterFile(f *coredata.TrustCenterFile) *TrustCenterFile {
|
||||
return &TrustCenterFile{
|
||||
ID: f.ID,
|
||||
Name: f.Name,
|
||||
Category: f.Category,
|
||||
}
|
||||
}
|
||||
|
||||
func NewTrustCenterFileEdge(f *coredata.TrustCenterFile, orderField coredata.TrustCenterFileOrderField) *TrustCenterFileEdge {
|
||||
return &TrustCenterFileEdge{
|
||||
Node: NewTrustCenterFile(f),
|
||||
Cursor: f.CursorKey(orderField),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
func NewTrustCenterReference(tcc *coredata.TrustCenterReference) *TrustCenterReference {
|
||||
return &TrustCenterReference{
|
||||
ID: tcc.ID,
|
||||
Name: tcc.Name,
|
||||
Description: tcc.Description,
|
||||
WebsiteURL: tcc.WebsiteURL,
|
||||
}
|
||||
}
|
||||
|
||||
func NewTrustCenterReferenceConnection(p *page.Page[*coredata.TrustCenterReference, coredata.TrustCenterReferenceOrderField]) *TrustCenterReferenceConnection {
|
||||
edges := make([]*TrustCenterReferenceEdge, len(p.Data))
|
||||
|
||||
for i, item := range p.Data {
|
||||
edges[i] = NewTrustCenterReferenceEdge(item, p.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &TrustCenterReferenceConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(p),
|
||||
}
|
||||
}
|
||||
|
||||
func NewTrustCenterReferenceEdge(tcc *coredata.TrustCenterReference, orderBy coredata.TrustCenterReferenceOrderField) *TrustCenterReferenceEdge {
|
||||
return &TrustCenterReferenceEdge{
|
||||
Cursor: tcc.CursorKey(orderBy),
|
||||
Node: NewTrustCenterReference(tcc),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user