Split employee document policy from core document actions
Introduce dedicated employee-scoped IAM actions and update all resolvers and frontend mutations accordingly. Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
@@ -46,7 +46,7 @@ import type { DocumentApprovePage_approveMutation } from "#/__generated__/core/D
|
|||||||
import type { DocumentApprovePage_rejectMutation } from "#/__generated__/core/DocumentApprovePage_rejectMutation.graphql";
|
import type { DocumentApprovePage_rejectMutation } from "#/__generated__/core/DocumentApprovePage_rejectMutation.graphql";
|
||||||
import type { DocumentApprovePageDecisionFragment$key } from "#/__generated__/core/DocumentApprovePageDecisionFragment.graphql";
|
import type { DocumentApprovePageDecisionFragment$key } from "#/__generated__/core/DocumentApprovePageDecisionFragment.graphql";
|
||||||
import type { DocumentApprovePageDocumentFragment$key } from "#/__generated__/core/DocumentApprovePageDocumentFragment.graphql";
|
import type { DocumentApprovePageDocumentFragment$key } from "#/__generated__/core/DocumentApprovePageDocumentFragment.graphql";
|
||||||
import type { DocumentApprovePageExportPDFMutation } from "#/__generated__/core/DocumentApprovePageExportPDFMutation.graphql";
|
import type { DocumentApprovePageExportEmployeePDFMutation } from "#/__generated__/core/DocumentApprovePageExportEmployeePDFMutation.graphql";
|
||||||
import type { DocumentApprovePageQuery } from "#/__generated__/core/DocumentApprovePageQuery.graphql";
|
import type { DocumentApprovePageQuery } from "#/__generated__/core/DocumentApprovePageQuery.graphql";
|
||||||
import type { DocumentApprovePageVersionRowFragment$key } from "#/__generated__/core/DocumentApprovePageVersionRowFragment.graphql";
|
import type { DocumentApprovePageVersionRowFragment$key } from "#/__generated__/core/DocumentApprovePageVersionRowFragment.graphql";
|
||||||
import { PDFPreview } from "#/components/documents/PDFPreview";
|
import { PDFPreview } from "#/components/documents/PDFPreview";
|
||||||
@@ -100,9 +100,6 @@ const decisionFragment = graphql`
|
|||||||
state
|
state
|
||||||
canApprove: permission(action: "core:document-version:approve")
|
canApprove: permission(action: "core:document-version:approve")
|
||||||
canReject: permission(action: "core:document-version:reject")
|
canReject: permission(action: "core:document-version:reject")
|
||||||
documentVersion {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -131,10 +128,10 @@ const rejectDocumentVersionMutation = graphql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const exportPDFMutation = graphql`
|
const exportPDFMutation = graphql`
|
||||||
mutation DocumentApprovePageExportPDFMutation(
|
mutation DocumentApprovePageExportEmployeePDFMutation(
|
||||||
$input: ExportDocumentVersionPDFInput!
|
$input: ExportEmployeeDocumentVersionPDFInput!
|
||||||
) {
|
) {
|
||||||
exportDocumentVersionPDF(input: $input) {
|
exportEmployeeDocumentVersionPDF(input: $input) {
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -227,9 +224,10 @@ function VersionRow({
|
|||||||
|
|
||||||
function ViewerDecision(props: {
|
function ViewerDecision(props: {
|
||||||
fragmentRef: DocumentApprovePageDecisionFragment$key;
|
fragmentRef: DocumentApprovePageDecisionFragment$key;
|
||||||
|
versionId: string;
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
}) {
|
}) {
|
||||||
const { fragmentRef, onBack } = props;
|
const { fragmentRef, versionId, onBack } = props;
|
||||||
const { __ } = useTranslate();
|
const { __ } = useTranslate();
|
||||||
const decision = useFragment(decisionFragment, fragmentRef);
|
const decision = useFragment(decisionFragment, fragmentRef);
|
||||||
const rejectDialogRef = useDialogRef();
|
const rejectDialogRef = useDialogRef();
|
||||||
@@ -311,7 +309,7 @@ function ViewerDecision(props: {
|
|||||||
approveVersion({
|
approveVersion({
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
documentVersionId: decision.documentVersion.id,
|
documentVersionId: versionId,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
onCompleted(_, errors) {
|
onCompleted(_, errors) {
|
||||||
@@ -372,7 +370,7 @@ function ViewerDecision(props: {
|
|||||||
rejectVersion({
|
rejectVersion({
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
documentVersionId: decision.documentVersion.id,
|
documentVersionId: versionId,
|
||||||
comment: rejectComment || undefined,
|
comment: rejectComment || undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -434,7 +432,7 @@ function DocumentApproveContent({
|
|||||||
|
|
||||||
usePageTitle(__("Review and Approve Document"));
|
usePageTitle(__("Review and Approve Document"));
|
||||||
|
|
||||||
const [exportPDF] = useMutation<DocumentApprovePageExportPDFMutation>(
|
const [exportPDF] = useMutation<DocumentApprovePageExportEmployeePDFMutation>(
|
||||||
exportPDFMutation,
|
exportPDFMutation,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -448,8 +446,6 @@ function DocumentApproveContent({
|
|||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
documentVersionId: selectedVersion.id,
|
documentVersionId: selectedVersion.id,
|
||||||
withWatermark: true,
|
|
||||||
withSignatures: false,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
onCompleted: (data, errors): void => {
|
onCompleted: (data, errors): void => {
|
||||||
@@ -464,8 +460,8 @@ function DocumentApproveContent({
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.exportDocumentVersionPDF?.data) {
|
if (data.exportEmployeeDocumentVersionPDF?.data) {
|
||||||
const dataUrl = data.exportDocumentVersionPDF.data;
|
const dataUrl = data.exportEmployeeDocumentVersionPDF.data;
|
||||||
pdfUrlRef.current = dataUrl;
|
pdfUrlRef.current = dataUrl;
|
||||||
setPdfUrl(dataUrl);
|
setPdfUrl(dataUrl);
|
||||||
}
|
}
|
||||||
@@ -522,6 +518,7 @@ function DocumentApproveContent({
|
|||||||
? (
|
? (
|
||||||
<ViewerDecision
|
<ViewerDecision
|
||||||
fragmentRef={decision}
|
fragmentRef={decision}
|
||||||
|
versionId={selectedVersion.id}
|
||||||
onBack={() =>
|
onBack={() =>
|
||||||
void navigate(`/organizations/${organizationId}/employee/approvals`)}
|
void navigate(`/organizations/${organizationId}/employee/approvals`)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { useNavigate } from "react-router";
|
|||||||
import { useWindowSize } from "usehooks-ts";
|
import { useWindowSize } from "usehooks-ts";
|
||||||
|
|
||||||
import type { EmployeeDocumentSignaturePageDocumentFragment$key } from "#/__generated__/core/EmployeeDocumentSignaturePageDocumentFragment.graphql";
|
import type { EmployeeDocumentSignaturePageDocumentFragment$key } from "#/__generated__/core/EmployeeDocumentSignaturePageDocumentFragment.graphql";
|
||||||
import type { EmployeeDocumentSignaturePageExportSignablePDFMutation } from "#/__generated__/core/EmployeeDocumentSignaturePageExportSignablePDFMutation.graphql";
|
import type { EmployeeDocumentSignaturePageExportEmployeePDFMutation } from "#/__generated__/core/EmployeeDocumentSignaturePageExportEmployeePDFMutation.graphql";
|
||||||
import type { EmployeeDocumentSignaturePageQuery } from "#/__generated__/core/EmployeeDocumentSignaturePageQuery.graphql";
|
import type { EmployeeDocumentSignaturePageQuery } from "#/__generated__/core/EmployeeDocumentSignaturePageQuery.graphql";
|
||||||
import type { EmployeeDocumentSignaturePageSignMutation } from "#/__generated__/core/EmployeeDocumentSignaturePageSignMutation.graphql";
|
import type { EmployeeDocumentSignaturePageSignMutation } from "#/__generated__/core/EmployeeDocumentSignaturePageSignMutation.graphql";
|
||||||
import { PDFPreview } from "#/components/documents/PDFPreview";
|
import { PDFPreview } from "#/components/documents/PDFPreview";
|
||||||
@@ -80,11 +80,11 @@ const signDocumentMutation = graphql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const exportSignableVersionDocumentPDFMutation = graphql`
|
const exportEmployeeDocumentVersionPDFMutation = graphql`
|
||||||
mutation EmployeeDocumentSignaturePageExportSignablePDFMutation(
|
mutation EmployeeDocumentSignaturePageExportEmployeePDFMutation(
|
||||||
$input: ExportSignableDocumentVersionPDFInput!
|
$input: ExportEmployeeDocumentVersionPDFInput!
|
||||||
) {
|
) {
|
||||||
exportSignableVersionDocumentPDF(input: $input) {
|
exportEmployeeDocumentVersionPDF(input: $input) {
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,9 +145,9 @@ function DocumentSignatureContent({
|
|||||||
signDocumentMutation,
|
signDocumentMutation,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [exportSignableVersionDocumentPDF]
|
const [exportEmployeeDocumentVersionPDF]
|
||||||
= useMutation<EmployeeDocumentSignaturePageExportSignablePDFMutation>(
|
= useMutation<EmployeeDocumentSignaturePageExportEmployeePDFMutation>(
|
||||||
exportSignableVersionDocumentPDFMutation,
|
exportEmployeeDocumentVersionPDFMutation,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [pdfUrl, setPdfUrl] = useState<string | null>(null);
|
const [pdfUrl, setPdfUrl] = useState<string | null>(null);
|
||||||
@@ -191,7 +191,7 @@ function DocumentSignatureContent({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!selectedVersion?.id) return;
|
if (!selectedVersion?.id) return;
|
||||||
|
|
||||||
exportSignableVersionDocumentPDF({
|
exportEmployeeDocumentVersionPDF({
|
||||||
variables: {
|
variables: {
|
||||||
input: {
|
input: {
|
||||||
documentVersionId: selectedVersion.id,
|
documentVersionId: selectedVersion.id,
|
||||||
@@ -209,8 +209,8 @@ function DocumentSignatureContent({
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.exportSignableVersionDocumentPDF?.data) {
|
if (data.exportEmployeeDocumentVersionPDF?.data) {
|
||||||
const dataUrl = data.exportSignableVersionDocumentPDF.data;
|
const dataUrl = data.exportEmployeeDocumentVersionPDF.data;
|
||||||
pdfUrlRef.current = dataUrl;
|
pdfUrlRef.current = dataUrl;
|
||||||
setPdfUrl(dataUrl);
|
setPdfUrl(dataUrl);
|
||||||
}
|
}
|
||||||
@@ -230,7 +230,7 @@ function DocumentSignatureContent({
|
|||||||
return () => {
|
return () => {
|
||||||
pdfUrlRef.current = null;
|
pdfUrlRef.current = null;
|
||||||
};
|
};
|
||||||
}, [selectedVersion?.id, exportSignableVersionDocumentPDF, toast, __]);
|
}, [selectedVersion?.id, exportEmployeeDocumentVersionPDF, toast, __]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -23,47 +23,10 @@ import (
|
|||||||
"go.probo.inc/probo/e2e/internal/testutil"
|
"go.probo.inc/probo/e2e/internal/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// getOwnerProfileID queries the organization profiles and returns the first one (the owner's).
|
|
||||||
func getOwnerProfileID(t *testing.T, owner *testutil.Client) string {
|
func getOwnerProfileID(t *testing.T, owner *testutil.Client) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
query := `
|
return owner.GetProfileID().String()
|
||||||
query GetProfiles($orgId: ID!) {
|
|
||||||
node(id: $orgId) {
|
|
||||||
... on Organization {
|
|
||||||
profiles(first: 1) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
var result struct {
|
|
||||||
Node struct {
|
|
||||||
Profiles struct {
|
|
||||||
Edges []struct {
|
|
||||||
Node struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
} `json:"node"`
|
|
||||||
} `json:"edges"`
|
|
||||||
} `json:"profiles"`
|
|
||||||
} `json:"node"`
|
|
||||||
}
|
|
||||||
|
|
||||||
err := owner.Execute(
|
|
||||||
query,
|
|
||||||
map[string]any{"orgId": owner.GetOrganizationID().String()},
|
|
||||||
&result,
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotEmpty(t, result.Node.Profiles.Edges)
|
|
||||||
|
|
||||||
return result.Node.Profiles.Edges[0].Node.ID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// createTestDocument creates a document and returns its ID and the document version ID
|
// createTestDocument creates a document and returns its ID and the document version ID
|
||||||
|
|||||||
1147
e2e/console/employee_document_test.go
Normal file
1147
e2e/console/employee_document_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -41,6 +41,8 @@ const (
|
|||||||
RoleOwner TestRole = "OWNER"
|
RoleOwner TestRole = "OWNER"
|
||||||
RoleAdmin TestRole = "ADMIN"
|
RoleAdmin TestRole = "ADMIN"
|
||||||
RoleViewer TestRole = "VIEWER"
|
RoleViewer TestRole = "VIEWER"
|
||||||
|
RoleEmployee TestRole = "EMPLOYEE"
|
||||||
|
RoleAuditor TestRole = "AUDITOR"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
@@ -50,6 +52,7 @@ type Client struct {
|
|||||||
mailpitBaseURL string
|
mailpitBaseURL string
|
||||||
role TestRole
|
role TestRole
|
||||||
userID gid.GID
|
userID gid.GID
|
||||||
|
profileID gid.GID
|
||||||
organizationID gid.GID
|
organizationID gid.GID
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,6 +132,7 @@ func (c *Client) SetupTestUserInOrg(ownerClient *Client) {
|
|||||||
// Owner invites user to organization
|
// Owner invites user to organization
|
||||||
profileID, identityID := ownerClient.createUser(email, fullName, coredata.MembershipRole(c.role))
|
profileID, identityID := ownerClient.createUser(email, fullName, coredata.MembershipRole(c.role))
|
||||||
c.userID = identityID
|
c.userID = identityID
|
||||||
|
c.profileID = profileID
|
||||||
ownerClient.inviteUser(profileID)
|
ownerClient.inviteUser(profileID)
|
||||||
token := c.getActivationToken(email)
|
token := c.getActivationToken(email)
|
||||||
passwordToken := c.activateUser(token)
|
passwordToken := c.activateUser(token)
|
||||||
@@ -212,6 +216,9 @@ func (c *Client) createOrganization(name string) gid.GID {
|
|||||||
Organization struct {
|
Organization struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
} `json:"organization"`
|
} `json:"organization"`
|
||||||
|
Profile struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
} `json:"profile"`
|
||||||
} `json:"createOrganization"`
|
} `json:"createOrganization"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,6 +230,11 @@ func (c *Client) createOrganization(name string) gid.GID {
|
|||||||
orgID, err := gid.ParseGID(result.CreateOrganization.Organization.ID)
|
orgID, err := gid.ParseGID(result.CreateOrganization.Organization.ID)
|
||||||
require.NoError(c.T, err, "cannot parse organization ID")
|
require.NoError(c.T, err, "cannot parse organization ID")
|
||||||
|
|
||||||
|
profileID, err := gid.ParseGID(result.CreateOrganization.Profile.ID)
|
||||||
|
require.NoError(c.T, err, "cannot parse profile ID")
|
||||||
|
|
||||||
|
c.profileID = profileID
|
||||||
|
|
||||||
return orgID
|
return orgID
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -483,6 +495,10 @@ func (c *Client) GetUserID() gid.GID {
|
|||||||
return c.userID
|
return c.userID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetProfileID() gid.GID {
|
||||||
|
return c.profileID
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) GetOrganizationID() gid.GID {
|
func (c *Client) GetOrganizationID() gid.GID {
|
||||||
return c.organizationID
|
return c.organizationID
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ package coredata
|
|||||||
import (
|
import (
|
||||||
"github.com/jackc/pgx/v5"
|
"github.com/jackc/pgx/v5"
|
||||||
"go.probo.inc/probo/pkg/gid"
|
"go.probo.inc/probo/pkg/gid"
|
||||||
"go.probo.inc/probo/pkg/mail"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -25,8 +24,8 @@ type (
|
|||||||
query *string
|
query *string
|
||||||
trustCenterVisibilities []TrustCenterVisibility
|
trustCenterVisibilities []TrustCenterVisibility
|
||||||
published *bool
|
published *bool
|
||||||
userEmail *mail.Addr
|
employeeIdentityID *gid.GID
|
||||||
approverIdentityID *gid.GID
|
employeeFilterModes []EmployeeFilterMode
|
||||||
documentTypes []DocumentType
|
documentTypes []DocumentType
|
||||||
classifications []DocumentClassification
|
classifications []DocumentClassification
|
||||||
status []DocumentStatus
|
status []DocumentStatus
|
||||||
@@ -56,13 +55,9 @@ func (f *DocumentFilter) WithPublished(published *bool) *DocumentFilter {
|
|||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *DocumentFilter) WithUserEmail(userEmail *mail.Addr) *DocumentFilter {
|
func (f *DocumentFilter) WithEmployeeIdentityID(identityID *gid.GID, modes ...EmployeeFilterMode) *DocumentFilter {
|
||||||
f.userEmail = userEmail
|
f.employeeIdentityID = identityID
|
||||||
return f
|
f.employeeFilterModes = modes
|
||||||
}
|
|
||||||
|
|
||||||
func (f *DocumentFilter) WithApproverIdentityID(identityID *gid.GID) *DocumentFilter {
|
|
||||||
f.approverIdentityID = identityID
|
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,12 +109,17 @@ func (f *DocumentFilter) SQLArguments() pgx.NamedArgs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var employeeFilterModes []string
|
||||||
|
for _, m := range f.employeeFilterModes {
|
||||||
|
employeeFilterModes = append(employeeFilterModes, string(m))
|
||||||
|
}
|
||||||
|
|
||||||
return pgx.NamedArgs{
|
return pgx.NamedArgs{
|
||||||
"query": f.query,
|
"query": f.query,
|
||||||
"trust_center_visibilities": visibilities,
|
"trust_center_visibilities": visibilities,
|
||||||
"published": f.published,
|
"published": f.published,
|
||||||
"user_email": f.userEmail,
|
"employee_identity_id": f.employeeIdentityID,
|
||||||
"approver_identity_id": f.approverIdentityID,
|
"employee_filter_modes": employeeFilterModes,
|
||||||
"document_types": documentTypes,
|
"document_types": documentTypes,
|
||||||
"classifications": classifications,
|
"classifications": classifications,
|
||||||
"document_status": status,
|
"document_status": status,
|
||||||
@@ -151,30 +151,30 @@ func (f *DocumentFilter) SQLFragment() string {
|
|||||||
END
|
END
|
||||||
AND
|
AND
|
||||||
CASE
|
CASE
|
||||||
WHEN @user_email::text IS NULL THEN TRUE
|
WHEN @employee_identity_id::text IS NULL THEN TRUE
|
||||||
ELSE EXISTS (
|
ELSE (
|
||||||
|
(
|
||||||
|
'signature' = ANY(@employee_filter_modes::text[]) AND EXISTS (
|
||||||
SELECT 1
|
SELECT 1
|
||||||
FROM document_versions dv
|
FROM document_versions dv
|
||||||
INNER JOIN document_version_signatures dvs ON dv.id = dvs.document_version_id
|
INNER JOIN document_version_signatures dvs ON dv.id = dvs.document_version_id
|
||||||
INNER JOIN iam_membership_profiles p ON dvs.signed_by_profile_id = p.id
|
INNER JOIN iam_membership_profiles p ON dvs.signed_by_profile_id = p.id
|
||||||
INNER JOIN identities i ON p.identity_id = i.id
|
|
||||||
WHERE dv.document_id = documents.id
|
WHERE dv.document_id = documents.id
|
||||||
AND dv.status = 'PUBLISHED'
|
AND p.identity_id = @employee_identity_id::text
|
||||||
AND i.email_address = @user_email::CITEXT
|
|
||||||
AND dvs.state IN ('REQUESTED', 'SIGNED')
|
AND dvs.state IN ('REQUESTED', 'SIGNED')
|
||||||
)
|
)
|
||||||
END
|
)
|
||||||
AND
|
OR (
|
||||||
CASE
|
'approval' = ANY(@employee_filter_modes::text[]) AND EXISTS (
|
||||||
WHEN @approver_identity_id::text IS NULL THEN TRUE
|
|
||||||
ELSE EXISTS (
|
|
||||||
SELECT 1
|
SELECT 1
|
||||||
FROM document_versions dv
|
FROM document_versions dv
|
||||||
INNER JOIN document_version_approval_quorums dvaq ON dvaq.version_id = dv.id
|
INNER JOIN document_version_approval_quorums dvaq ON dvaq.version_id = dv.id
|
||||||
INNER JOIN document_version_approval_decisions dvad ON dvad.quorum_id = dvaq.id
|
INNER JOIN document_version_approval_decisions dvad ON dvad.quorum_id = dvaq.id
|
||||||
INNER JOIN iam_membership_profiles p ON dvad.approver_id = p.id
|
INNER JOIN iam_membership_profiles p ON dvad.approver_id = p.id
|
||||||
WHERE dv.document_id = documents.id
|
WHERE dv.document_id = documents.id
|
||||||
AND p.identity_id = @approver_identity_id::text
|
AND p.identity_id = @employee_identity_id::text
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
END
|
END
|
||||||
AND
|
AND
|
||||||
|
|||||||
@@ -17,14 +17,20 @@ package coredata
|
|||||||
import (
|
import (
|
||||||
"github.com/jackc/pgx/v5"
|
"github.com/jackc/pgx/v5"
|
||||||
"go.probo.inc/probo/pkg/gid"
|
"go.probo.inc/probo/pkg/gid"
|
||||||
"go.probo.inc/probo/pkg/mail"
|
)
|
||||||
|
|
||||||
|
type EmployeeFilterMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
EmployeeFilterModeSignature EmployeeFilterMode = "signature"
|
||||||
|
EmployeeFilterModeApproval EmployeeFilterMode = "approval"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
DocumentVersionFilter struct {
|
DocumentVersionFilter struct {
|
||||||
statuses []DocumentVersionStatus
|
statuses []DocumentVersionStatus
|
||||||
userEmail *mail.Addr
|
employeeIdentityID *gid.GID
|
||||||
approverIdentityID *gid.GID
|
employeeFilterModes []EmployeeFilterMode
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,13 +43,9 @@ func (f *DocumentVersionFilter) WithStatuses(statuses ...DocumentVersionStatus)
|
|||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *DocumentVersionFilter) WithUserEmail(userEmail *mail.Addr) *DocumentVersionFilter {
|
func (f *DocumentVersionFilter) WithEmployeeIdentityID(identityID *gid.GID, modes ...EmployeeFilterMode) *DocumentVersionFilter {
|
||||||
f.userEmail = userEmail
|
f.employeeIdentityID = identityID
|
||||||
return f
|
f.employeeFilterModes = modes
|
||||||
}
|
|
||||||
|
|
||||||
func (f *DocumentVersionFilter) WithApproverIdentityID(identityID *gid.GID) *DocumentVersionFilter {
|
|
||||||
f.approverIdentityID = identityID
|
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,10 +55,15 @@ func (f *DocumentVersionFilter) SQLArguments() pgx.StrictNamedArgs {
|
|||||||
filterStatuses = append(filterStatuses, s.String())
|
filterStatuses = append(filterStatuses, s.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var employeeFilterModes []string
|
||||||
|
for _, m := range f.employeeFilterModes {
|
||||||
|
employeeFilterModes = append(employeeFilterModes, string(m))
|
||||||
|
}
|
||||||
|
|
||||||
return pgx.StrictNamedArgs{
|
return pgx.StrictNamedArgs{
|
||||||
"filter_statuses": filterStatuses,
|
"filter_statuses": filterStatuses,
|
||||||
"user_email": f.userEmail,
|
"employee_identity_id": f.employeeIdentityID,
|
||||||
"approver_identity_id": f.approverIdentityID,
|
"employee_filter_modes": employeeFilterModes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,27 +76,26 @@ func (f *DocumentVersionFilter) SQLFragment() string {
|
|||||||
)
|
)
|
||||||
AND
|
AND
|
||||||
(
|
(
|
||||||
@user_email::text IS NULL
|
@employee_identity_id::text IS NULL
|
||||||
OR EXISTS (
|
OR (
|
||||||
|
'signature' = ANY(@employee_filter_modes::text[]) AND EXISTS (
|
||||||
SELECT 1
|
SELECT 1
|
||||||
FROM document_version_signatures dvs
|
FROM document_version_signatures dvs
|
||||||
INNER JOIN iam_membership_profiles p ON dvs.signed_by_profile_id = p.id
|
INNER JOIN iam_membership_profiles p ON dvs.signed_by_profile_id = p.id
|
||||||
INNER JOIN identities i ON p.identity_id = i.id
|
|
||||||
WHERE dvs.document_version_id = document_versions.id
|
WHERE dvs.document_version_id = document_versions.id
|
||||||
AND i.email_address = @user_email::CITEXT
|
AND p.identity_id = @employee_identity_id::text
|
||||||
AND dvs.state IN ('REQUESTED', 'SIGNED')
|
AND dvs.state IN ('REQUESTED', 'SIGNED')
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
AND
|
OR (
|
||||||
(
|
'approval' = ANY(@employee_filter_modes::text[]) AND EXISTS (
|
||||||
@approver_identity_id::text IS NULL
|
|
||||||
OR EXISTS (
|
|
||||||
SELECT 1
|
SELECT 1
|
||||||
FROM document_version_approval_quorums dvaq
|
FROM document_version_approval_quorums dvaq
|
||||||
INNER JOIN document_version_approval_decisions dvad ON dvad.quorum_id = dvaq.id
|
INNER JOIN document_version_approval_decisions dvad ON dvad.quorum_id = dvaq.id
|
||||||
INNER JOIN iam_membership_profiles p ON dvad.approver_id = p.id
|
INNER JOIN iam_membership_profiles p ON dvad.approver_id = p.id
|
||||||
WHERE dvaq.version_id = document_versions.id
|
WHERE dvaq.version_id = document_versions.id
|
||||||
AND p.identity_id = @approver_identity_id::text
|
AND p.identity_id = @employee_identity_id::text
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)`
|
)`
|
||||||
|
|||||||
@@ -190,7 +190,6 @@ const (
|
|||||||
ActionDocumentVersionGet = "core:document-version:get"
|
ActionDocumentVersionGet = "core:document-version:get"
|
||||||
ActionDocumentVersionList = "core:document-version:list"
|
ActionDocumentVersionList = "core:document-version:list"
|
||||||
ActionDocumentVersionExportPDF = "core:document-version:export-pdf"
|
ActionDocumentVersionExportPDF = "core:document-version:export-pdf"
|
||||||
ActionDocumentVersionExportSignable = "core:document-version:export-signable-pdf"
|
|
||||||
ActionDocumentVersionSign = "core:document-version:sign"
|
ActionDocumentVersionSign = "core:document-version:sign"
|
||||||
ActionDocumentVersionUpdate = "core:document-version:update"
|
ActionDocumentVersionUpdate = "core:document-version:update"
|
||||||
ActionDocumentVersionDeleteDraft = "core:document-version:delete-draft"
|
ActionDocumentVersionDeleteDraft = "core:document-version:delete-draft"
|
||||||
@@ -203,6 +202,11 @@ const (
|
|||||||
ActionDocumentVersionPublish = "core:document-version:publish"
|
ActionDocumentVersionPublish = "core:document-version:publish"
|
||||||
ActionDocumentVersionExport = "core:document-version:export"
|
ActionDocumentVersionExport = "core:document-version:export"
|
||||||
|
|
||||||
|
// EmployeeDocument actions
|
||||||
|
ActionEmployeeDocumentGet = "core:employee-document:get"
|
||||||
|
ActionEmployeeDocumentList = "core:employee-document:list"
|
||||||
|
ActionEmployeeDocumentVersionExportPDF = "core:employee-document-version:export-pdf"
|
||||||
|
|
||||||
// DocumentVersionSignature actions
|
// DocumentVersionSignature actions
|
||||||
ActionDocumentVersionSignatureRequest = "core:document-version-signature:request"
|
ActionDocumentVersionSignatureRequest = "core:document-version-signature:request"
|
||||||
ActionDocumentVersionCancelSignature = "core:document-version-signature:cancel"
|
ActionDocumentVersionCancelSignature = "core:document-version-signature:cancel"
|
||||||
|
|||||||
@@ -162,13 +162,18 @@ var ViewerPolicy = policy.NewPolicy(
|
|||||||
policy.Allow(ActionCustomDomainGet).WithSID("custom-domain-read").When(organizationCondition),
|
policy.Allow(ActionCustomDomainGet).WithSID("custom-domain-read").When(organizationCondition),
|
||||||
policy.Allow(ActionOrganizationContextGet).WithSID("organization-context-read").When(organizationCondition),
|
policy.Allow(ActionOrganizationContextGet).WithSID("organization-context-read").When(organizationCondition),
|
||||||
policy.Allow(
|
policy.Allow(
|
||||||
ActionDocumentVersionExportPDF, ActionDocumentVersionExportSignable, ActionDocumentVersionSign,
|
ActionDocumentVersionExportPDF, ActionDocumentVersionSign,
|
||||||
).WithSID("document-signing").When(organizationCondition),
|
).WithSID("document-signing").When(organizationCondition),
|
||||||
|
|
||||||
policy.Allow(
|
policy.Allow(
|
||||||
ActionDocumentVersionApprove, ActionDocumentVersionReject,
|
ActionDocumentVersionApprove, ActionDocumentVersionReject,
|
||||||
).WithSID("document-approval").When(organizationCondition),
|
).WithSID("document-approval").When(organizationCondition),
|
||||||
|
|
||||||
|
policy.Allow(
|
||||||
|
ActionEmployeeDocumentGet, ActionEmployeeDocumentList,
|
||||||
|
ActionEmployeeDocumentVersionExportPDF,
|
||||||
|
).WithSID("employee-document-access").When(organizationCondition),
|
||||||
|
|
||||||
policy.Allow(
|
policy.Allow(
|
||||||
ActionProcessingActivityExport,
|
ActionProcessingActivityExport,
|
||||||
ActionDataProtectionImpactAssessmentExport,
|
ActionDataProtectionImpactAssessmentExport,
|
||||||
@@ -219,9 +224,14 @@ var AuditorPolicy = policy.NewPolicy(
|
|||||||
).WithSID("entity-read-access").When(organizationCondition),
|
).WithSID("entity-read-access").When(organizationCondition),
|
||||||
|
|
||||||
policy.Allow(
|
policy.Allow(
|
||||||
ActionDocumentVersionExportPDF, ActionDocumentVersionExportSignable, ActionDocumentVersionSign,
|
ActionDocumentVersionExportPDF, ActionDocumentVersionSign,
|
||||||
).WithSID("document-signing").When(organizationCondition),
|
).WithSID("document-signing").When(organizationCondition),
|
||||||
|
|
||||||
|
policy.Allow(
|
||||||
|
ActionEmployeeDocumentGet, ActionEmployeeDocumentList,
|
||||||
|
ActionEmployeeDocumentVersionExportPDF,
|
||||||
|
).WithSID("employee-document-access").When(organizationCondition),
|
||||||
|
|
||||||
policy.Allow(
|
policy.Allow(
|
||||||
ActionStateOfApplicabilityExport,
|
ActionStateOfApplicabilityExport,
|
||||||
).WithSID("soa-export").When(organizationCondition),
|
).WithSID("soa-export").When(organizationCondition),
|
||||||
@@ -238,20 +248,19 @@ var EmployeePolicy = policy.NewPolicy(
|
|||||||
).WithSID("org-basic-access").When(organizationCondition),
|
).WithSID("org-basic-access").When(organizationCondition),
|
||||||
|
|
||||||
policy.Allow(
|
policy.Allow(
|
||||||
ActionDocumentGet, ActionDocumentList,
|
ActionEmployeeDocumentGet, ActionEmployeeDocumentList,
|
||||||
).WithSID("document-signing-access").When(organizationCondition),
|
).WithSID("employee-document-access").When(organizationCondition),
|
||||||
|
|
||||||
policy.Allow(
|
policy.Allow(
|
||||||
ActionDocumentVersionGet, ActionDocumentVersionList,
|
|
||||||
ActionDocumentVersionSign,
|
ActionDocumentVersionSign,
|
||||||
ActionDocumentVersionExportSignable,
|
ActionEmployeeDocumentVersionExportPDF,
|
||||||
).WithSID("document-version-signing").When(organizationCondition),
|
).WithSID("document-version-signing").When(organizationCondition),
|
||||||
|
|
||||||
policy.Allow(
|
policy.Allow(
|
||||||
ActionDocumentVersionApprovalList,
|
ActionDocumentVersionApprovalList,
|
||||||
ActionDocumentVersionApprove,
|
ActionDocumentVersionApprove,
|
||||||
ActionDocumentVersionReject,
|
ActionDocumentVersionReject,
|
||||||
ActionDocumentVersionExportPDF,
|
ActionEmployeeDocumentVersionExportPDF,
|
||||||
).WithSID("document-version-approval").When(organizationCondition),
|
).WithSID("document-version-approval").When(organizationCondition),
|
||||||
).WithDescription("Employee access - can sign documents, approve documents, and view internal content")
|
).WithDescription("Employee access - can sign documents, approve documents, and view internal content")
|
||||||
|
|
||||||
|
|||||||
@@ -3860,9 +3860,9 @@ type Mutation {
|
|||||||
exportDocumentVersionPDF(
|
exportDocumentVersionPDF(
|
||||||
input: ExportDocumentVersionPDFInput!
|
input: ExportDocumentVersionPDFInput!
|
||||||
): ExportDocumentVersionPDFPayload!
|
): ExportDocumentVersionPDFPayload!
|
||||||
exportSignableVersionDocumentPDF(
|
exportEmployeeDocumentVersionPDF(
|
||||||
input: ExportSignableDocumentVersionPDFInput!
|
input: ExportEmployeeDocumentVersionPDFInput!
|
||||||
): ExportSignableDocumentVersionPDFPayload!
|
): ExportEmployeeDocumentVersionPDFPayload!
|
||||||
exportProcessingActivitiesPDF(
|
exportProcessingActivitiesPDF(
|
||||||
input: ExportProcessingActivitiesPDFInput!
|
input: ExportProcessingActivitiesPDFInput!
|
||||||
): ExportProcessingActivitiesPDFPayload!
|
): ExportProcessingActivitiesPDFPayload!
|
||||||
@@ -4492,7 +4492,7 @@ input ExportDocumentVersionPDFInput {
|
|||||||
withSignatures: Boolean!
|
withSignatures: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
input ExportSignableDocumentVersionPDFInput {
|
input ExportEmployeeDocumentVersionPDFInput {
|
||||||
documentVersionId: ID!
|
documentVersionId: ID!
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5222,7 +5222,7 @@ type ExportDocumentVersionPDFPayload {
|
|||||||
data: String!
|
data: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExportSignableDocumentVersionPDFPayload {
|
type ExportEmployeeDocumentVersionPDFPayload {
|
||||||
data: String!
|
data: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ import (
|
|||||||
"go.probo.inc/probo/pkg/page"
|
"go.probo.inc/probo/pkg/page"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EmployeeDocumentFilterMode int
|
type EmployeeDocumentFilterMode string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
EmployeeDocumentFilterModeSignature EmployeeDocumentFilterMode = iota
|
EmployeeDocumentFilterModeSignature EmployeeDocumentFilterMode = "SIGNATURE"
|
||||||
EmployeeDocumentFilterModeApproval
|
EmployeeDocumentFilterModeApproval EmployeeDocumentFilterMode = "APPROVAL"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -64,6 +64,7 @@ type (
|
|||||||
|
|
||||||
EmployeeDocumentVersion struct {
|
EmployeeDocumentVersion struct {
|
||||||
ID gid.GID
|
ID gid.GID
|
||||||
|
DocumentID gid.GID
|
||||||
OrganizationID gid.GID
|
OrganizationID gid.GID
|
||||||
Major int
|
Major int
|
||||||
Minor int
|
Minor int
|
||||||
|
|||||||
@@ -1564,7 +1564,7 @@ func (r *electronicSignatureResolver) Events(ctx context.Context, obj *types.Ele
|
|||||||
|
|
||||||
// Signed is the resolver for the signed field.
|
// Signed is the resolver for the signed field.
|
||||||
func (r *employeeDocumentResolver) Signed(ctx context.Context, obj *types.EmployeeDocument) (*bool, error) {
|
func (r *employeeDocumentResolver) Signed(ctx context.Context, obj *types.EmployeeDocument) (*bool, error) {
|
||||||
if err := r.authorize(ctx, obj.ID, probo.ActionDocumentGet); err != nil {
|
if err := r.authorize(ctx, obj.ID, probo.ActionEmployeeDocumentGet); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1586,7 +1586,7 @@ func (r *employeeDocumentResolver) Signed(ctx context.Context, obj *types.Employ
|
|||||||
|
|
||||||
// ApprovalState is the resolver for the approvalState field.
|
// ApprovalState is the resolver for the approvalState field.
|
||||||
func (r *employeeDocumentResolver) ApprovalState(ctx context.Context, obj *types.EmployeeDocument) (*coredata.DocumentVersionApprovalDecisionState, error) {
|
func (r *employeeDocumentResolver) ApprovalState(ctx context.Context, obj *types.EmployeeDocument) (*coredata.DocumentVersionApprovalDecisionState, error) {
|
||||||
if err := r.authorize(ctx, obj.ID, probo.ActionDocumentGet); err != nil {
|
if err := r.authorize(ctx, obj.ID, probo.ActionEmployeeDocumentGet); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1608,7 +1608,7 @@ func (r *employeeDocumentResolver) ApprovalState(ctx context.Context, obj *types
|
|||||||
|
|
||||||
// Versions is the resolver for the versions field.
|
// 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) {
|
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.ActionDocumentVersionList); err != nil {
|
if err := r.authorize(ctx, obj.ID, probo.ActionEmployeeDocumentGet); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1629,14 +1629,20 @@ func (r *employeeDocumentResolver) Versions(ctx context.Context, obj *types.Empl
|
|||||||
|
|
||||||
identity := authn.IdentityFromContext(ctx)
|
identity := authn.IdentityFromContext(ctx)
|
||||||
|
|
||||||
versionFilter := coredata.NewDocumentVersionFilter()
|
var filterMode coredata.EmployeeFilterMode
|
||||||
switch obj.FilterMode {
|
switch obj.FilterMode {
|
||||||
case types.EmployeeDocumentFilterModeSignature:
|
case types.EmployeeDocumentFilterModeSignature:
|
||||||
versionFilter = versionFilter.WithUserEmail(&identity.EmailAddress)
|
filterMode = coredata.EmployeeFilterModeSignature
|
||||||
case types.EmployeeDocumentFilterModeApproval:
|
case types.EmployeeDocumentFilterModeApproval:
|
||||||
versionFilter = versionFilter.WithApproverIdentityID(&identity.ID)
|
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)
|
versionsPage, err := prb.Documents.ListVersions(ctx, obj.ID, cursor, versionFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.ErrorCtx(ctx, "cannot list employee document versions", log.Error(err))
|
r.logger.ErrorCtx(ctx, "cannot list employee document versions", log.Error(err))
|
||||||
@@ -1647,6 +1653,7 @@ func (r *employeeDocumentResolver) Versions(ctx context.Context, obj *types.Empl
|
|||||||
for i, v := range versionsPage.Data {
|
for i, v := range versionsPage.Data {
|
||||||
employeeVersions[i] = &types.EmployeeDocumentVersion{
|
employeeVersions[i] = &types.EmployeeDocumentVersion{
|
||||||
ID: v.ID,
|
ID: v.ID,
|
||||||
|
DocumentID: obj.ID,
|
||||||
OrganizationID: v.OrganizationID,
|
OrganizationID: v.OrganizationID,
|
||||||
Major: v.Major,
|
Major: v.Major,
|
||||||
Minor: v.Minor,
|
Minor: v.Minor,
|
||||||
@@ -1665,7 +1672,7 @@ func (r *employeeDocumentResolver) Versions(ctx context.Context, obj *types.Empl
|
|||||||
|
|
||||||
// Signed is the resolver for the signed field.
|
// Signed is the resolver for the signed field.
|
||||||
func (r *employeeDocumentVersionResolver) Signed(ctx context.Context, obj *types.EmployeeDocumentVersion) (bool, error) {
|
func (r *employeeDocumentVersionResolver) Signed(ctx context.Context, obj *types.EmployeeDocumentVersion) (bool, error) {
|
||||||
if err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionGet); err != nil {
|
if err := r.authorize(ctx, obj.DocumentID, probo.ActionEmployeeDocumentGet); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1684,7 +1691,7 @@ func (r *employeeDocumentVersionResolver) Signed(ctx context.Context, obj *types
|
|||||||
|
|
||||||
// ApprovalDecision is the resolver for the approvalDecision field.
|
// ApprovalDecision is the resolver for the approvalDecision field.
|
||||||
func (r *employeeDocumentVersionResolver) ApprovalDecision(ctx context.Context, obj *types.EmployeeDocumentVersion) (*types.DocumentVersionApprovalDecision, error) {
|
func (r *employeeDocumentVersionResolver) ApprovalDecision(ctx context.Context, obj *types.EmployeeDocumentVersion) (*types.DocumentVersionApprovalDecision, error) {
|
||||||
if err := r.authorize(ctx, obj.ID, probo.ActionDocumentVersionApprovalList); err != nil {
|
if err := r.authorize(ctx, obj.DocumentID, probo.ActionEmployeeDocumentGet); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5708,9 +5715,9 @@ func (r *mutationResolver) ExportDocumentVersionPDF(ctx context.Context, input t
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExportSignableVersionDocumentPDF is the resolver for the exportSignableVersionDocumentPDF field.
|
// ExportEmployeeDocumentVersionPDF is the resolver for the exportEmployeeDocumentVersionPDF field.
|
||||||
func (r *mutationResolver) ExportSignableVersionDocumentPDF(ctx context.Context, input types.ExportSignableDocumentVersionPDFInput) (*types.ExportSignableDocumentVersionPDFPayload, error) {
|
func (r *mutationResolver) ExportEmployeeDocumentVersionPDF(ctx context.Context, input types.ExportEmployeeDocumentVersionPDFInput) (*types.ExportEmployeeDocumentVersionPDFPayload, error) {
|
||||||
if err := r.authorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionExportSignable); err != nil {
|
if err := r.authorize(ctx, input.DocumentVersionID, probo.ActionEmployeeDocumentVersionExportPDF); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5723,7 +5730,11 @@ func (r *mutationResolver) ExportSignableVersionDocumentPDF(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
identity := authn.IdentityFromContext(ctx)
|
identity := authn.IdentityFromContext(ctx)
|
||||||
documentFilter := coredata.NewDocumentFilter(nil).WithUserEmail(&identity.EmailAddress)
|
documentFilter := coredata.NewDocumentFilter(nil).WithEmployeeIdentityID(
|
||||||
|
&identity.ID,
|
||||||
|
coredata.EmployeeFilterModeSignature,
|
||||||
|
coredata.EmployeeFilterModeApproval,
|
||||||
|
)
|
||||||
|
|
||||||
_, err = prb.Documents.GetWithFilter(ctx, documentVersion.DocumentID, documentFilter)
|
_, err = prb.Documents.GetWithFilter(ctx, documentVersion.DocumentID, documentFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -5731,7 +5742,7 @@ func (r *mutationResolver) ExportSignableVersionDocumentPDF(ctx context.Context,
|
|||||||
return nil, gqlutils.NotFound(ctx, err)
|
return nil, gqlutils.NotFound(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r.logger.ErrorCtx(ctx, "cannot get signable document", log.Error(err))
|
r.logger.ErrorCtx(ctx, "cannot get employee document", log.Error(err))
|
||||||
return nil, gqlutils.Internal(ctx)
|
return nil, gqlutils.Internal(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5743,11 +5754,11 @@ func (r *mutationResolver) ExportSignableVersionDocumentPDF(ctx context.Context,
|
|||||||
|
|
||||||
pdf, err := prb.Documents.ExportPDF(ctx, input.DocumentVersionID, options)
|
pdf, err := prb.Documents.ExportPDF(ctx, input.DocumentVersionID, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.ErrorCtx(ctx, "cannot export signable document PDF", log.Error(err))
|
r.logger.ErrorCtx(ctx, "cannot export employee document PDF", log.Error(err))
|
||||||
return nil, gqlutils.Internal(ctx)
|
return nil, gqlutils.Internal(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &types.ExportSignableDocumentVersionPDFPayload{
|
return &types.ExportEmployeeDocumentVersionPDFPayload{
|
||||||
Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
|
Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -10201,7 +10212,7 @@ func (r *vendorServiceResolver) Permission(ctx context.Context, obj *types.Vendo
|
|||||||
|
|
||||||
// SignableDocuments is the resolver for the signableDocuments field.
|
// SignableDocuments is the resolver for the signableDocuments field.
|
||||||
func (r *viewerResolver) SignableDocuments(ctx context.Context, obj *types.Viewer, organizationID gid.GID, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) (*types.EmployeeDocumentConnection, error) {
|
func (r *viewerResolver) SignableDocuments(ctx context.Context, obj *types.Viewer, organizationID gid.GID, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) (*types.EmployeeDocumentConnection, error) {
|
||||||
if err := r.authorize(ctx, organizationID, probo.ActionDocumentList); err != nil {
|
if err := r.authorize(ctx, organizationID, probo.ActionEmployeeDocumentList); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10222,7 +10233,7 @@ func (r *viewerResolver) SignableDocuments(ctx context.Context, obj *types.Viewe
|
|||||||
|
|
||||||
identity := authn.IdentityFromContext(ctx)
|
identity := authn.IdentityFromContext(ctx)
|
||||||
|
|
||||||
documentFilter := coredata.NewDocumentFilter(nil).WithUserEmail(&identity.EmailAddress)
|
documentFilter := coredata.NewDocumentFilter(nil).WithEmployeeIdentityID(&identity.ID, coredata.EmployeeFilterModeSignature)
|
||||||
|
|
||||||
documentsPage, err := prb.Documents.ListByOrganizationID(ctx, organizationID, cursor, documentFilter)
|
documentsPage, err := prb.Documents.ListByOrganizationID(ctx, organizationID, cursor, documentFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -10249,7 +10260,7 @@ func (r *viewerResolver) SignableDocuments(ctx context.Context, obj *types.Viewe
|
|||||||
|
|
||||||
// SignableDocument is the resolver for the signableDocument field.
|
// SignableDocument is the resolver for the signableDocument field.
|
||||||
func (r *viewerResolver) SignableDocument(ctx context.Context, obj *types.Viewer, id gid.GID) (*types.EmployeeDocument, error) {
|
func (r *viewerResolver) SignableDocument(ctx context.Context, obj *types.Viewer, id gid.GID) (*types.EmployeeDocument, error) {
|
||||||
if err := r.authorize(ctx, id, probo.ActionDocumentGet); err != nil {
|
if err := r.authorize(ctx, id, probo.ActionEmployeeDocumentGet); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10257,7 +10268,7 @@ func (r *viewerResolver) SignableDocument(ctx context.Context, obj *types.Viewer
|
|||||||
|
|
||||||
identity := authn.IdentityFromContext(ctx)
|
identity := authn.IdentityFromContext(ctx)
|
||||||
|
|
||||||
documentFilter := coredata.NewDocumentFilter(nil).WithUserEmail(&identity.EmailAddress)
|
documentFilter := coredata.NewDocumentFilter(nil).WithEmployeeIdentityID(&identity.ID, coredata.EmployeeFilterModeSignature)
|
||||||
document, err := prb.Documents.GetWithFilter(ctx, id, documentFilter)
|
document, err := prb.Documents.GetWithFilter(ctx, id, documentFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, coredata.ErrResourceNotFound) {
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||||
@@ -10280,7 +10291,7 @@ func (r *viewerResolver) SignableDocument(ctx context.Context, obj *types.Viewer
|
|||||||
|
|
||||||
// ApprovableDocuments is the resolver for the approvableDocuments field.
|
// ApprovableDocuments is the resolver for the approvableDocuments field.
|
||||||
func (r *viewerResolver) ApprovableDocuments(ctx context.Context, obj *types.Viewer, organizationID gid.GID, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) (*types.EmployeeDocumentConnection, error) {
|
func (r *viewerResolver) ApprovableDocuments(ctx context.Context, obj *types.Viewer, organizationID gid.GID, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) (*types.EmployeeDocumentConnection, error) {
|
||||||
if err := r.authorize(ctx, organizationID, probo.ActionDocumentVersionApprovalList); err != nil {
|
if err := r.authorize(ctx, organizationID, probo.ActionEmployeeDocumentList); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10301,7 +10312,7 @@ func (r *viewerResolver) ApprovableDocuments(ctx context.Context, obj *types.Vie
|
|||||||
|
|
||||||
identity := authn.IdentityFromContext(ctx)
|
identity := authn.IdentityFromContext(ctx)
|
||||||
|
|
||||||
documentFilter := coredata.NewDocumentFilter(nil).WithApproverIdentityID(&identity.ID)
|
documentFilter := coredata.NewDocumentFilter(nil).WithEmployeeIdentityID(&identity.ID, coredata.EmployeeFilterModeApproval)
|
||||||
|
|
||||||
documentsPage, err := prb.Documents.ListByOrganizationID(ctx, organizationID, cursor, documentFilter)
|
documentsPage, err := prb.Documents.ListByOrganizationID(ctx, organizationID, cursor, documentFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -10328,7 +10339,7 @@ func (r *viewerResolver) ApprovableDocuments(ctx context.Context, obj *types.Vie
|
|||||||
|
|
||||||
// ApprovableDocument is the resolver for the approvableDocument field.
|
// ApprovableDocument is the resolver for the approvableDocument field.
|
||||||
func (r *viewerResolver) ApprovableDocument(ctx context.Context, obj *types.Viewer, id gid.GID) (*types.EmployeeDocument, error) {
|
func (r *viewerResolver) ApprovableDocument(ctx context.Context, obj *types.Viewer, id gid.GID) (*types.EmployeeDocument, error) {
|
||||||
if err := r.authorize(ctx, id, probo.ActionDocumentGet); err != nil {
|
if err := r.authorize(ctx, id, probo.ActionEmployeeDocumentGet); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10336,7 +10347,7 @@ func (r *viewerResolver) ApprovableDocument(ctx context.Context, obj *types.View
|
|||||||
|
|
||||||
identity := authn.IdentityFromContext(ctx)
|
identity := authn.IdentityFromContext(ctx)
|
||||||
|
|
||||||
documentFilter := coredata.NewDocumentFilter(nil).WithApproverIdentityID(&identity.ID)
|
documentFilter := coredata.NewDocumentFilter(nil).WithEmployeeIdentityID(&identity.ID, coredata.EmployeeFilterModeApproval)
|
||||||
document, err := prb.Documents.GetWithFilter(ctx, id, documentFilter)
|
document, err := prb.Documents.GetWithFilter(ctx, id, documentFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, coredata.ErrResourceNotFound) {
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||||
|
|||||||
Reference in New Issue
Block a user