fix: add document rejection webhook

Adds the document rejection webhook since it was missing.

Additionally, normalises and standardises the webhook body.
This commit is contained in:
Mythie
2024-12-04 14:35:20 +11:00
parent f3da11b3e7
commit 8ec69388a5
17 changed files with 436 additions and 86 deletions

View File

@ -21,6 +21,7 @@ import { insertFieldInPDF } from '../../../server-only/pdf/insert-field-in-pdf';
import { normalizeSignatureAppearances } from '../../../server-only/pdf/normalize-signature-appearances';
import { triggerWebhook } from '../../../server-only/webhooks/trigger/trigger-webhook';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../../types/document-audit-logs';
import { ZWebhookDocumentSchema } from '../../../types/webhook-payload';
import { ZRequestMetadataSchema } from '../../../universal/extract-request-metadata';
import { getFile } from '../../../universal/upload/get-file';
import { putPdfFile } from '../../../universal/upload/put-file';
@ -249,13 +250,14 @@ export const SEAL_DOCUMENT_JOB_DEFINITION = {
},
include: {
documentData: true,
documentMeta: true,
Recipient: true,
},
});
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_COMPLETED,
data: updatedDocument,
data: ZWebhookDocumentSchema.parse(updatedDocument),
userId: updatedDocument.userId,
teamId: updatedDocument.teamId ?? undefined,
});

View File

@ -13,6 +13,7 @@ import { WebhookTriggerEvents } from '@documenso/prisma/client';
import { jobs } from '../../jobs/client';
import type { TRecipientActionAuth } from '../../types/document-auth';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import { getIsRecipientsTurnToSign } from '../recipient/get-is-recipient-turn';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
import { sendPendingEmail } from './send-pending-email';
@ -203,11 +204,19 @@ export const completeDocumentWithToken = async ({
});
}
const updatedDocument = await getDocument({ token, documentId });
const updatedDocument = await prisma.document.findFirstOrThrow({
where: {
id: document.id,
},
include: {
documentMeta: true,
Recipient: true,
},
});
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_SIGNED,
data: updatedDocument,
data: ZWebhookDocumentSchema.parse(updatedDocument),
userId: updatedDocument.userId,
teamId: updatedDocument.teamId ?? undefined,
});

View File

@ -9,6 +9,7 @@ import { DocumentSource, DocumentVisibility, WebhookTriggerEvents } from '@docum
import type { Team, TeamGlobalSettings } from '@documenso/prisma/client';
import { TeamMemberRole } from '@documenso/prisma/client';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
export type CreateDocumentOptions = {
@ -135,13 +136,27 @@ export const createDocument = async ({
}),
});
const createdDocument = await tx.document.findFirst({
where: {
id: document.id,
},
include: {
documentMeta: true,
Recipient: true,
},
});
if (!createdDocument) {
throw new Error('Document not found');
}
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_CREATED,
data: document,
data: ZWebhookDocumentSchema.parse(createdDocument),
userId,
teamId,
});
return document;
return createdDocument;
});
};

View File

@ -3,10 +3,13 @@ import { TRPCError } from '@trpc/server';
import { jobs } from '@documenso/lib/jobs/client';
import { prisma } from '@documenso/prisma';
import { WebhookTriggerEvents } from '@documenso/prisma/client';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
export type RejectDocumentWithTokenOptions = {
token: string;
@ -31,6 +34,8 @@ export async function rejectDocumentWithToken({
Document: {
include: {
User: true,
Recipient: true,
documentMeta: true,
},
},
},
@ -45,8 +50,6 @@ export async function rejectDocumentWithToken({
});
}
// Add the audit log entry before updating the recipient
// Update the recipient status to rejected
const [updatedRecipient] = await prisma.$transaction([
prisma.recipient.update({
@ -88,5 +91,28 @@ export async function rejectDocumentWithToken({
},
});
// Get the updated document with all recipients
const updatedDocument = await prisma.document.findFirst({
where: {
id: document.id,
},
include: {
Recipient: true,
documentMeta: true,
},
});
if (!updatedDocument) {
throw new Error('Document not found after update');
}
// Trigger webhook for document rejection
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_REJECTED,
data: ZWebhookDocumentSchema.parse(updatedDocument),
userId: document.userId,
teamId: document.teamId ?? undefined,
});
return updatedRecipient;
}

View File

@ -10,6 +10,7 @@ import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/
import { WebhookTriggerEvents } from '@documenso/prisma/client';
import { signPdf } from '@documenso/signing';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { getFile } from '../../universal/upload/get-file';
import { putPdfFile } from '../../universal/upload/put-file';
@ -199,13 +200,14 @@ export const sealDocument = async ({
},
include: {
documentData: true,
documentMeta: true,
Recipient: true,
},
});
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_COMPLETED,
data: updatedDocument,
data: ZWebhookDocumentSchema.parse(updatedDocument),
userId: document.userId,
teamId: document.teamId ?? undefined,
});

View File

@ -14,6 +14,7 @@ import { WebhookTriggerEvents } from '@documenso/prisma/client';
import { jobs } from '../../jobs/client';
import { extractDerivedDocumentEmailSettings } from '../../types/document-email';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import { getFile } from '../../universal/upload/get-file';
import { insertFormValuesInPdf } from '../pdf/insert-form-values-in-pdf';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
@ -236,6 +237,7 @@ export const sendDocument = async ({
status: DocumentStatus.PENDING,
},
include: {
documentMeta: true,
Recipient: true,
},
});
@ -243,7 +245,7 @@ export const sendDocument = async ({
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_SENT,
data: updatedDocument,
data: ZWebhookDocumentSchema.parse(updatedDocument),
userId,
teamId,
});

View File

@ -6,8 +6,8 @@ import { ReadStatus } from '@documenso/prisma/client';
import { WebhookTriggerEvents } from '@documenso/prisma/client';
import type { TDocumentAccessAuthTypes } from '../../types/document-auth';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
import { getDocumentAndRecipientByToken } from './get-document-by-token';
export type ViewedDocumentOptions = {
token: string;
@ -63,11 +63,23 @@ export const viewedDocument = async ({
});
});
const document = await getDocumentAndRecipientByToken({ token, requireAccessAuth: false });
const document = await prisma.document.findFirst({
where: {
id: documentId,
},
include: {
documentMeta: true,
Recipient: true,
},
});
if (!document) {
throw new Error('Document not found');
}
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_OPENED,
data: document,
data: ZWebhookDocumentSchema.parse(document),
userId: document.userId,
teamId: document.teamId ?? undefined,
});

View File

@ -31,6 +31,7 @@ import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import type { TRecipientActionAuthTypes } from '../../types/document-auth';
import { DocumentAccessAuth, ZRecipientAuthOptionsSchema } from '../../types/document-auth';
import { ZFieldMetaSchema } from '../../types/field-meta';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import type { RequestMetadata } from '../../universal/extract-request-metadata';
import type { CreateDocumentAuditLogDataResponse } from '../../utils/document-audit-logs';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
@ -591,7 +592,7 @@ export const createDocumentFromDirectTemplate = async ({
requestMetadata,
});
const updatedDocument = await prisma.document.findFirstOrThrow({
const createdDocument = await prisma.document.findFirstOrThrow({
where: {
id: documentId,
},
@ -603,9 +604,9 @@ export const createDocumentFromDirectTemplate = async ({
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_SIGNED,
data: updatedDocument,
userId: updatedDocument.userId,
teamId: updatedDocument.teamId ?? undefined,
data: ZWebhookDocumentSchema.parse(createdDocument),
userId: template.userId,
teamId: template.teamId ?? undefined,
});
} catch (err) {
console.error('[CREATE_DOCUMENT_FROM_DIRECT_TEMPLATE]:', err);

View File

@ -18,6 +18,7 @@ import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import { ZRecipientAuthOptionsSchema } from '../../types/document-auth';
import type { TDocumentEmailSettings } from '../../types/document-email';
import { ZFieldMetaSchema } from '../../types/field-meta';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
import {
@ -291,9 +292,23 @@ export const createDocumentFromTemplate = async ({
}),
});
const createdDocument = await tx.document.findFirst({
where: {
id: document.id,
},
include: {
documentMeta: true,
Recipient: true,
},
});
if (!createdDocument) {
throw new Error('Document not found');
}
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_CREATED,
data: document,
data: ZWebhookDocumentSchema.parse(createdDocument),
userId,
teamId,
});

View File

@ -724,7 +724,7 @@ msgid "Document created"
msgstr "Dokument erstellt"
#: packages/email/templates/document-created-from-direct-template.tsx:32
#: packages/lib/server-only/template/create-document-from-direct-template.ts:573
#: packages/lib/server-only/template/create-document-from-direct-template.ts:574
msgid "Document created from direct template"
msgstr "Dokument erstellt aus direkter Vorlage"

View File

@ -719,7 +719,7 @@ msgid "Document created"
msgstr "Document created"
#: packages/email/templates/document-created-from-direct-template.tsx:32
#: packages/lib/server-only/template/create-document-from-direct-template.ts:573
#: packages/lib/server-only/template/create-document-from-direct-template.ts:574
msgid "Document created from direct template"
msgstr "Document created from direct template"

View File

@ -724,7 +724,7 @@ msgid "Document created"
msgstr "Documento creado"
#: packages/email/templates/document-created-from-direct-template.tsx:32
#: packages/lib/server-only/template/create-document-from-direct-template.ts:573
#: packages/lib/server-only/template/create-document-from-direct-template.ts:574
msgid "Document created from direct template"
msgstr "Documento creado a partir de plantilla directa"

View File

@ -724,7 +724,7 @@ msgid "Document created"
msgstr "Document créé"
#: packages/email/templates/document-created-from-direct-template.tsx:32
#: packages/lib/server-only/template/create-document-from-direct-template.ts:573
#: packages/lib/server-only/template/create-document-from-direct-template.ts:574
msgid "Document created from direct template"
msgstr "Document créé à partir d'un modèle direct"

View File

@ -0,0 +1,80 @@
import { z } from 'zod';
import {
DocumentDistributionMethod,
DocumentSigningOrder,
DocumentSource,
DocumentStatus,
DocumentVisibility,
ReadStatus,
RecipientRole,
SendStatus,
SigningStatus,
} from '@documenso/prisma/client';
/**
* Schema for recipient data in webhook payloads.
*/
export const ZWebhookRecipientSchema = z.object({
id: z.number(),
documentId: z.number().nullable(),
templateId: z.number().nullable(),
email: z.string(),
name: z.string(),
token: z.string(),
documentDeletedAt: z.date().nullable(),
expired: z.date().nullable(),
signedAt: z.date().nullable(),
authOptions: z.any().nullable(),
signingOrder: z.number().nullable(),
rejectionReason: z.string().nullable(),
role: z.nativeEnum(RecipientRole),
readStatus: z.nativeEnum(ReadStatus),
signingStatus: z.nativeEnum(SigningStatus),
sendStatus: z.nativeEnum(SendStatus),
});
/**
* Schema for document meta in webhook payloads.
*/
export const ZWebhookDocumentMetaSchema = z.object({
id: z.string(),
subject: z.string().nullable(),
message: z.string().nullable(),
timezone: z.string(),
password: z.string().nullable(),
dateFormat: z.string(),
redirectUrl: z.string().nullable(),
signingOrder: z.nativeEnum(DocumentSigningOrder),
typedSignatureEnabled: z.boolean(),
language: z.string(),
distributionMethod: z.nativeEnum(DocumentDistributionMethod),
emailSettings: z.any().nullable(),
});
/**
* Schema for document data in webhook payloads.
*/
export const ZWebhookDocumentSchema = z.object({
id: z.number(),
externalId: z.string().nullable(),
userId: z.number(),
authOptions: z.any().nullable(),
formValues: z.any().nullable(),
visibility: z.nativeEnum(DocumentVisibility),
title: z.string(),
status: z.nativeEnum(DocumentStatus),
documentDataId: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
completedAt: z.date().nullable(),
deletedAt: z.date().nullable(),
teamId: z.number().nullable(),
templateId: z.number().nullable(),
source: z.nativeEnum(DocumentSource),
documentMeta: ZWebhookDocumentMetaSchema.nullable(),
Recipient: z.array(ZWebhookRecipientSchema),
});
export type TWebhookRecipient = z.infer<typeof ZWebhookRecipientSchema>;
export type TWebhookDocument = z.infer<typeof ZWebhookDocumentSchema>;