Split each API's monolithic schema.graphql into per-coredata-model
files under graphql/ subdirectories. gqlgen's follow-schema layout
with {name}.resolvers.go template generates one resolver file per
schema file. Relay uses schema + schemaExtensions to load the split
files.
Connect API: 8 files (base, session, organization, profile,
personal_api_key, saml, scim, audit_log)
Trust API: 5 files (base, trust_center, auth, nda, mailing_list)
Console API: 25 files covering all domain entities
Types extended across files (Organization, Mutation, Viewer,
TrustCenter, Identity) are defined in base.graphql as required by
Relay's schemaExtensions.
Signed-off-by: Émile Ré <emile@getprobo.com>
1902 lines
67 KiB
Go
1902 lines
67 KiB
Go
package console_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.87
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
|
|
"github.com/vikstrous/dataloadgen"
|
|
"go.gearno.de/kit/log"
|
|
"go.probo.inc/probo/pkg/coredata"
|
|
"go.probo.inc/probo/pkg/gid"
|
|
"go.probo.inc/probo/pkg/iam"
|
|
"go.probo.inc/probo/pkg/page"
|
|
"go.probo.inc/probo/pkg/probo"
|
|
"go.probo.inc/probo/pkg/server/api/authn"
|
|
"go.probo.inc/probo/pkg/server/api/console/v1/dataloader"
|
|
"go.probo.inc/probo/pkg/server/api/console/v1/schema"
|
|
"go.probo.inc/probo/pkg/server/api/console/v1/types"
|
|
"go.probo.inc/probo/pkg/server/gqlutils"
|
|
"go.probo.inc/probo/pkg/validator"
|
|
)
|
|
|
|
// Organization is the resolver for the organization field.
|
|
func (r *documentResolver) Organization(ctx context.Context, obj *types.Document) (*types.Organization, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
loaders := dataloader.FromContext(ctx)
|
|
|
|
organization, err := loaders.Organization.Load(ctx, obj.Organization.ID)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
|
|
return nil, gqlutils.NotFound(ctx, err)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewOrganization(organization), nil
|
|
}
|
|
|
|
// Versions is the resolver for the versions field.
|
|
func (r *documentResolver) Versions(ctx context.Context, obj *types.Document, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentVersionOrderBy, filter *types.DocumentVersionFilter) (*types.DocumentVersionConnection, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionList); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
pageOrderBy := page.OrderBy[coredata.DocumentVersionOrderField]{
|
|
Field: coredata.DocumentVersionOrderFieldCreatedAt,
|
|
Direction: page.OrderDirectionDesc,
|
|
}
|
|
if orderBy != nil {
|
|
pageOrderBy = page.OrderBy[coredata.DocumentVersionOrderField]{
|
|
Field: orderBy.Field,
|
|
Direction: orderBy.Direction,
|
|
}
|
|
}
|
|
|
|
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
|
|
|
versionFilter := coredata.NewDocumentVersionFilter()
|
|
if filter != nil && len(filter.Statuses) > 0 {
|
|
versionFilter = versionFilter.WithStatuses(filter.Statuses...)
|
|
}
|
|
|
|
page, err := prb.Documents.ListVersions(ctx, obj.ID, cursor, versionFilter)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot list document versions", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewDocumentVersionConnection(page, r, obj.ID), nil
|
|
}
|
|
|
|
// Controls is the resolver for the controls field.
|
|
func (r *documentResolver) Controls(ctx context.Context, obj *types.Document, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionControlList); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
pageOrderBy := page.OrderBy[coredata.ControlOrderField]{
|
|
Field: coredata.ControlOrderFieldCreatedAt,
|
|
Direction: page.OrderDirectionDesc,
|
|
}
|
|
if orderBy != nil {
|
|
pageOrderBy = page.OrderBy[coredata.ControlOrderField]{
|
|
Field: orderBy.Field,
|
|
Direction: orderBy.Direction,
|
|
}
|
|
}
|
|
|
|
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
|
|
|
var controlFilter = coredata.NewControlFilter(nil)
|
|
if filter != nil {
|
|
controlFilter = coredata.NewControlFilter(filter.Query)
|
|
}
|
|
|
|
page, err := prb.Controls.ListForDocumentID(ctx, obj.ID, cursor, controlFilter)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot list document controls", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewControlConnection(page, r, obj.ID, controlFilter), nil
|
|
}
|
|
|
|
// DefaultApprovers is the resolver for the defaultApprovers field.
|
|
func (r *documentResolver) DefaultApprovers(ctx context.Context, obj *types.Document) ([]*types.Profile, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionDocumentGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
profiles, err := prb.Documents.GetDefaultApprovers(ctx, obj.ID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot get default approvers", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
result := make([]*types.Profile, len(profiles))
|
|
for i, p := range profiles {
|
|
result[i] = types.NewProfile(p)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// Permission is the resolver for the permission field.
|
|
func (r *documentResolver) Permission(ctx context.Context, obj *types.Document, action string) (bool, error) {
|
|
return r.Resolver.Permission(ctx, obj, action)
|
|
}
|
|
|
|
// TotalCount is the resolver for the totalCount field.
|
|
func (r *documentConnectionResolver) TotalCount(ctx context.Context, obj *types.DocumentConnection) (int, error) {
|
|
if err := r.authorize(ctx, obj.ParentID, probo.ActionDocumentList); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ParentID.TenantID())
|
|
|
|
switch obj.Resolver.(type) {
|
|
case *controlResolver:
|
|
count, err := prb.Documents.CountForControlID(ctx, obj.ParentID, obj.Filters)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot count controls", log.Error(err))
|
|
return 0, gqlutils.Internal(ctx)
|
|
}
|
|
return count, nil
|
|
case *organizationResolver:
|
|
count, err := prb.Documents.CountForOrganizationID(ctx, obj.ParentID, obj.Filters)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot count documents", log.Error(err))
|
|
return 0, gqlutils.Internal(ctx)
|
|
}
|
|
return count, nil
|
|
case *riskResolver:
|
|
count, err := prb.Documents.CountForRiskID(ctx, obj.ParentID, obj.Filters)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot count risks", log.Error(err))
|
|
return 0, gqlutils.Internal(ctx)
|
|
}
|
|
return count, nil
|
|
case *measureResolver:
|
|
count, err := prb.Documents.CountForMeasureID(ctx, obj.ParentID, obj.Filters)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot count documents", log.Error(err))
|
|
return 0, gqlutils.Internal(ctx)
|
|
}
|
|
return count, nil
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "unsupported resolver")
|
|
return 0, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
// Document is the resolver for the document field.
|
|
func (r *documentVersionResolver) Document(ctx context.Context, obj *types.DocumentVersion) (*types.Document, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionDocumentGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
loaders := dataloader.FromContext(ctx)
|
|
|
|
document, err := loaders.Document.Load(ctx, obj.Document.ID)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
|
|
return nil, gqlutils.NotFound(ctx, err)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get document", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewDocument(document), nil
|
|
}
|
|
|
|
// Approvers is the resolver for the approvers field.
|
|
func (r *documentVersionResolver) Approvers(ctx context.Context, obj *types.DocumentVersion, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ProfileOrderBy) (*types.ProfileConnection, error) {
|
|
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileList); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if gqlutils.OnlyTotalCountSelected(ctx) {
|
|
return &types.ProfileConnection{
|
|
Resolver: r,
|
|
ParentID: obj.ID,
|
|
}, nil
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
pageOrderBy := page.OrderBy[coredata.MembershipProfileOrderField]{
|
|
Field: coredata.MembershipProfileOrderFieldCreatedAt,
|
|
Direction: page.OrderDirectionDesc,
|
|
}
|
|
if orderBy != nil {
|
|
pageOrderBy.Field = coredata.MembershipProfileOrderField(orderBy.Field)
|
|
pageOrderBy.Direction = page.OrderDirection(orderBy.Direction)
|
|
}
|
|
|
|
c := types.NewCursor(first, after, last, before, pageOrderBy)
|
|
|
|
p, err := prb.Documents.ListVersionApprovers(ctx, obj.ID, c)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot list document version approvers", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewProfileConnection(p, r, obj.ID, nil), nil
|
|
}
|
|
|
|
// Signatures is the resolver for the signatures field.
|
|
func (r *documentVersionResolver) Signatures(ctx context.Context, obj *types.DocumentVersion, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentVersionSignatureOrder, filter *types.DocumentVersionSignatureFilter) (*types.DocumentVersionSignatureConnection, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionSignatureList); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
pageOrderBy := page.OrderBy[coredata.DocumentVersionSignatureOrderField]{
|
|
Field: coredata.DocumentVersionSignatureOrderFieldCreatedAt,
|
|
Direction: page.OrderDirectionDesc,
|
|
}
|
|
if orderBy != nil {
|
|
pageOrderBy = page.OrderBy[coredata.DocumentVersionSignatureOrderField]{
|
|
Field: orderBy.Field,
|
|
Direction: orderBy.Direction,
|
|
}
|
|
}
|
|
|
|
var signatureStates []coredata.DocumentVersionSignatureState
|
|
var activeContract *bool
|
|
if filter != nil {
|
|
if filter.States != nil {
|
|
signatureStates = filter.States
|
|
}
|
|
if filter.ActiveContract != nil {
|
|
activeContract = filter.ActiveContract
|
|
}
|
|
}
|
|
signatureFilter := coredata.NewDocumentVersionSignatureFilter(signatureStates, activeContract)
|
|
|
|
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
|
|
|
page, err := prb.Documents.ListSignatures(ctx, obj.ID, cursor, signatureFilter)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot list document version signatures", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewDocumentVersionSignatureConnection(page, r, obj.ID, signatureFilter), nil
|
|
}
|
|
|
|
// ApprovalQuorums is the resolver for the approvalQuorums field.
|
|
func (r *documentVersionResolver) ApprovalQuorums(ctx context.Context, obj *types.DocumentVersion, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentVersionApprovalQuorumOrder) (*types.DocumentVersionApprovalQuorumConnection, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionApprovalList); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
pageOrderBy := page.OrderBy[coredata.DocumentVersionApprovalQuorumOrderField]{
|
|
Field: coredata.DocumentVersionApprovalQuorumOrderFieldCreatedAt,
|
|
Direction: page.OrderDirectionDesc,
|
|
}
|
|
if orderBy != nil {
|
|
pageOrderBy = page.OrderBy[coredata.DocumentVersionApprovalQuorumOrderField]{
|
|
Field: orderBy.Field,
|
|
Direction: orderBy.Direction,
|
|
}
|
|
}
|
|
|
|
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
|
|
|
p, err := prb.DocumentApprovals.ListQuorums(ctx, obj.ID, cursor)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot list approval quorums", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewDocumentVersionApprovalQuorumConnection(p, r, obj.ID), nil
|
|
}
|
|
|
|
// Signed is the resolver for the signed field.
|
|
func (r *documentVersionResolver) Signed(ctx context.Context, obj *types.DocumentVersion) (bool, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionGet); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
identity := authn.IdentityFromContext(ctx)
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
signed, err := prb.Documents.IsVersionSignedByUserEmail(ctx, obj.ID, identity.EmailAddress)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot check if document version is signed", log.Error(err))
|
|
return false, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return signed, nil
|
|
}
|
|
|
|
// Permission is the resolver for the permission field.
|
|
func (r *documentVersionResolver) Permission(ctx context.Context, obj *types.DocumentVersion, action string) (bool, error) {
|
|
return r.Resolver.Permission(ctx, obj, action)
|
|
}
|
|
|
|
// Quorum is the resolver for the quorum field.
|
|
func (r *documentVersionApprovalDecisionResolver) Quorum(ctx context.Context, obj *types.DocumentVersionApprovalDecision) (*types.DocumentVersionApprovalQuorum, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionApprovalList); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
quorum, err := prb.DocumentApprovals.GetQuorum(ctx, obj.Quorum.ID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot get approval quorum", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewDocumentVersionApprovalQuorum(quorum), nil
|
|
}
|
|
|
|
// DocumentVersion is the resolver for the documentVersion field.
|
|
func (r *documentVersionApprovalDecisionResolver) DocumentVersion(ctx context.Context, obj *types.DocumentVersionApprovalDecision) (*types.DocumentVersion, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
quorum, err := prb.DocumentApprovals.GetQuorum(ctx, obj.Quorum.ID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot get approval quorum", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
documentVersion, err := prb.Documents.GetVersion(ctx, quorum.VersionID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot get document version", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewDocumentVersion(documentVersion), nil
|
|
}
|
|
|
|
// Approver is the resolver for the approver field.
|
|
func (r *documentVersionApprovalDecisionResolver) Approver(ctx context.Context, obj *types.DocumentVersionApprovalDecision) (*types.Profile, error) {
|
|
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
profile, err := r.iam.OrganizationService.GetProfile(ctx, obj.Approver.ID)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, gqlutils.NotFound(ctx, err)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get approver profile", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewProfile(profile), nil
|
|
}
|
|
|
|
// Permission is the resolver for the permission field.
|
|
func (r *documentVersionApprovalDecisionResolver) Permission(ctx context.Context, obj *types.DocumentVersionApprovalDecision, action string) (bool, error) {
|
|
// Approve and reject actions are only allowed for the viewer's own decision.
|
|
if action == probo.ActionDocumentVersionApprove || action == probo.ActionDocumentVersionReject {
|
|
identity := authn.IdentityFromContext(ctx)
|
|
|
|
profile, err := r.iam.OrganizationService.GetProfile(ctx, obj.Approver.ID)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return false, nil
|
|
}
|
|
|
|
return false, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
if profile.IdentityID != identity.ID {
|
|
return false, nil
|
|
}
|
|
}
|
|
|
|
return r.Resolver.Permission(ctx, obj, action)
|
|
}
|
|
|
|
// TotalCount is the resolver for the totalCount field.
|
|
func (r *documentVersionApprovalDecisionConnectionResolver) TotalCount(ctx context.Context, obj *types.DocumentVersionApprovalDecisionConnection) (int, error) {
|
|
if obj.ParentID.EntityType() != coredata.DocumentVersionApprovalQuorumEntityType {
|
|
return 0, nil
|
|
}
|
|
|
|
if err := r.authorize(ctx, obj.ParentID, probo.ActionDocumentVersionApprovalList); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ParentID.TenantID())
|
|
|
|
filter := coredata.NewDocumentVersionApprovalDecisionFilter(nil)
|
|
if obj.Filters != nil {
|
|
filter = obj.Filters
|
|
}
|
|
|
|
count, err := prb.DocumentApprovals.CountDecisions(ctx, obj.ParentID, filter)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot count approval decisions", log.Error(err))
|
|
return 0, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return count, nil
|
|
}
|
|
|
|
// DocumentVersion is the resolver for the documentVersion field.
|
|
func (r *documentVersionApprovalQuorumResolver) DocumentVersion(ctx context.Context, obj *types.DocumentVersionApprovalQuorum) (*types.DocumentVersion, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
documentVersion, err := prb.Documents.GetVersion(ctx, obj.DocumentVersion.ID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot get document version", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewDocumentVersion(documentVersion), nil
|
|
}
|
|
|
|
// Decisions is the resolver for the decisions field.
|
|
func (r *documentVersionApprovalQuorumResolver) Decisions(ctx context.Context, obj *types.DocumentVersionApprovalQuorum, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentVersionApprovalDecisionOrder, filter *types.DocumentVersionApprovalDecisionFilter) (*types.DocumentVersionApprovalDecisionConnection, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionApprovalList); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
pageOrderBy := page.OrderBy[coredata.DocumentVersionApprovalDecisionOrderField]{
|
|
Field: coredata.DocumentVersionApprovalDecisionOrderFieldCreatedAt,
|
|
Direction: page.OrderDirectionDesc,
|
|
}
|
|
if orderBy != nil {
|
|
pageOrderBy = page.OrderBy[coredata.DocumentVersionApprovalDecisionOrderField]{
|
|
Field: orderBy.Field,
|
|
Direction: orderBy.Direction,
|
|
}
|
|
}
|
|
|
|
var approvalStates []coredata.DocumentVersionApprovalDecisionState
|
|
if filter != nil && filter.States != nil {
|
|
approvalStates = filter.States
|
|
}
|
|
approvalFilter := coredata.NewDocumentVersionApprovalDecisionFilter(approvalStates)
|
|
|
|
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
|
|
|
p, err := prb.DocumentApprovals.ListDecisions(ctx, obj.ID, cursor, approvalFilter)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot list approval decisions", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewDocumentVersionApprovalDecisionConnection(p, r, obj.ID, approvalFilter), nil
|
|
}
|
|
|
|
// Permission is the resolver for the permission field.
|
|
func (r *documentVersionApprovalQuorumResolver) Permission(ctx context.Context, obj *types.DocumentVersionApprovalQuorum, action string) (bool, error) {
|
|
return r.Resolver.Permission(ctx, obj, action)
|
|
}
|
|
|
|
// TotalCount is the resolver for the totalCount field.
|
|
func (r *documentVersionApprovalQuorumConnectionResolver) TotalCount(ctx context.Context, obj *types.DocumentVersionApprovalQuorumConnection) (int, error) {
|
|
if err := r.authorize(ctx, obj.ParentID, probo.ActionDocumentVersionApprovalList); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ParentID.TenantID())
|
|
|
|
count, err := prb.DocumentApprovals.CountQuorums(ctx, obj.ParentID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot count approval quorums", log.Error(err))
|
|
return 0, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return count, nil
|
|
}
|
|
|
|
// TotalCount is the resolver for the totalCount field.
|
|
func (r *documentVersionConnectionResolver) TotalCount(ctx context.Context, obj *types.DocumentVersionConnection) (int, error) {
|
|
if err := r.authorize(ctx, obj.ParentID, probo.ActionDocumentVersionList); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ParentID.TenantID())
|
|
|
|
switch obj.Resolver.(type) {
|
|
case *documentResolver:
|
|
filter := &coredata.DocumentVersionFilter{}
|
|
if obj.Filters != nil {
|
|
filter = obj.Filters
|
|
}
|
|
count, err := prb.Documents.CountVersionsForDocumentID(ctx, obj.ParentID, filter)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot count document versions", log.Error(err))
|
|
return 0, gqlutils.Internal(ctx)
|
|
}
|
|
return count, nil
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "unsupported resolver")
|
|
return 0, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
// DocumentVersion is the resolver for the documentVersion field.
|
|
func (r *documentVersionSignatureResolver) DocumentVersion(ctx context.Context, obj *types.DocumentVersionSignature) (*types.DocumentVersion, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
documentVersion, err := prb.Documents.GetVersion(ctx, obj.DocumentVersion.ID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot get document version", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewDocumentVersion(documentVersion), nil
|
|
}
|
|
|
|
// SignedBy is the resolver for the signedBy field.
|
|
func (r *documentVersionSignatureResolver) SignedBy(ctx context.Context, obj *types.DocumentVersionSignature) (*types.Profile, error) {
|
|
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
loaders := dataloader.FromContext(ctx)
|
|
|
|
signatory, err := loaders.Profile.Load(ctx, obj.SignedBy.ID)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
|
|
return nil, gqlutils.NotFound(ctx, err)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get people", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewProfile(signatory), nil
|
|
}
|
|
|
|
// Permission is the resolver for the permission field.
|
|
func (r *documentVersionSignatureResolver) Permission(ctx context.Context, obj *types.DocumentVersionSignature, action string) (bool, error) {
|
|
return r.Resolver.Permission(ctx, obj, action)
|
|
}
|
|
|
|
// TotalCount is the resolver for the totalCount field.
|
|
func (r *documentVersionSignatureConnectionResolver) TotalCount(ctx context.Context, obj *types.DocumentVersionSignatureConnection) (int, error) {
|
|
if err := r.authorize(ctx, obj.ParentID, probo.ActionDocumentVersionSignatureList); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ParentID.TenantID())
|
|
|
|
switch obj.Resolver.(type) {
|
|
case *documentVersionResolver:
|
|
filter := &coredata.DocumentVersionSignatureFilter{}
|
|
if obj.Filters != nil {
|
|
filter = obj.Filters
|
|
}
|
|
count, err := prb.Documents.CountSignaturesForVersionID(ctx, obj.ParentID, filter)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot count signatures", log.Error(err))
|
|
return 0, gqlutils.Internal(ctx)
|
|
}
|
|
return count, nil
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "unsupported resolver")
|
|
return 0, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
// Signed is the resolver for the signed field.
|
|
func (r *employeeDocumentResolver) Signed(ctx context.Context, obj *types.EmployeeDocument) (*bool, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionEmployeeDocumentGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
identity := authn.IdentityFromContext(ctx)
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
signed, err := prb.Documents.IsSigned(ctx, obj.ID, identity.EmailAddress)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, nil
|
|
}
|
|
r.logger.ErrorCtx(ctx, "cannot check if document is signed", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &signed, nil
|
|
}
|
|
|
|
// ApprovalState is the resolver for the approvalState field.
|
|
func (r *employeeDocumentResolver) ApprovalState(ctx context.Context, obj *types.EmployeeDocument) (*coredata.DocumentVersionApprovalDecisionState, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionEmployeeDocumentGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
identity := authn.IdentityFromContext(ctx)
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
state, err := prb.Documents.GetViewerApprovalState(ctx, obj.ID, identity.ID)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, nil
|
|
}
|
|
r.logger.ErrorCtx(ctx, "cannot get viewer approval state", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &state, nil
|
|
}
|
|
|
|
// Versions is the resolver for the versions field.
|
|
func (r *employeeDocumentResolver) Versions(ctx context.Context, obj *types.EmployeeDocument, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentVersionOrderBy) (*types.EmployeeDocumentVersionConnection, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionEmployeeDocumentGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
pageOrderBy := page.OrderBy[coredata.DocumentVersionOrderField]{
|
|
Field: coredata.DocumentVersionOrderFieldCreatedAt,
|
|
Direction: page.OrderDirectionDesc,
|
|
}
|
|
if orderBy != nil {
|
|
pageOrderBy = page.OrderBy[coredata.DocumentVersionOrderField]{
|
|
Field: orderBy.Field,
|
|
Direction: orderBy.Direction,
|
|
}
|
|
}
|
|
|
|
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
|
|
|
identity := authn.IdentityFromContext(ctx)
|
|
|
|
var filterMode coredata.EmployeeFilterMode
|
|
switch obj.FilterMode {
|
|
case types.EmployeeDocumentFilterModeSignature:
|
|
filterMode = coredata.EmployeeFilterModeSignature
|
|
case types.EmployeeDocumentFilterModeApproval:
|
|
filterMode = coredata.EmployeeFilterModeApproval
|
|
default:
|
|
r.logger.ErrorCtx(ctx, "unsupported employee document filter mode", log.String("filter_mode", string(obj.FilterMode)))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
versionFilter := coredata.NewDocumentVersionFilter().
|
|
WithEmployeeIdentityID(&identity.ID, filterMode)
|
|
|
|
versionsPage, err := prb.Documents.ListVersions(ctx, obj.ID, cursor, versionFilter)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot list employee document versions", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
employeeVersions := make([]*types.EmployeeDocumentVersion, len(versionsPage.Data))
|
|
for i, v := range versionsPage.Data {
|
|
employeeVersions[i] = &types.EmployeeDocumentVersion{
|
|
ID: v.ID,
|
|
DocumentID: obj.ID,
|
|
OrganizationID: v.OrganizationID,
|
|
Major: v.Major,
|
|
Minor: v.Minor,
|
|
Status: v.Status,
|
|
Classification: v.Classification,
|
|
DocumentType: v.DocumentType,
|
|
PublishedAt: v.PublishedAt,
|
|
CreatedAt: v.CreatedAt,
|
|
UpdatedAt: v.UpdatedAt,
|
|
}
|
|
}
|
|
|
|
p := page.NewPage(employeeVersions, versionsPage.Cursor)
|
|
|
|
return types.NewEmployeeDocumentVersionConnection(p), nil
|
|
}
|
|
|
|
// Signed is the resolver for the signed field.
|
|
func (r *employeeDocumentVersionResolver) Signed(ctx context.Context, obj *types.EmployeeDocumentVersion) (bool, error) {
|
|
if err := r.authorize(ctx, obj.DocumentID, probo.ActionEmployeeDocumentGet); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
identity := authn.IdentityFromContext(ctx)
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
signed, err := prb.Documents.IsVersionSignedByUserEmail(ctx, obj.ID, identity.EmailAddress)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot check if version is signed", log.Error(err))
|
|
return false, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return signed, nil
|
|
}
|
|
|
|
// ApprovalDecision is the resolver for the approvalDecision field.
|
|
func (r *employeeDocumentVersionResolver) ApprovalDecision(ctx context.Context, obj *types.EmployeeDocumentVersion) (*types.DocumentVersionApprovalDecision, error) {
|
|
if err := r.authorize(ctx, obj.DocumentID, probo.ActionEmployeeDocumentGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
identity := authn.IdentityFromContext(ctx)
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
decision, err := prb.DocumentApprovals.GetViewerDecision(ctx, obj.ID, identity.ID)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, nil
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get viewer approval decision", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewDocumentVersionApprovalDecision(decision), nil
|
|
}
|
|
|
|
// CreateDocument is the resolver for the createDocument field.
|
|
func (r *mutationResolver) CreateDocument(ctx context.Context, input types.CreateDocumentInput) (*types.CreateDocumentPayload, error) {
|
|
if err := r.authorize(ctx, input.OrganizationID, probo.ActionDocumentCreate); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
|
|
|
|
var content string
|
|
if input.Content != nil {
|
|
content = *input.Content
|
|
}
|
|
|
|
document, documentVersion, err := prb.Documents.Create(
|
|
ctx,
|
|
probo.CreateDocumentRequest{
|
|
OrganizationID: input.OrganizationID,
|
|
Title: input.Title,
|
|
Content: content,
|
|
Classification: input.Classification,
|
|
DocumentType: input.DocumentType,
|
|
TrustCenterVisibility: input.TrustCenterVisibility,
|
|
DefaultApproverIDs: input.DefaultApproverIds,
|
|
},
|
|
)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceAlreadyExists) {
|
|
return nil, gqlutils.Conflict(ctx, err)
|
|
}
|
|
|
|
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
|
|
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
|
|
}
|
|
r.logger.ErrorCtx(ctx, "cannot create document", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.CreateDocumentPayload{
|
|
DocumentEdge: types.NewDocumentEdge(document, coredata.DocumentOrderFieldTitle),
|
|
DocumentVersionEdge: types.NewDocumentVersionEdge(documentVersion, coredata.DocumentVersionOrderFieldCreatedAt),
|
|
}, nil
|
|
}
|
|
|
|
// UpdateDocument is the resolver for the updateDocument field.
|
|
func (r *mutationResolver) UpdateDocument(ctx context.Context, input types.UpdateDocumentInput) (*types.UpdateDocumentPayload, error) {
|
|
if err := r.authorize(ctx, input.ID, probo.ActionDocumentUpdate); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.ID.TenantID())
|
|
|
|
var defaultApproverIDs *[]gid.GID
|
|
if input.DefaultApproverIds != nil {
|
|
defaultApproverIDs = &input.DefaultApproverIds
|
|
}
|
|
|
|
document, documentVersion, draftCreated, err := prb.Documents.Update(
|
|
ctx,
|
|
probo.UpdateDocumentRequest{
|
|
DocumentID: input.ID,
|
|
Title: input.Title,
|
|
Content: input.Content,
|
|
Classification: input.Classification,
|
|
DocumentType: input.DocumentType,
|
|
TrustCenterVisibility: input.TrustCenterVisibility,
|
|
DefaultApproverIDs: defaultApproverIDs,
|
|
},
|
|
)
|
|
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, gqlutils.NotFound(ctx, err)
|
|
}
|
|
if errArchived, ok := errors.AsType[*probo.ErrDocumentArchived](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errArchived)
|
|
}
|
|
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
|
|
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
|
|
}
|
|
r.logger.ErrorCtx(ctx, "cannot update document", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
payload := &types.UpdateDocumentPayload{
|
|
Document: types.NewDocument(document),
|
|
}
|
|
|
|
if documentVersion != nil {
|
|
payload.DocumentVersion = types.NewDocumentVersion(documentVersion)
|
|
}
|
|
|
|
if draftCreated {
|
|
payload.DocumentVersionEdge = types.NewDocumentVersionEdge(
|
|
documentVersion,
|
|
coredata.DocumentVersionOrderFieldCreatedAt,
|
|
)
|
|
}
|
|
|
|
return payload, nil
|
|
}
|
|
|
|
// DeleteDocumentDraft is the resolver for the deleteDocumentDraft field.
|
|
func (r *mutationResolver) DeleteDocumentDraft(ctx context.Context, input types.DeleteDocumentDraftInput) (*types.DeleteDocumentDraftPayload, error) {
|
|
if err := r.authorize(ctx, input.DocumentID, probo.ActionDocumentDeleteDraft); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentID.TenantID())
|
|
|
|
document, err := prb.Documents.DeleteDraft(ctx, input.DocumentID)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, gqlutils.NotFound(ctx, err)
|
|
}
|
|
if errNotDeletable, ok := errors.AsType[*probo.ErrDocumentDraftNotDeletable](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errNotDeletable)
|
|
}
|
|
if errArchived, ok := errors.AsType[*probo.ErrDocumentArchived](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errArchived)
|
|
}
|
|
r.logger.ErrorCtx(ctx, "cannot delete document draft", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.DeleteDocumentDraftPayload{
|
|
Document: types.NewDocument(document),
|
|
}, nil
|
|
}
|
|
|
|
// ArchiveDocument is the resolver for the archiveDocument field.
|
|
func (r *mutationResolver) ArchiveDocument(ctx context.Context, input types.ArchiveDocumentInput) (*types.ArchiveDocumentPayload, error) {
|
|
if err := r.authorize(ctx, input.DocumentID, probo.ActionDocumentArchive); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentID.TenantID())
|
|
|
|
document, err := prb.Documents.Archive(ctx, input.DocumentID)
|
|
if err != nil {
|
|
if errArchived, ok := errors.AsType[*probo.ErrDocumentArchived](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errArchived)
|
|
}
|
|
r.logger.ErrorCtx(ctx, "cannot archive document", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.ArchiveDocumentPayload{
|
|
Document: types.NewDocument(document),
|
|
}, nil
|
|
}
|
|
|
|
// UnarchiveDocument is the resolver for the unarchiveDocument field.
|
|
func (r *mutationResolver) UnarchiveDocument(ctx context.Context, input types.UnarchiveDocumentInput) (*types.UnarchiveDocumentPayload, error) {
|
|
if err := r.authorize(ctx, input.DocumentID, probo.ActionDocumentUnarchive); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentID.TenantID())
|
|
|
|
document, err := prb.Documents.Unarchive(ctx, input.DocumentID)
|
|
if err != nil {
|
|
if errNotArchived, ok := errors.AsType[*probo.ErrDocumentNotArchived](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errNotArchived)
|
|
}
|
|
r.logger.ErrorCtx(ctx, "cannot unarchive document", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.UnarchiveDocumentPayload{
|
|
Document: types.NewDocument(document),
|
|
}, nil
|
|
}
|
|
|
|
// DeleteDocument is the resolver for the deleteDocument field.
|
|
func (r *mutationResolver) DeleteDocument(ctx context.Context, input types.DeleteDocumentInput) (*types.DeleteDocumentPayload, error) {
|
|
if err := r.authorize(ctx, input.DocumentID, probo.ActionDocumentDelete); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentID.TenantID())
|
|
|
|
err := prb.Documents.SoftDelete(ctx, input.DocumentID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot soft delete document", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.DeleteDocumentPayload{
|
|
DeletedDocumentID: input.DocumentID,
|
|
}, nil
|
|
}
|
|
|
|
// PublishMajorDocumentVersion is the resolver for the publishMajorDocumentVersion field.
|
|
func (r *mutationResolver) PublishMajorDocumentVersion(ctx context.Context, input types.PublishMajorDocumentVersionInput) (*types.PublishDocumentVersionPayload, error) {
|
|
if err := r.authorize(ctx, input.DocumentID, probo.ActionDocumentVersionPublish); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentID.TenantID())
|
|
|
|
document, documentVersion, err := prb.Documents.PublishMajorVersion(
|
|
ctx,
|
|
input.DocumentID,
|
|
authn.IdentityFromContext(ctx).ID,
|
|
input.Changelog,
|
|
)
|
|
if err != nil {
|
|
if errArchived, ok := errors.AsType[*probo.ErrDocumentArchived](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errArchived)
|
|
}
|
|
|
|
if errNotDraft, ok := errors.AsType[*probo.ErrDocumentVersionNotDraft](err); ok {
|
|
return nil, gqlutils.Invalid(ctx, errNotDraft)
|
|
}
|
|
|
|
if errPending, ok := errors.AsType[*probo.ErrDocumentVersionPendingApproval](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errPending)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot publish major document version", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.PublishDocumentVersionPayload{
|
|
Document: types.NewDocument(document),
|
|
DocumentVersion: types.NewDocumentVersion(documentVersion),
|
|
}, nil
|
|
}
|
|
|
|
// PublishMinorDocumentVersion is the resolver for the publishMinorDocumentVersion field.
|
|
func (r *mutationResolver) PublishMinorDocumentVersion(ctx context.Context, input types.PublishMinorDocumentVersionInput) (*types.PublishDocumentVersionPayload, error) {
|
|
if err := r.authorize(ctx, input.DocumentID, probo.ActionDocumentVersionPublish); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentID.TenantID())
|
|
|
|
document, documentVersion, err := prb.Documents.PublishMinorVersion(
|
|
ctx,
|
|
input.DocumentID,
|
|
authn.IdentityFromContext(ctx).ID,
|
|
input.Changelog,
|
|
)
|
|
if err != nil {
|
|
if errArchived, ok := errors.AsType[*probo.ErrDocumentArchived](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errArchived)
|
|
}
|
|
|
|
if errNotDraft, ok := errors.AsType[*probo.ErrDocumentVersionNotDraft](err); ok {
|
|
return nil, gqlutils.Invalid(ctx, errNotDraft)
|
|
}
|
|
|
|
if errPending, ok := errors.AsType[*probo.ErrDocumentVersionPendingApproval](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errPending)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot publish minor document version", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.PublishDocumentVersionPayload{
|
|
Document: types.NewDocument(document),
|
|
DocumentVersion: types.NewDocumentVersion(documentVersion),
|
|
}, nil
|
|
}
|
|
|
|
// BulkPublishMajorDocumentVersions is the resolver for the bulkPublishMajorDocumentVersions field.
|
|
func (r *mutationResolver) BulkPublishMajorDocumentVersions(ctx context.Context, input types.BulkPublishDocumentVersionsInput) (*types.BulkPublishDocumentVersionsPayload, error) {
|
|
if len(input.DocumentIds) == 0 {
|
|
return &types.BulkPublishDocumentVersionsPayload{
|
|
DocumentVersions: []*types.DocumentVersion{},
|
|
Documents: []*types.Document{},
|
|
}, nil
|
|
}
|
|
|
|
for _, documentID := range input.DocumentIds {
|
|
if err := r.authorize(ctx, documentID, probo.ActionDocumentVersionPublish); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentIds[0].TenantID())
|
|
|
|
versions, documents, err := prb.DocumentApprovals.BulkPublishMajorVersions(ctx, probo.BulkPublishVersionsRequest{
|
|
DocumentIDs: input.DocumentIds,
|
|
Changelog: input.Changelog,
|
|
})
|
|
if err != nil {
|
|
if errArchived, ok := errors.AsType[*probo.ErrDocumentArchived](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errArchived)
|
|
}
|
|
|
|
if errNotDraft, ok := errors.AsType[*probo.ErrDocumentVersionNotDraft](err); ok {
|
|
return nil, gqlutils.Invalid(ctx, errNotDraft)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot bulk publish major document versions", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
typesVersions := make([]*types.DocumentVersion, len(versions))
|
|
for i, v := range versions {
|
|
typesVersions[i] = types.NewDocumentVersion(v)
|
|
}
|
|
|
|
typesDocuments := make([]*types.Document, len(documents))
|
|
for i, d := range documents {
|
|
typesDocuments[i] = types.NewDocument(d)
|
|
}
|
|
|
|
return &types.BulkPublishDocumentVersionsPayload{
|
|
DocumentVersions: typesVersions,
|
|
Documents: typesDocuments,
|
|
}, nil
|
|
}
|
|
|
|
// BulkPublishMinorDocumentVersions is the resolver for the bulkPublishMinorDocumentVersions field.
|
|
func (r *mutationResolver) BulkPublishMinorDocumentVersions(ctx context.Context, input types.BulkPublishDocumentVersionsInput) (*types.BulkPublishDocumentVersionsPayload, error) {
|
|
if len(input.DocumentIds) == 0 {
|
|
return &types.BulkPublishDocumentVersionsPayload{
|
|
DocumentVersions: []*types.DocumentVersion{},
|
|
Documents: []*types.Document{},
|
|
}, nil
|
|
}
|
|
|
|
for _, documentID := range input.DocumentIds {
|
|
if err := r.authorize(ctx, documentID, probo.ActionDocumentVersionPublish); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentIds[0].TenantID())
|
|
|
|
versions, documents, err := prb.Documents.BulkPublishMinorVersions(ctx, probo.BulkPublishVersionsRequest{
|
|
DocumentIDs: input.DocumentIds,
|
|
Changelog: input.Changelog,
|
|
})
|
|
if err != nil {
|
|
if errArchived, ok := errors.AsType[*probo.ErrDocumentArchived](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errArchived)
|
|
}
|
|
|
|
if errNotDraft, ok := errors.AsType[*probo.ErrDocumentVersionNotDraft](err); ok {
|
|
return nil, gqlutils.Invalid(ctx, errNotDraft)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot bulk publish minor document versions", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
typesVersions := make([]*types.DocumentVersion, len(versions))
|
|
for i, v := range versions {
|
|
typesVersions[i] = types.NewDocumentVersion(v)
|
|
}
|
|
|
|
typesDocuments := make([]*types.Document, len(documents))
|
|
for i, d := range documents {
|
|
typesDocuments[i] = types.NewDocument(d)
|
|
}
|
|
|
|
return &types.BulkPublishDocumentVersionsPayload{
|
|
DocumentVersions: typesVersions,
|
|
Documents: typesDocuments,
|
|
}, nil
|
|
}
|
|
|
|
// RequestDocumentVersionApproval is the resolver for the requestDocumentVersionApproval field.
|
|
func (r *mutationResolver) RequestDocumentVersionApproval(ctx context.Context, input types.RequestDocumentVersionApprovalInput) (*types.RequestDocumentVersionApprovalPayload, error) {
|
|
if err := r.authorize(ctx, input.DocumentID, probo.ActionDocumentVersionRequestApproval); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentID.TenantID())
|
|
|
|
quorum, err := prb.DocumentApprovals.RequestApproval(ctx, probo.RequestApprovalRequest{
|
|
DocumentID: input.DocumentID,
|
|
ApproverIDs: input.ApproverIds,
|
|
Changelog: input.Changelog,
|
|
})
|
|
if err != nil {
|
|
if errArchived, ok := errors.AsType[*probo.ErrDocumentArchived](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errArchived)
|
|
}
|
|
|
|
if errNotDraft, ok := errors.AsType[*probo.ErrDocumentVersionNotDraft](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errNotDraft)
|
|
}
|
|
|
|
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
|
|
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot request document version approval", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.RequestDocumentVersionApprovalPayload{
|
|
ApprovalQuorum: types.NewDocumentVersionApprovalQuorum(quorum),
|
|
}, nil
|
|
}
|
|
|
|
// VoidDocumentVersionApproval is the resolver for the voidDocumentVersionApproval field.
|
|
func (r *mutationResolver) VoidDocumentVersionApproval(ctx context.Context, input types.VoidDocumentVersionApprovalInput) (*types.VoidDocumentVersionApprovalPayload, error) {
|
|
if err := r.authorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionVoidApproval); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentVersionID.TenantID())
|
|
|
|
quorum, documentVersion, err := prb.DocumentApprovals.VoidApproval(ctx, input.DocumentVersionID)
|
|
if err != nil {
|
|
if errArchived, ok := errors.AsType[*probo.ErrDocumentArchived](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errArchived)
|
|
}
|
|
|
|
if errNotPending, ok := errors.AsType[*probo.ErrDocumentVersionNotPendingApproval](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errNotPending)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot void document version approval", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.VoidDocumentVersionApprovalPayload{
|
|
ApprovalQuorum: types.NewDocumentVersionApprovalQuorum(quorum),
|
|
DocumentVersion: types.NewDocumentVersion(documentVersion),
|
|
}, nil
|
|
}
|
|
|
|
// BulkDeleteDocuments is the resolver for the bulkDeleteDocuments field.
|
|
func (r *mutationResolver) BulkDeleteDocuments(ctx context.Context, input types.BulkDeleteDocumentsInput) (*types.BulkDeleteDocumentsPayload, error) {
|
|
if len(input.DocumentIds) == 0 {
|
|
return &types.BulkDeleteDocumentsPayload{
|
|
DeletedDocumentIds: []gid.GID{},
|
|
}, nil
|
|
}
|
|
|
|
for _, documentID := range input.DocumentIds {
|
|
if err := r.authorize(ctx, documentID, probo.ActionDocumentDelete); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentIds[0].TenantID())
|
|
|
|
err := prb.Documents.BulkSoftDelete(ctx, input.DocumentIds)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot bulk delete documents", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.BulkDeleteDocumentsPayload{
|
|
DeletedDocumentIds: input.DocumentIds,
|
|
}, nil
|
|
}
|
|
|
|
// BulkArchiveDocuments is the resolver for the bulkArchiveDocuments field.
|
|
func (r *mutationResolver) BulkArchiveDocuments(ctx context.Context, input types.BulkArchiveDocumentsInput) (*types.BulkArchiveDocumentsPayload, error) {
|
|
if len(input.DocumentIds) == 0 {
|
|
return &types.BulkArchiveDocumentsPayload{
|
|
Documents: []*types.Document{},
|
|
}, nil
|
|
}
|
|
|
|
for _, documentID := range input.DocumentIds {
|
|
if err := r.authorize(ctx, documentID, probo.ActionDocumentArchive); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentIds[0].TenantID())
|
|
|
|
if err := prb.Documents.BulkArchive(ctx, input.DocumentIds); err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot bulk archive documents", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.BulkArchiveDocumentsPayload{
|
|
Documents: []*types.Document{},
|
|
}, nil
|
|
}
|
|
|
|
// BulkUnarchiveDocuments is the resolver for the bulkUnarchiveDocuments field.
|
|
func (r *mutationResolver) BulkUnarchiveDocuments(ctx context.Context, input types.BulkUnarchiveDocumentsInput) (*types.BulkUnarchiveDocumentsPayload, error) {
|
|
if len(input.DocumentIds) == 0 {
|
|
return &types.BulkUnarchiveDocumentsPayload{
|
|
Documents: []*types.Document{},
|
|
}, nil
|
|
}
|
|
|
|
for _, documentID := range input.DocumentIds {
|
|
if err := r.authorize(ctx, documentID, probo.ActionDocumentUnarchive); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentIds[0].TenantID())
|
|
|
|
if err := prb.Documents.BulkUnarchive(ctx, input.DocumentIds); err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot bulk unarchive documents", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.BulkUnarchiveDocumentsPayload{
|
|
Documents: []*types.Document{},
|
|
}, nil
|
|
}
|
|
|
|
// BulkExportDocuments is the resolver for the bulkExportDocuments field.
|
|
func (r *mutationResolver) BulkExportDocuments(ctx context.Context, input types.BulkExportDocumentsInput) (*types.BulkExportDocumentsPayload, error) {
|
|
if len(input.DocumentIds) == 0 {
|
|
r.logger.ErrorCtx(ctx, "no document ids provided")
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
// TODO have a way to batch authorize for resources
|
|
for _, documentID := range input.DocumentIds {
|
|
if err := r.authorize(ctx, documentID, probo.ActionDocumentVersionExport); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentIds[0].TenantID())
|
|
|
|
identity := authn.IdentityFromContext(ctx)
|
|
|
|
options := probo.ExportPDFOptions{
|
|
WithWatermark: input.WithWatermark,
|
|
WithSignatures: input.WithSignatures,
|
|
WatermarkEmail: input.WatermarkEmail,
|
|
}
|
|
|
|
documentExport, exportErr := prb.Documents.RequestExport(ctx, input.DocumentIds, identity.EmailAddress, identity.FullName, options)
|
|
if exportErr != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot request document export", log.Error(exportErr))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.BulkExportDocumentsPayload{
|
|
ExportJobID: documentExport.ID,
|
|
}, nil
|
|
}
|
|
|
|
// GenerateDocumentChangelog is the resolver for the generateDocumentChangelog field.
|
|
func (r *mutationResolver) GenerateDocumentChangelog(ctx context.Context, input types.GenerateDocumentChangelogInput) (*types.GenerateDocumentChangelogPayload, error) {
|
|
if err := r.authorize(ctx, input.DocumentID, probo.ActionDocumentChangelogGenerate); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentID.TenantID())
|
|
|
|
changelog, err := prb.Documents.GenerateChangelog(ctx, input.DocumentID)
|
|
if err != nil {
|
|
if errArchived, ok := errors.AsType[*probo.ErrDocumentArchived](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errArchived)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot generate document changelog", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.GenerateDocumentChangelogPayload{
|
|
Changelog: *changelog,
|
|
}, nil
|
|
}
|
|
|
|
// RequestSignature is the resolver for the requestSignature field.
|
|
func (r *mutationResolver) RequestSignature(ctx context.Context, input types.RequestSignatureInput) (*types.RequestSignaturePayload, error) {
|
|
if err := r.authorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionSignatureRequest); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentVersionID.TenantID())
|
|
|
|
documentVersionSignature, err := prb.Documents.RequestSignature(
|
|
ctx,
|
|
probo.RequestSignatureRequest{
|
|
DocumentVersionID: input.DocumentVersionID,
|
|
Signatory: input.SignatoryID,
|
|
},
|
|
)
|
|
if err != nil {
|
|
if errArchived, ok := errors.AsType[*probo.ErrDocumentArchived](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errArchived)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot request signature", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.RequestSignaturePayload{
|
|
DocumentVersionSignatureEdge: types.NewDocumentVersionSignatureEdge(documentVersionSignature, coredata.DocumentVersionSignatureOrderFieldCreatedAt),
|
|
}, nil
|
|
}
|
|
|
|
// BulkRequestSignatures is the resolver for the bulkRequestSignatures field.
|
|
func (r *mutationResolver) BulkRequestSignatures(ctx context.Context, input types.BulkRequestSignaturesInput) (*types.BulkRequestSignaturesPayload, error) {
|
|
if len(input.DocumentIds) == 0 {
|
|
return &types.BulkRequestSignaturesPayload{
|
|
DocumentVersionSignatureEdges: []*types.DocumentVersionSignatureEdge{},
|
|
}, nil
|
|
}
|
|
|
|
for _, documentID := range input.DocumentIds {
|
|
if err := r.authorize(ctx, documentID, probo.ActionDocumentVersionSignatureRequest); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentIds[0].TenantID())
|
|
|
|
documentVersionSignatures, err := prb.Documents.BulkRequestSignatures(
|
|
ctx,
|
|
probo.BulkRequestSignaturesRequest{
|
|
DocumentIDs: input.DocumentIds,
|
|
SignatoryIDs: input.SignatoryIds,
|
|
},
|
|
)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot bulk request signatures", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.BulkRequestSignaturesPayload{
|
|
DocumentVersionSignatureEdges: types.NewDocumentVersionSignatureEdges(documentVersionSignatures, coredata.DocumentVersionSignatureOrderFieldCreatedAt),
|
|
}, nil
|
|
}
|
|
|
|
// SendSigningNotifications is the resolver for the sendSigningNotifications field.
|
|
func (r *mutationResolver) SendSigningNotifications(ctx context.Context, input types.SendSigningNotificationsInput) (*types.SendSigningNotificationsPayload, error) {
|
|
if err := r.authorize(ctx, input.OrganizationID, probo.ActionDocumentSendSigningNotifications); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
|
|
|
|
err := prb.Documents.SendSigningNotifications(ctx, input.OrganizationID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot send signing notifications", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.SendSigningNotificationsPayload{
|
|
Success: true,
|
|
}, nil
|
|
}
|
|
|
|
// CancelSignatureRequest is the resolver for the cancelSignatureRequest field.
|
|
func (r *mutationResolver) CancelSignatureRequest(ctx context.Context, input types.CancelSignatureRequestInput) (*types.CancelSignatureRequestPayload, error) {
|
|
if err := r.authorize(ctx, input.DocumentVersionSignatureID, probo.ActionDocumentVersionCancelSignature); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentVersionSignatureID.TenantID())
|
|
|
|
err := prb.Documents.CancelSignatureRequest(ctx, input.DocumentVersionSignatureID)
|
|
if err != nil {
|
|
if errArchived, ok := errors.AsType[*probo.ErrDocumentArchived](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errArchived)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot cancel signature request", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.CancelSignatureRequestPayload{
|
|
DeletedDocumentVersionSignatureID: input.DocumentVersionSignatureID,
|
|
}, nil
|
|
}
|
|
|
|
// SignDocument is the resolver for the signDocument field.
|
|
func (r *mutationResolver) SignDocument(ctx context.Context, input types.SignDocumentInput) (*types.SignDocumentPayload, error) {
|
|
if err := r.authorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionSign); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
identity := authn.IdentityFromContext(ctx)
|
|
prb := r.ProboService(ctx, input.DocumentVersionID.TenantID())
|
|
|
|
documentVersionSignature, err := prb.Documents.SignDocumentVersionByIdentity(ctx, input.DocumentVersionID, identity.ID)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceAlreadyExists) {
|
|
return nil, gqlutils.Conflict(ctx, err)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot sign document", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.SignDocumentPayload{
|
|
DocumentVersionSignature: types.NewDocumentVersionSignature(documentVersionSignature),
|
|
}, nil
|
|
}
|
|
|
|
// ApproveDocumentVersion is the resolver for the approveDocumentVersion field.
|
|
func (r *mutationResolver) ApproveDocumentVersion(ctx context.Context, input types.ApproveDocumentVersionInput) (*types.ApproveDocumentVersionPayload, error) {
|
|
if err := r.authorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionApprove); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
identity := authn.IdentityFromContext(ctx)
|
|
httpReq := gqlutils.HTTPRequestFromContext(ctx)
|
|
|
|
signerIP, _, _ := net.SplitHostPort(httpReq.RemoteAddr)
|
|
if signerIP == "" {
|
|
signerIP = httpReq.RemoteAddr
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentVersionID.TenantID())
|
|
|
|
decision, err := prb.DocumentApprovals.Approve(ctx, probo.ApproveDocumentVersionRequest{
|
|
DocumentVersionID: input.DocumentVersionID,
|
|
IdentityID: identity.ID,
|
|
Comment: input.Comment,
|
|
SignerFullName: identity.FullName,
|
|
SignerEmail: identity.EmailAddress,
|
|
SignerIPAddr: signerIP,
|
|
SignerUA: httpReq.UserAgent(),
|
|
})
|
|
if err != nil {
|
|
if errArchived, ok := errors.AsType[*probo.ErrDocumentArchived](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errArchived)
|
|
}
|
|
|
|
if errNotPending, ok := errors.AsType[*probo.ErrDocumentVersionNotPendingApproval](err); ok {
|
|
return nil, gqlutils.Invalid(ctx, errNotPending)
|
|
}
|
|
|
|
if errAlready, ok := errors.AsType[*probo.ErrApprovalDecisionAlreadyMade](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errAlready)
|
|
}
|
|
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, gqlutils.NotFound(ctx, err)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot approve document version", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.ApproveDocumentVersionPayload{
|
|
ApprovalDecision: types.NewDocumentVersionApprovalDecision(decision),
|
|
}, nil
|
|
}
|
|
|
|
// RejectDocumentVersion is the resolver for the rejectDocumentVersion field.
|
|
func (r *mutationResolver) RejectDocumentVersion(ctx context.Context, input types.RejectDocumentVersionInput) (*types.RejectDocumentVersionPayload, error) {
|
|
if err := r.authorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionReject); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
identity := authn.IdentityFromContext(ctx)
|
|
|
|
prb := r.ProboService(ctx, input.DocumentVersionID.TenantID())
|
|
|
|
decision, err := prb.DocumentApprovals.Reject(ctx, probo.RejectDocumentVersionRequest{
|
|
DocumentVersionID: input.DocumentVersionID,
|
|
IdentityID: identity.ID,
|
|
Comment: input.Comment,
|
|
})
|
|
if err != nil {
|
|
if errArchived, ok := errors.AsType[*probo.ErrDocumentArchived](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errArchived)
|
|
}
|
|
|
|
if errNotPending, ok := errors.AsType[*probo.ErrDocumentVersionNotPendingApproval](err); ok {
|
|
return nil, gqlutils.Invalid(ctx, errNotPending)
|
|
}
|
|
|
|
if errAlready, ok := errors.AsType[*probo.ErrApprovalDecisionAlreadyMade](err); ok {
|
|
return nil, gqlutils.Conflict(ctx, errAlready)
|
|
}
|
|
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, gqlutils.NotFound(ctx, err)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot reject document version", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.RejectDocumentVersionPayload{
|
|
ApprovalDecision: types.NewDocumentVersionApprovalDecision(decision),
|
|
}, nil
|
|
}
|
|
|
|
// ExportDocumentVersionPDF is the resolver for the exportDocumentVersionPDF field.
|
|
func (r *mutationResolver) ExportDocumentVersionPDF(ctx context.Context, input types.ExportDocumentVersionPDFInput) (*types.ExportDocumentVersionPDFPayload, error) {
|
|
if err := r.authorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionExportPDF); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentVersionID.TenantID())
|
|
|
|
watermarkEmail := input.WatermarkEmail
|
|
if input.WithWatermark && watermarkEmail == nil {
|
|
identity := authn.IdentityFromContext(ctx)
|
|
watermarkEmail = &identity.EmailAddress
|
|
}
|
|
|
|
options := probo.ExportPDFOptions{
|
|
WithSignatures: input.WithSignatures,
|
|
WithWatermark: input.WithWatermark,
|
|
WatermarkEmail: watermarkEmail,
|
|
}
|
|
|
|
pdf, err := prb.Documents.ExportPDF(ctx, input.DocumentVersionID, options)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot export document version PDF", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.ExportDocumentVersionPDFPayload{
|
|
Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
|
|
}, nil
|
|
}
|
|
|
|
// ExportEmployeeDocumentVersionPDF is the resolver for the exportEmployeeDocumentVersionPDF field.
|
|
func (r *mutationResolver) ExportEmployeeDocumentVersionPDF(ctx context.Context, input types.ExportEmployeeDocumentVersionPDFInput) (*types.ExportEmployeeDocumentVersionPDFPayload, error) {
|
|
if err := r.authorize(ctx, input.DocumentVersionID, probo.ActionEmployeeDocumentVersionExportPDF); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, input.DocumentVersionID.TenantID())
|
|
|
|
documentVersion, err := prb.Documents.GetVersion(ctx, input.DocumentVersionID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot get document version", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
identity := authn.IdentityFromContext(ctx)
|
|
documentFilter := coredata.NewDocumentFilter(nil).WithEmployeeIdentityID(
|
|
&identity.ID,
|
|
coredata.EmployeeFilterModeSignature,
|
|
coredata.EmployeeFilterModeApproval,
|
|
)
|
|
|
|
_, err = prb.Documents.GetWithFilter(ctx, documentVersion.DocumentID, documentFilter)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, gqlutils.NotFound(ctx, err)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get employee document", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
options := probo.ExportPDFOptions{
|
|
WithSignatures: false,
|
|
WithWatermark: true,
|
|
WatermarkEmail: &identity.EmailAddress,
|
|
}
|
|
|
|
pdf, err := prb.Documents.ExportPDF(ctx, input.DocumentVersionID, options)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot export employee document PDF", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.ExportEmployeeDocumentVersionPDFPayload{
|
|
Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
|
|
}, nil
|
|
}
|
|
|
|
// Documents is the resolver for the documents field.
|
|
func (r *organizationResolver) Documents(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy, filter *types.DocumentFilter) (*types.DocumentConnection, error) {
|
|
if err := r.authorize(ctx, obj.ID, probo.ActionDocumentList); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, obj.ID.TenantID())
|
|
|
|
pageOrderBy := page.OrderBy[coredata.DocumentOrderField]{
|
|
Field: coredata.DocumentOrderFieldTitle,
|
|
Direction: page.OrderDirectionDesc,
|
|
}
|
|
if orderBy != nil {
|
|
pageOrderBy = page.OrderBy[coredata.DocumentOrderField]{
|
|
Field: orderBy.Field,
|
|
Direction: orderBy.Direction,
|
|
}
|
|
}
|
|
|
|
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
|
|
|
var documentFilter = coredata.NewDocumentFilter(nil)
|
|
if filter != nil {
|
|
documentFilter = coredata.NewDocumentFilter(filter.Query).
|
|
WithDocumentTypes(filter.DocumentTypes).
|
|
WithClassifications(filter.Classifications).
|
|
WithStatus(filter.Status)
|
|
}
|
|
|
|
page, err := prb.Documents.ListByOrganizationID(ctx, obj.ID, cursor, documentFilter)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot list organization documents", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewDocumentConnection(page, r, obj.ID, documentFilter), nil
|
|
}
|
|
|
|
// SignableDocuments is the resolver for the signableDocuments field.
|
|
func (r *viewerResolver) SignableDocuments(ctx context.Context, obj *types.Viewer, organizationID gid.GID, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) (*types.EmployeeDocumentConnection, error) {
|
|
if err := r.authorize(ctx, organizationID, probo.ActionEmployeeDocumentList); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, organizationID.TenantID())
|
|
|
|
pageOrderBy := page.OrderBy[coredata.DocumentOrderField]{
|
|
Field: coredata.DocumentOrderFieldCreatedAt,
|
|
Direction: page.OrderDirectionDesc,
|
|
}
|
|
if orderBy != nil {
|
|
pageOrderBy = page.OrderBy[coredata.DocumentOrderField]{
|
|
Field: orderBy.Field,
|
|
Direction: orderBy.Direction,
|
|
}
|
|
}
|
|
|
|
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
|
|
|
identity := authn.IdentityFromContext(ctx)
|
|
|
|
documentFilter := coredata.NewDocumentFilter(nil).WithEmployeeIdentityID(&identity.ID, coredata.EmployeeFilterModeSignature)
|
|
|
|
documentsPage, err := prb.Documents.ListByOrganizationID(ctx, organizationID, cursor, documentFilter)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot list organization signable documents", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
employeeDocuments := make([]*types.EmployeeDocument, len(documentsPage.Data))
|
|
for i, doc := range documentsPage.Data {
|
|
employeeDocuments[i] = &types.EmployeeDocument{
|
|
ID: doc.ID,
|
|
Title: doc.Title,
|
|
DocumentType: doc.DocumentType,
|
|
CreatedAt: doc.CreatedAt,
|
|
UpdatedAt: doc.UpdatedAt,
|
|
FilterMode: types.EmployeeDocumentFilterModeSignature,
|
|
}
|
|
}
|
|
|
|
page := page.NewPage(employeeDocuments, documentsPage.Cursor)
|
|
|
|
return types.NewEmployeeDocumentConnection(page), nil
|
|
}
|
|
|
|
// SignableDocument is the resolver for the signableDocument field.
|
|
func (r *viewerResolver) SignableDocument(ctx context.Context, obj *types.Viewer, id gid.GID) (*types.EmployeeDocument, error) {
|
|
if err := r.authorize(ctx, id, probo.ActionEmployeeDocumentGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, id.TenantID())
|
|
|
|
identity := authn.IdentityFromContext(ctx)
|
|
|
|
documentFilter := coredata.NewDocumentFilter(nil).WithEmployeeIdentityID(&identity.ID, coredata.EmployeeFilterModeSignature)
|
|
document, err := prb.Documents.GetWithFilter(ctx, id, documentFilter)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, gqlutils.NotFound(ctx, err)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get signable document", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.EmployeeDocument{
|
|
ID: document.ID,
|
|
Title: document.Title,
|
|
DocumentType: document.DocumentType,
|
|
CreatedAt: document.CreatedAt,
|
|
UpdatedAt: document.UpdatedAt,
|
|
FilterMode: types.EmployeeDocumentFilterModeSignature,
|
|
}, nil
|
|
}
|
|
|
|
// ApprovableDocuments is the resolver for the approvableDocuments field.
|
|
func (r *viewerResolver) ApprovableDocuments(ctx context.Context, obj *types.Viewer, organizationID gid.GID, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) (*types.EmployeeDocumentConnection, error) {
|
|
if err := r.authorize(ctx, organizationID, probo.ActionEmployeeDocumentList); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, organizationID.TenantID())
|
|
|
|
pageOrderBy := page.OrderBy[coredata.DocumentOrderField]{
|
|
Field: coredata.DocumentOrderFieldCreatedAt,
|
|
Direction: page.OrderDirectionDesc,
|
|
}
|
|
if orderBy != nil {
|
|
pageOrderBy = page.OrderBy[coredata.DocumentOrderField]{
|
|
Field: orderBy.Field,
|
|
Direction: orderBy.Direction,
|
|
}
|
|
}
|
|
|
|
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
|
|
|
identity := authn.IdentityFromContext(ctx)
|
|
|
|
documentFilter := coredata.NewDocumentFilter(nil).WithEmployeeIdentityID(&identity.ID, coredata.EmployeeFilterModeApproval)
|
|
|
|
documentsPage, err := prb.Documents.ListByOrganizationID(ctx, organizationID, cursor, documentFilter)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot list organization approvable documents", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
employeeDocuments := make([]*types.EmployeeDocument, len(documentsPage.Data))
|
|
for i, doc := range documentsPage.Data {
|
|
employeeDocuments[i] = &types.EmployeeDocument{
|
|
ID: doc.ID,
|
|
Title: doc.Title,
|
|
DocumentType: doc.DocumentType,
|
|
CreatedAt: doc.CreatedAt,
|
|
UpdatedAt: doc.UpdatedAt,
|
|
FilterMode: types.EmployeeDocumentFilterModeApproval,
|
|
}
|
|
}
|
|
|
|
page := page.NewPage(employeeDocuments, documentsPage.Cursor)
|
|
|
|
return types.NewEmployeeDocumentConnection(page), nil
|
|
}
|
|
|
|
// ApprovableDocument is the resolver for the approvableDocument field.
|
|
func (r *viewerResolver) ApprovableDocument(ctx context.Context, obj *types.Viewer, id gid.GID) (*types.EmployeeDocument, error) {
|
|
if err := r.authorize(ctx, id, probo.ActionEmployeeDocumentGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
prb := r.ProboService(ctx, id.TenantID())
|
|
|
|
identity := authn.IdentityFromContext(ctx)
|
|
|
|
documentFilter := coredata.NewDocumentFilter(nil).WithEmployeeIdentityID(&identity.ID, coredata.EmployeeFilterModeApproval)
|
|
document, err := prb.Documents.GetWithFilter(ctx, id, documentFilter)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
return nil, gqlutils.NotFound(ctx, err)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get approvable document", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.EmployeeDocument{
|
|
ID: document.ID,
|
|
Title: document.Title,
|
|
DocumentType: document.DocumentType,
|
|
CreatedAt: document.CreatedAt,
|
|
UpdatedAt: document.UpdatedAt,
|
|
FilterMode: types.EmployeeDocumentFilterModeApproval,
|
|
}, nil
|
|
}
|
|
|
|
// Document returns schema.DocumentResolver implementation.
|
|
func (r *Resolver) Document() schema.DocumentResolver { return &documentResolver{r} }
|
|
|
|
// DocumentConnection returns schema.DocumentConnectionResolver implementation.
|
|
func (r *Resolver) DocumentConnection() schema.DocumentConnectionResolver {
|
|
return &documentConnectionResolver{r}
|
|
}
|
|
|
|
// DocumentVersion returns schema.DocumentVersionResolver implementation.
|
|
func (r *Resolver) DocumentVersion() schema.DocumentVersionResolver {
|
|
return &documentVersionResolver{r}
|
|
}
|
|
|
|
// DocumentVersionApprovalDecision returns schema.DocumentVersionApprovalDecisionResolver implementation.
|
|
func (r *Resolver) DocumentVersionApprovalDecision() schema.DocumentVersionApprovalDecisionResolver {
|
|
return &documentVersionApprovalDecisionResolver{r}
|
|
}
|
|
|
|
// DocumentVersionApprovalDecisionConnection returns schema.DocumentVersionApprovalDecisionConnectionResolver implementation.
|
|
func (r *Resolver) DocumentVersionApprovalDecisionConnection() schema.DocumentVersionApprovalDecisionConnectionResolver {
|
|
return &documentVersionApprovalDecisionConnectionResolver{r}
|
|
}
|
|
|
|
// DocumentVersionApprovalQuorum returns schema.DocumentVersionApprovalQuorumResolver implementation.
|
|
func (r *Resolver) DocumentVersionApprovalQuorum() schema.DocumentVersionApprovalQuorumResolver {
|
|
return &documentVersionApprovalQuorumResolver{r}
|
|
}
|
|
|
|
// DocumentVersionApprovalQuorumConnection returns schema.DocumentVersionApprovalQuorumConnectionResolver implementation.
|
|
func (r *Resolver) DocumentVersionApprovalQuorumConnection() schema.DocumentVersionApprovalQuorumConnectionResolver {
|
|
return &documentVersionApprovalQuorumConnectionResolver{r}
|
|
}
|
|
|
|
// DocumentVersionConnection returns schema.DocumentVersionConnectionResolver implementation.
|
|
func (r *Resolver) DocumentVersionConnection() schema.DocumentVersionConnectionResolver {
|
|
return &documentVersionConnectionResolver{r}
|
|
}
|
|
|
|
// DocumentVersionSignature returns schema.DocumentVersionSignatureResolver implementation.
|
|
func (r *Resolver) DocumentVersionSignature() schema.DocumentVersionSignatureResolver {
|
|
return &documentVersionSignatureResolver{r}
|
|
}
|
|
|
|
// DocumentVersionSignatureConnection returns schema.DocumentVersionSignatureConnectionResolver implementation.
|
|
func (r *Resolver) DocumentVersionSignatureConnection() schema.DocumentVersionSignatureConnectionResolver {
|
|
return &documentVersionSignatureConnectionResolver{r}
|
|
}
|
|
|
|
// EmployeeDocument returns schema.EmployeeDocumentResolver implementation.
|
|
func (r *Resolver) EmployeeDocument() schema.EmployeeDocumentResolver {
|
|
return &employeeDocumentResolver{r}
|
|
}
|
|
|
|
// EmployeeDocumentVersion returns schema.EmployeeDocumentVersionResolver implementation.
|
|
func (r *Resolver) EmployeeDocumentVersion() schema.EmployeeDocumentVersionResolver {
|
|
return &employeeDocumentVersionResolver{r}
|
|
}
|
|
|
|
type documentResolver struct{ *Resolver }
|
|
type documentConnectionResolver struct{ *Resolver }
|
|
type documentVersionResolver struct{ *Resolver }
|
|
type documentVersionApprovalDecisionResolver struct{ *Resolver }
|
|
type documentVersionApprovalDecisionConnectionResolver struct{ *Resolver }
|
|
type documentVersionApprovalQuorumResolver struct{ *Resolver }
|
|
type documentVersionApprovalQuorumConnectionResolver struct{ *Resolver }
|
|
type documentVersionConnectionResolver struct{ *Resolver }
|
|
type documentVersionSignatureResolver struct{ *Resolver }
|
|
type documentVersionSignatureConnectionResolver struct{ *Resolver }
|
|
type employeeDocumentResolver struct{ *Resolver }
|
|
type employeeDocumentVersionResolver struct{ *Resolver }
|