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.90 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 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 { 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) { 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 { 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 := prb.Documents.GetVersion(ctx, 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.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 { 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) { 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 { 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.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 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) { 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 } // PublishDocument is the resolver for the publishDocument field. func (r *mutationResolver) PublishDocument(ctx context.Context, input types.PublishDocumentInput) (*types.PublishDocumentPayload, error) { action := probo.ActionDocumentVersionPublish if !input.Minor && len(input.ApproverIds) > 0 { action = probo.ActionDocumentVersionRequestApproval } if err := r.authorize(ctx, input.DocumentID, action); err != nil { return nil, err } prb := r.ProboService(ctx, input.DocumentID.TenantID()) result, err := prb.Documents.PublishVersion(ctx, 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 errMinor, ok := errors.AsType[*probo.ErrCannotPublishMinorWithoutMajor](err); ok { return nil, gqlutils.Invalid(ctx, errMinor) } 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 } } prb := r.ProboService(ctx, input.DocumentIds[0].TenantID()) versions, documents, err := prb.DocumentApprovals.BulkPublishVersions(ctx, 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) { 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 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 } } 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 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 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 } } 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 { 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 } // 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 } // 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 }