Add employee page

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2025-11-18 22:56:22 +01:00
parent 2f4cfd7ac0
commit 975d16c623
44 changed files with 5731 additions and 1842 deletions

View File

@@ -714,7 +714,9 @@ func (r *documentResolver) Versions(ctx context.Context, obj *types.Document, fi
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := prb.Documents.ListVersions(ctx, obj.ID, cursor)
versionFilter := coredata.NewDocumentVersionFilter()
page, err := prb.Documents.ListVersions(ctx, obj.ID, cursor, versionFilter)
if err != nil {
panic(fmt.Errorf("cannot list document versions: %w", err))
}
@@ -785,7 +787,7 @@ func (r *documentConnectionResolver) TotalCount(ctx context.Context, obj *types.
// Document is the resolver for the document field.
func (r *documentVersionResolver) Document(ctx context.Context, obj *types.DocumentVersion) (*types.Document, error) {
r.MustBeAuthorized(ctx, obj.ID, authz.ActionDocument)
r.MustBeAuthorized(ctx, obj.ID, authz.ActionGetDocument)
prb := r.ProboService(ctx, obj.ID.TenantID())
@@ -862,6 +864,24 @@ func (r *documentVersionResolver) Signatures(ctx context.Context, obj *types.Doc
return types.NewDocumentVersionSignatureConnection(page), nil
}
// Signed is the resolver for the signed field.
func (r *documentVersionResolver) Signed(ctx context.Context, obj *types.DocumentVersion) (bool, error) {
r.MustBeAuthorized(ctx, obj.ID, authz.ActionGetSigned)
user := UserFromContext(ctx)
if user == nil {
panic(fmt.Errorf("user not found in context"))
}
prb := r.ProboService(ctx, obj.ID.TenantID())
signed, err := prb.Documents.IsVersionSignedByUserEmail(ctx, obj.ID, user.EmailAddress)
if err != nil {
panic(fmt.Errorf("cannot check if document version is signed: %w", err))
}
return signed, nil
}
// DocumentVersion is the resolver for the documentVersion field.
func (r *documentVersionSignatureResolver) DocumentVersion(ctx context.Context, obj *types.DocumentVersionSignature) (*types.DocumentVersion, error) {
r.MustBeAuthorized(ctx, obj.ID, authz.ActionDocumentVersion)
@@ -3551,6 +3571,31 @@ func (r *mutationResolver) CancelSignatureRequest(ctx context.Context, input typ
}, nil
}
// SignDocument is the resolver for the signDocument field.
func (r *mutationResolver) SignDocument(ctx context.Context, input types.SignDocumentInput) (*types.SignDocumentPayload, error) {
r.MustBeAuthorized(ctx, input.DocumentVersionID, authz.ActionSignDocument)
user := UserFromContext(ctx)
if user == nil {
panic(fmt.Errorf("user not found in context"))
}
prb := r.ProboService(ctx, input.DocumentVersionID.TenantID())
documentVersionSignature, err := prb.Documents.SignDocumentVersionByEmail(ctx, input.DocumentVersionID, user.EmailAddress)
if err != nil {
var errAlreadySigned *coredata.ErrDocumentVersionSignatureAlreadySigned
if errors.As(err, &errAlreadySigned) {
return nil, gqlutils.Conflict(errAlreadySigned)
}
panic(fmt.Errorf("cannot sign document: %w", err))
}
return &types.SignDocumentPayload{
DocumentVersionSignature: types.NewDocumentVersionSignature(documentVersionSignature),
}, nil
}
// ExportDocumentVersionPDF is the resolver for the exportDocumentVersionPDF field.
func (r *mutationResolver) ExportDocumentVersionPDF(ctx context.Context, input types.ExportDocumentVersionPDFInput) (*types.ExportDocumentVersionPDFPayload, error) {
r.MustBeAuthorized(ctx, input.DocumentVersionID, authz.ActionExportDocumentVersionPDF)
@@ -3573,6 +3618,49 @@ func (r *mutationResolver) ExportDocumentVersionPDF(ctx context.Context, input t
}, nil
}
// ExportSignableVersionDocumentPDF is the resolver for the exportSignableVersionDocumentPDF field.
func (r *mutationResolver) ExportSignableVersionDocumentPDF(ctx context.Context, input types.ExportSignableDocumentVersionPDFInput) (*types.ExportSignableDocumentVersionPDFPayload, error) {
r.MustBeAuthorized(ctx, input.DocumentVersionID, authz.ActionExportSignableVersionDocumentPDF)
prb := r.ProboService(ctx, input.DocumentVersionID.TenantID())
documentVersion, err := prb.Documents.GetVersion(ctx, input.DocumentVersionID)
if err != nil {
panic(fmt.Errorf("cannot get document version: %w", err))
}
user := UserFromContext(ctx)
if user == nil {
panic(fmt.Errorf("user not found in context"))
}
documentFilter := coredata.NewDocumentFilter(nil).WithUserEmail(&user.EmailAddress)
_, err = prb.Documents.GetWithFilter(ctx, documentVersion.DocumentID, documentFilter)
if err != nil {
var errNotFound *coredata.ErrDocumentNotFound
if errors.As(err, &errNotFound) {
return nil, gqlutils.NotFound(errNotFound)
}
panic(fmt.Errorf("cannot get signable document: %w", err))
}
options := probo.ExportPDFOptions{
WithSignatures: false,
WithWatermark: true,
WatermarkEmail: &user.EmailAddress,
}
pdf, err := prb.Documents.ExportPDF(ctx, input.DocumentVersionID, options)
if err != nil {
panic(fmt.Errorf("cannot export signable document PDF: %w", err))
}
return &types.ExportSignableDocumentVersionPDFPayload{
Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
}, nil
}
// CreateVendorRiskAssessment is the resolver for the createVendorRiskAssessment field.
func (r *mutationResolver) CreateVendorRiskAssessment(ctx context.Context, input types.CreateVendorRiskAssessmentInput) (*types.CreateVendorRiskAssessmentPayload, error) {
r.MustBeAuthorized(ctx, input.VendorID, authz.ActionCreateVendorRiskAssessment)
@@ -5994,6 +6082,59 @@ func (r *sAMLConfigurationResolver) TestLoginURL(ctx context.Context, obj *types
return fmt.Sprintf("%s/connect/saml/login/%s", parts[0], obj.ID), nil
}
// Signed is the resolver for the signed field.
func (r *signableDocumentResolver) Signed(ctx context.Context, obj *types.SignableDocument) (bool, error) {
r.MustBeAuthorized(ctx, obj.ID, authz.ActionGetSigned)
user := UserFromContext(ctx)
if user == nil {
panic(fmt.Errorf("user not found in context"))
}
prb := r.ProboService(ctx, obj.ID.TenantID())
signed, err := prb.Documents.IsSigned(ctx, obj.ID, user.EmailAddress)
if err != nil {
panic(fmt.Errorf("cannot check if document is signed: %w", err))
}
return signed, nil
}
// Versions is the resolver for the versions field.
func (r *signableDocumentResolver) Versions(ctx context.Context, obj *types.SignableDocument, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentVersionOrderBy, filter *types.DocumentVersionFilter) (*types.DocumentVersionConnection, error) {
r.MustBeAuthorized(ctx, obj.ID, authz.ActionListSignableDocumentVersion)
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)
user := UserFromContext(ctx)
if user == nil {
panic(fmt.Errorf("user not found in context"))
}
versionFilter := coredata.NewDocumentVersionFilter().WithUserEmail(&user.EmailAddress)
page, err := prb.Documents.ListVersions(ctx, obj.ID, cursor, versionFilter)
if err != nil {
panic(fmt.Errorf("cannot list signable document versions: %w", err))
}
return types.NewDocumentVersionConnection(page), nil
}
// Organization is the resolver for the organization field.
func (r *snapshotResolver) Organization(ctx context.Context, obj *types.Snapshot) (*types.Organization, error) {
r.MustBeAuthorized(ctx, obj.ID, authz.ActionGetOrganization)
@@ -6349,7 +6490,7 @@ func (r *trustCenterAccessResolver) AvailableDocumentAccesses(ctx context.Contex
// Document is the resolver for the document field.
func (r *trustCenterDocumentAccessResolver) Document(ctx context.Context, obj *types.TrustCenterDocumentAccess) (*types.Document, error) {
r.MustBeAuthorized(ctx, obj.TrustCenterAccessID, authz.ActionDocument)
r.MustBeAuthorized(ctx, obj.ID, authz.ActionGet)
if obj.DocumentID == nil {
return nil, nil
@@ -6979,6 +7120,85 @@ func (r *viewerResolver) Organizations(ctx context.Context, obj *types.Viewer, f
return types.NewOrganizationConnection(page), nil
}
// SignableDocuments is the resolver for the signableDocuments field.
func (r *viewerResolver) SignableDocuments(ctx context.Context, obj *types.Viewer, organizationID gid.GID, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) (*types.SignableDocumentConnection, error) {
r.MustBeAuthorized(ctx, organizationID, authz.ActionListSignableDocuments)
prb := r.ProboService(ctx, organizationID.TenantID())
pageOrderBy := page.OrderBy[coredata.DocumentOrderField]{
Field: coredata.DocumentOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.DocumentOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
user := UserFromContext(ctx)
if user == nil {
panic(fmt.Errorf("user not found in context"))
}
documentFilter := coredata.NewDocumentFilter(nil).WithUserEmail(&user.EmailAddress)
documentsPage, err := prb.Documents.ListByOrganizationID(ctx, organizationID, cursor, documentFilter)
if err != nil {
panic(fmt.Errorf("cannot list organization signable documents: %w", err))
}
signableDocuments := make([]*types.SignableDocument, len(documentsPage.Data))
for i, doc := range documentsPage.Data {
signableDocuments[i] = &types.SignableDocument{
ID: doc.ID,
Title: doc.Title,
DocumentType: doc.DocumentType,
Classification: doc.Classification,
CreatedAt: doc.CreatedAt,
UpdatedAt: doc.UpdatedAt,
}
}
page := page.NewPage(signableDocuments, documentsPage.Cursor)
return types.NewSignableDocumentConnection(page), nil
}
// SignableDocument is the resolver for the signableDocument field.
func (r *viewerResolver) SignableDocument(ctx context.Context, obj *types.Viewer, id gid.GID) (*types.SignableDocument, error) {
r.MustBeAuthorized(ctx, id, authz.ActionGetSignableDocument)
prb := r.ProboService(ctx, id.TenantID())
user := UserFromContext(ctx)
if user == nil {
panic(fmt.Errorf("user not found in context"))
}
documentFilter := coredata.NewDocumentFilter(nil).WithUserEmail(&user.EmailAddress)
document, err := prb.Documents.GetWithFilter(ctx, id, documentFilter)
if err != nil {
var errNotFound *coredata.ErrDocumentNotFound
if errors.As(err, &errNotFound) {
return nil, gqlutils.NotFound(errNotFound)
}
panic(fmt.Errorf("cannot get signable document: %w", err))
}
return &types.SignableDocument{
ID: document.ID,
Title: document.Title,
DocumentType: document.DocumentType,
Classification: document.Classification,
CreatedAt: document.CreatedAt,
UpdatedAt: document.UpdatedAt,
}, nil
}
// Asset returns schema.AssetResolver implementation.
func (r *Resolver) Asset() schema.AssetResolver { return &assetResolver{r} }
@@ -7144,6 +7364,11 @@ func (r *Resolver) SAMLConfiguration() schema.SAMLConfigurationResolver {
return &sAMLConfigurationResolver{r}
}
// SignableDocument returns schema.SignableDocumentResolver implementation.
func (r *Resolver) SignableDocument() schema.SignableDocumentResolver {
return &signableDocumentResolver{r}
}
// Snapshot returns schema.SnapshotResolver implementation.
func (r *Resolver) Snapshot() schema.SnapshotResolver { return &snapshotResolver{r} }
@@ -7277,6 +7502,7 @@ type reportResolver struct{ *Resolver }
type riskResolver struct{ *Resolver }
type riskConnectionResolver struct{ *Resolver }
type sAMLConfigurationResolver struct{ *Resolver }
type signableDocumentResolver struct{ *Resolver }
type snapshotResolver struct{ *Resolver }
type snapshotConnectionResolver struct{ *Resolver }
type taskResolver struct{ *Resolver }