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.94 import ( "context" "encoding/base64" "errors" "fmt" "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/resourcealias" "go.probo.inc/probo/pkg/server/api/authn" "go.probo.inc/probo/pkg/server/api/clientip" "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" ) // Alias is the resolver for the alias field. func (r *documentResolver) Alias(ctx context.Context, obj *types.Document) (*string, error) { scope, err := r.authorize(ctx, obj.ID, resourcealias.ActionAliasGet) if err != nil { return nil, err } return r.resourceAlias.GetByResourceID(ctx, scope, obj.ID) } // Organization is the resolver for the organization field. func (r *documentResolver) Organization(ctx context.Context, obj *types.Document) (*types.Organization, error) { if _, err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil { 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) { scope, err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionList) if err != nil { return nil, err } 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 := r.probo.Documents.ListVersions(ctx, scope, 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) { scope, err := r.authorize(ctx, obj.ID, probo.ActionControlList) if err != nil { return nil, err } 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 := r.probo.Controls.ListForDocumentID(ctx, scope, 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) { scope, err := r.authorize(ctx, obj.ID, probo.ActionDocumentGet) if err != nil { return nil, err } profiles, err := r.probo.Documents.GetDefaultApprovers(ctx, scope, 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) { scope, err := r.authorize(ctx, obj.ParentID, probo.ActionDocumentList) if err != nil { return 0, err } switch obj.Resolver.(type) { case *controlResolver: count, err := r.probo.Documents.CountForControlID(ctx, scope, 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 := r.probo.Documents.CountForOrganizationID(ctx, scope, 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 := r.probo.Documents.CountForRiskID(ctx, scope, 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 := r.probo.Documents.CountForMeasureID(ctx, scope, 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.Document.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) { scope, err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileList) if err != nil { return nil, err } if gqlutils.OnlyTotalCountSelected(ctx) { return &types.ProfileConnection{ Resolver: r, ParentID: obj.ID, }, nil } 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 := r.probo.Documents.ListVersionApprovers(ctx, scope, 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) { scope, err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionSignatureList) if err != nil { return nil, err } 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 activeContract *bool profileStates []coredata.ProfileState ) if filter != nil { if filter.States != nil { signatureStates = filter.States } if filter.ActiveContract != nil { activeContract = filter.ActiveContract } if filter.ProfileStates != nil { profileStates = filter.ProfileStates } } signatureFilter := coredata.NewDocumentVersionSignatureFilter(signatureStates, activeContract, profileStates) cursor := types.NewCursor(first, after, last, before, pageOrderBy) page, err := r.probo.Documents.ListSignatures(ctx, scope, 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) { scope, err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionApprovalList) if err != nil { return nil, err } 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 := r.probo.DocumentApprovals.ListQuorums(ctx, scope, 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) { scope, err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionGet) if err != nil { return false, err } identity := authn.IdentityFromContext(ctx) signed, err := r.probo.Documents.IsVersionSignedByUserEmail(ctx, scope, 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) { scope, err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionApprovalList) if err != nil { return nil, err } quorum, err := r.probo.DocumentApprovals.GetQuorum(ctx, scope, obj.Quorum.ID) if err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return nil, gqlutils.NotFound(ctx, err) } 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) { scope, err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionGet) if err != nil { return nil, err } quorum, err := r.probo.DocumentApprovals.GetQuorum(ctx, scope, obj.Quorum.ID) if err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return nil, gqlutils.NotFound(ctx, err) } r.logger.ErrorCtx(ctx, "cannot get approval quorum", log.Error(err)) return nil, gqlutils.Internal(ctx) } documentVersion, err := r.probo.Documents.GetVersion(ctx, scope, quorum.VersionID) if err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return nil, gqlutils.NotFound(ctx, err) } 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.Approver.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 } // ConsentText is the resolver for the consentText field. func (r *documentVersionApprovalDecisionResolver) ConsentText(ctx context.Context, obj *types.DocumentVersionApprovalDecision) (string, error) { return probo.DocumentApprovalConsentText, 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 } scope, err := r.authorize(ctx, obj.ParentID, probo.ActionDocumentVersionApprovalList) if err != nil { return 0, err } filter := coredata.NewDocumentVersionApprovalDecisionFilter(nil) if obj.Filters != nil { filter = obj.Filters } count, err := r.probo.DocumentApprovals.CountDecisions(ctx, scope, 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) { scope, err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionGet) if err != nil { return nil, err } documentVersion, err := r.probo.Documents.GetVersion(ctx, scope, obj.DocumentVersion.ID) if err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return nil, gqlutils.NotFound(ctx, err) } 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) { scope, err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionApprovalList) if err != nil { return nil, err } 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 := r.probo.DocumentApprovals.ListDecisions(ctx, scope, 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) { scope, err := r.authorize(ctx, obj.ParentID, probo.ActionDocumentVersionApprovalList) if err != nil { return 0, err } count, err := r.probo.DocumentApprovals.CountQuorums(ctx, scope, 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) { scope, err := r.authorize(ctx, obj.ParentID, probo.ActionDocumentVersionList) if err != nil { return 0, err } switch obj.Resolver.(type) { case *documentResolver: filter := &coredata.DocumentVersionFilter{} if obj.Filters != nil { filter = obj.Filters } count, err := r.probo.Documents.CountVersionsForDocumentID(ctx, scope, 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) { scope, err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionGet) if err != nil { return nil, err } documentVersion, err := r.probo.Documents.GetVersion(ctx, scope, obj.DocumentVersion.ID) if err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return nil, gqlutils.NotFound(ctx, err) } 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.SignedBy.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) { scope, err := r.authorize(ctx, obj.ParentID, probo.ActionDocumentVersionSignatureList) if err != nil { return 0, err } switch obj.Resolver.(type) { case *documentVersionResolver: filter := &coredata.DocumentVersionSignatureFilter{} if obj.Filters != nil { filter = obj.Filters } count, err := r.probo.Documents.CountSignaturesForVersionID(ctx, scope, 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) { scope, err := r.authorize(ctx, obj.ID, probo.ActionEmployeeDocumentGet) if err != nil { return nil, err } identity := authn.IdentityFromContext(ctx) signed, err := r.probo.Documents.IsSigned(ctx, scope, 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) { scope, err := r.authorize(ctx, obj.ID, probo.ActionEmployeeDocumentGet) if err != nil { return nil, err } identity := authn.IdentityFromContext(ctx) state, err := r.probo.Documents.GetViewerApprovalState(ctx, scope, 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) { scope, err := r.authorize(ctx, obj.ID, probo.ActionEmployeeDocumentGet) if err != nil { return nil, err } 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 := r.probo.Documents.ListVersions(ctx, scope, 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) { scope, err := r.authorize(ctx, obj.DocumentID, probo.ActionEmployeeDocumentGet) if err != nil { return false, err } identity := authn.IdentityFromContext(ctx) signed, err := r.probo.Documents.IsVersionSignedByUserEmail(ctx, scope, 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 } // ConsentText is the resolver for the consentText field. func (r *employeeDocumentVersionResolver) ConsentText(ctx context.Context, obj *types.EmployeeDocumentVersion) (string, error) { return probo.DocumentSignatureConsentText, nil } // ApprovalDecision is the resolver for the approvalDecision field. func (r *employeeDocumentVersionResolver) ApprovalDecision(ctx context.Context, obj *types.EmployeeDocumentVersion) (*types.DocumentVersionApprovalDecision, error) { scope, err := r.authorize(ctx, obj.DocumentID, probo.ActionEmployeeDocumentGet) if err != nil { return nil, err } identity := authn.IdentityFromContext(ctx) decision, err := r.probo.DocumentApprovals.GetViewerDecision(ctx, scope, 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) { scope, err := r.authorize(ctx, input.OrganizationID, probo.ActionDocumentCreate) if err != nil { return nil, err } var content string if input.Content != nil { content = *input.Content } document, documentVersion, err := r.probo.Documents.Create( ctx, scope, probo.CreateDocumentRequest{ OrganizationID: input.OrganizationID, Title: input.Title, Content: content, Classification: input.Classification, DocumentType: input.DocumentType, CompliancePortalVisibility: input.CompliancePortalVisibility, 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) { scope, err := r.authorize(ctx, input.ID, probo.ActionDocumentUpdate) if err != nil { return nil, err } var defaultApproverIDs *[]gid.GID if input.DefaultApproverIds != nil { defaultApproverIDs = &input.DefaultApproverIds } document, documentVersion, draftCreated, err := r.probo.Documents.Update( ctx, scope, probo.UpdateDocumentRequest{ DocumentID: input.ID, Title: input.Title, Content: input.Content, Classification: input.Classification, DocumentType: input.DocumentType, CompliancePortalVisibility: input.CompliancePortalVisibility, 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 errGenerated, ok := errors.AsType[*probo.ErrDocumentVersionGenerated](err); ok { return nil, gqlutils.Conflict(ctx, errGenerated) } 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) { scope, err := r.authorize(ctx, input.DocumentID, probo.ActionDocumentDeleteDraft) if err != nil { return nil, err } document, err := r.probo.Documents.DeleteDraft(ctx, scope, 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) { scope, err := r.authorize(ctx, input.DocumentID, probo.ActionDocumentArchive) if err != nil { return nil, err } document, err := r.probo.Documents.Archive(ctx, scope, 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) { scope, err := r.authorize(ctx, input.DocumentID, probo.ActionDocumentUnarchive) if err != nil { return nil, err } document, err := r.probo.Documents.Unarchive(ctx, scope, 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) { scope, err := r.authorize(ctx, input.DocumentID, probo.ActionDocumentDelete) if err != nil { return nil, err } if err := r.probo.Documents.SoftDelete(ctx, scope, input.DocumentID); 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 } // PublishDocument is the resolver for the publishDocument field. func (r *mutationResolver) PublishDocument(ctx context.Context, input types.PublishDocumentInput) (*types.PublishDocumentPayload, error) { scope, err := r.authorize(ctx, input.DocumentID, probo.ActionDocumentVersionPublish) if err != nil { return nil, err } result, err := r.probo.Documents.PublishVersion(ctx, scope, probo.PublishDocumentRequest{ DocumentID: input.DocumentID, Minor: input.Minor, 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.Invalid(ctx, errNotDraft) } if errPending, ok := errors.AsType[*probo.ErrDocumentVersionPendingApproval](err); ok { return nil, gqlutils.Conflict(ctx, errPending) } if errContractEnded, ok := errors.AsType[*probo.ErrProfileContractEnded](err); ok { return nil, gqlutils.Conflict(ctx, errContractEnded) } if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) } r.logger.ErrorCtx(ctx, "cannot publish document", log.Error(err)) return nil, gqlutils.Internal(ctx) } payload := &types.PublishDocumentPayload{ Document: types.NewDocument(result.Document), DocumentVersion: types.NewDocumentVersion(result.Version), } if result.Quorum != nil { payload.ApprovalQuorum = types.NewDocumentVersionApprovalQuorum(result.Quorum) } return payload, nil } // BulkPublishDocuments is the resolver for the bulkPublishDocuments field. func (r *mutationResolver) BulkPublishDocuments(ctx context.Context, input types.BulkPublishDocumentsInput) (*types.BulkPublishDocumentsPayload, error) { if len(input.DocumentIds) == 0 { return &types.BulkPublishDocumentsPayload{ 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 } } scope := coredata.NewScopeFromObjectID(input.DocumentIds[0]) versions, documents, err := r.probo.DocumentApprovals.BulkPublishVersions(ctx, scope, probo.BulkPublishVersionsRequest{ DocumentIDs: input.DocumentIds, Minor: input.Minor, Changelog: input.Changelog, }) 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 errNotDraft, ok := errors.AsType[*probo.ErrDocumentVersionNotDraft](err); ok { return nil, gqlutils.Invalid(ctx, errNotDraft) } r.logger.ErrorCtx(ctx, "cannot bulk publish documents", 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.BulkPublishDocumentsPayload{ DocumentVersions: typesVersions, Documents: typesDocuments, }, nil } // VoidDocumentVersionApproval is the resolver for the voidDocumentVersionApproval field. func (r *mutationResolver) VoidDocumentVersionApproval(ctx context.Context, input types.VoidDocumentVersionApprovalInput) (*types.VoidDocumentVersionApprovalPayload, error) { scope, err := r.authorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionVoidApproval) if err != nil { return nil, err } quorum, documentVersion, err := r.probo.DocumentApprovals.VoidApproval(ctx, scope, input.DocumentVersionID) 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 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 } } scope := coredata.NewScopeFromObjectID(input.DocumentIds[0]) if err := r.probo.Documents.BulkSoftDelete(ctx, scope, input.DocumentIds); 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 } } scope := coredata.NewScopeFromObjectID(input.DocumentIds[0]) if err := r.probo.Documents.BulkArchive(ctx, scope, 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 } } scope := coredata.NewScopeFromObjectID(input.DocumentIds[0]) if err := r.probo.Documents.BulkUnarchive(ctx, scope, 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 } } scope := coredata.NewScopeFromObjectID(input.DocumentIds[0]) identity := authn.IdentityFromContext(ctx) options := probo.ExportPDFOptions{ WithWatermark: input.WithWatermark, WithSignatures: input.WithSignatures, WatermarkEmail: input.WatermarkEmail, } documentExport, exportErr := r.probo.Documents.RequestExport(ctx, scope, 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) { scope, err := r.authorize(ctx, input.DocumentID, probo.ActionDocumentChangelogGenerate) if err != nil { return nil, err } changelog, err := r.probo.Documents.GenerateChangelog(ctx, scope, 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) { scope, err := r.authorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionSignatureRequest) if err != nil { return nil, err } documentVersionSignature, err := r.probo.Documents.RequestSignature( ctx, scope, probo.RequestSignatureRequest{ DocumentVersionID: input.DocumentVersionID, Signatory: input.SignatoryID, }, ) 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 errNotPublished, ok := errors.AsType[*probo.ErrDocumentVersionNotPublished](err); ok { return nil, gqlutils.Conflict(ctx, errNotPublished) } if errNotCurrent, ok := errors.AsType[*probo.ErrDocumentVersionNotCurrent](err); ok { return nil, gqlutils.Conflict(ctx, errNotCurrent) } if errContractEnded, ok := errors.AsType[*probo.ErrProfileContractEnded](err); ok { return nil, gqlutils.Conflict(ctx, errContractEnded) } 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 } } scope := coredata.NewScopeFromObjectID(input.DocumentIds[0]) documentVersionSignatures, err := r.probo.Documents.BulkRequestSignatures( ctx, scope, probo.BulkRequestSignaturesRequest{ DocumentIDs: input.DocumentIds, SignatoryIDs: input.SignatoryIds, }, ) if err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return nil, gqlutils.NotFound(ctx, err) } if errNotPublished, ok := errors.AsType[*probo.ErrDocumentVersionNotPublished](err); ok { return nil, gqlutils.Conflict(ctx, errNotPublished) } if errContractEnded, ok := errors.AsType[*probo.ErrProfileContractEnded](err); ok { return nil, gqlutils.Conflict(ctx, errContractEnded) } 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 } // CancelSignatureRequest is the resolver for the cancelSignatureRequest field. func (r *mutationResolver) CancelSignatureRequest(ctx context.Context, input types.CancelSignatureRequestInput) (*types.CancelSignatureRequestPayload, error) { scope, err := r.authorize(ctx, input.DocumentVersionSignatureID, probo.ActionDocumentVersionCancelSignature) if err != nil { return nil, err } if err := r.probo.Documents.CancelSignatureRequest(ctx, scope, input.DocumentVersionSignatureID); 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) { scope, err := r.authorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionSign) if err != nil { return nil, err } identity := authn.IdentityFromContext(ctx) httpReq := gqlutils.HTTPRequestFromContext(ctx) signerIP := clientip.Extract(httpReq) documentVersionSignature, err := r.probo.Documents.SignDocumentVersionByIdentity( ctx, scope, probo.SignDocumentVersionRequest{ DocumentVersionID: input.DocumentVersionID, IdentityID: identity.ID, 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 errNotPublished, ok := errors.AsType[*probo.ErrDocumentVersionNotPublished](err); ok { return nil, gqlutils.Invalid(ctx, errNotPublished) } if errAlreadySigned, ok := errors.AsType[*probo.ErrDocumentVersionSignatureAlreadySigned](err); ok { return nil, gqlutils.Conflict(ctx, errAlreadySigned) } 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) { scope, err := r.authorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionApprove) if err != nil { return nil, err } identity := authn.IdentityFromContext(ctx) httpReq := gqlutils.HTTPRequestFromContext(ctx) signerIP := clientip.Extract(httpReq) decision, err := r.probo.DocumentApprovals.Approve(ctx, scope, 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) { scope, err := r.authorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionReject) if err != nil { return nil, err } identity := authn.IdentityFromContext(ctx) decision, err := r.probo.DocumentApprovals.Reject(ctx, scope, 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) { scope, err := r.authorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionExportPDF) if err != nil { return nil, err } 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 := r.probo.Documents.ExportPDF(ctx, scope, 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) { scope, err := r.authorize(ctx, input.DocumentVersionID, probo.ActionEmployeeDocumentVersionExportPDF) if err != nil { return nil, err } documentVersion, err := r.probo.Documents.GetVersion(ctx, scope, 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 = r.probo.Documents.GetWithFilter(ctx, scope, 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 := r.probo.Documents.ExportPDF(ctx, scope, 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 } // 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 } documentConnectionResolver struct{ *Resolver } documentVersionResolver struct{ *Resolver } documentVersionApprovalDecisionResolver struct{ *Resolver } documentVersionApprovalDecisionConnectionResolver struct{ *Resolver } documentVersionApprovalQuorumResolver struct{ *Resolver } documentVersionApprovalQuorumConnectionResolver struct{ *Resolver } documentVersionConnectionResolver struct{ *Resolver } documentVersionSignatureResolver struct{ *Resolver } documentVersionSignatureConnectionResolver struct{ *Resolver } employeeDocumentResolver struct{ *Resolver } employeeDocumentVersionResolver struct{ *Resolver } )