mirror of
https://github.com/documenso/documenso.git
synced 2025-11-15 01:01:49 +10:00
feat: extend use envelope response (#2192)
This commit is contained in:
@ -28,9 +28,13 @@ import {
|
||||
seedPendingDocument,
|
||||
} from '@documenso/prisma/seed/documents';
|
||||
import { seedBlankFolder } from '@documenso/prisma/seed/folders';
|
||||
import { seedBlankTemplate } from '@documenso/prisma/seed/templates';
|
||||
import { seedBlankTemplate, seedTemplate } from '@documenso/prisma/seed/templates';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
import type { TCreateEnvelopeItemsPayload } from '@documenso/trpc/server/envelope-router/create-envelope-items.types';
|
||||
import type {
|
||||
TUseEnvelopePayload,
|
||||
TUseEnvelopeResponse,
|
||||
} from '@documenso/trpc/server/envelope-router/use-envelope.types';
|
||||
|
||||
const WEBAPP_BASE_URL = NEXT_PUBLIC_WEBAPP_URL();
|
||||
|
||||
@ -3074,6 +3078,82 @@ test.describe('Document API V2', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Envelope use endpoint', () => {
|
||||
test('should block unauthorized access to envelope use endpoint', async ({ request }) => {
|
||||
const doc = await seedTemplate({
|
||||
title: 'Team template 1',
|
||||
userId: userA.id,
|
||||
teamId: teamA.id,
|
||||
internalVersion: 2,
|
||||
});
|
||||
|
||||
const payload: TUseEnvelopePayload = {
|
||||
envelopeId: doc.id,
|
||||
};
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('payload', JSON.stringify(payload));
|
||||
|
||||
const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/use`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
multipart: formData,
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeFalsy();
|
||||
expect(res.status()).toBe(404);
|
||||
});
|
||||
|
||||
test('should allow authorized access to envelope use endpoint', async ({ page, request }) => {
|
||||
const doc = await seedTemplate({
|
||||
title: 'Team template 1',
|
||||
userId: userA.id,
|
||||
teamId: teamA.id,
|
||||
internalVersion: 2,
|
||||
});
|
||||
|
||||
const payload: TUseEnvelopePayload = {
|
||||
envelopeId: doc.id,
|
||||
distributeDocument: true,
|
||||
recipients: [
|
||||
{
|
||||
id: doc.recipients[0].id,
|
||||
email: doc.recipients[0].email,
|
||||
name: 'New Name',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('payload', JSON.stringify(payload));
|
||||
|
||||
const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/use`, {
|
||||
headers: { Authorization: `Bearer ${tokenA}` },
|
||||
multipart: formData,
|
||||
});
|
||||
|
||||
expect(res.ok()).toBeTruthy();
|
||||
expect(res.status()).toBe(200);
|
||||
|
||||
const data: TUseEnvelopeResponse = await res.json();
|
||||
|
||||
const createdEnvelope = await prisma.envelope.findFirst({
|
||||
where: {
|
||||
id: data.id,
|
||||
},
|
||||
include: {
|
||||
recipients: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(createdEnvelope).toBeDefined();
|
||||
expect(createdEnvelope?.recipients.length).toBe(1);
|
||||
expect(createdEnvelope?.recipients[0].email).toBe(doc.recipients[0].email);
|
||||
expect(createdEnvelope?.recipients[0].name).toBe('New Name');
|
||||
expect(createdEnvelope?.recipients[0].token).toBe(data.recipients[0].token);
|
||||
expect(createdEnvelope?.recipients[0].token).not.toBe(doc.recipients[0].token);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Envelope distribute endpoint', () => {
|
||||
test('should block unauthorized access to envelope distribute endpoint', async ({
|
||||
request,
|
||||
|
||||
@ -44,7 +44,7 @@ export const resendDocument = async ({
|
||||
recipients,
|
||||
teamId,
|
||||
requestMetadata,
|
||||
}: ResendDocumentOptions): Promise<void> => {
|
||||
}: ResendDocumentOptions) => {
|
||||
const user = await prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
id: userId,
|
||||
@ -103,7 +103,7 @@ export const resendDocument = async ({
|
||||
).recipientSigningRequest;
|
||||
|
||||
if (!isRecipientSigningRequestEmailEnabled) {
|
||||
return;
|
||||
return envelope;
|
||||
}
|
||||
|
||||
const { branding, emailLanguage, organisationType, senderEmail, replyToEmail } =
|
||||
@ -230,4 +230,6 @@ export const resendDocument = async ({
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
return envelope;
|
||||
};
|
||||
|
||||
@ -110,7 +110,7 @@ export const seedTemplate = async (options: SeedTemplateOptions) => {
|
||||
data: {
|
||||
id: prefixedId('envelope'),
|
||||
secondaryId: templateId.formattedTemplateId,
|
||||
internalVersion: 1,
|
||||
internalVersion: options.internalVersion ?? 1,
|
||||
type: EnvelopeType.TEMPLATE,
|
||||
title,
|
||||
envelopeItems: {
|
||||
@ -143,6 +143,7 @@ export const seedTemplate = async (options: SeedTemplateOptions) => {
|
||||
documentData: true,
|
||||
},
|
||||
},
|
||||
recipients: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { updateDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta';
|
||||
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
|
||||
import { formatSigningLink } from '@documenso/lib/utils/recipients';
|
||||
|
||||
import { ZGenericSuccessResponse } from '../schema';
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
ZDistributeEnvelopeRequestSchema,
|
||||
@ -45,7 +45,7 @@ export const distributeEnvelopeRoute = authenticatedProcedure
|
||||
});
|
||||
}
|
||||
|
||||
await sendDocument({
|
||||
const envelope = await sendDocument({
|
||||
userId: ctx.user.id,
|
||||
id: {
|
||||
type: 'envelopeId',
|
||||
@ -55,5 +55,17 @@ export const distributeEnvelopeRoute = authenticatedProcedure
|
||||
requestMetadata: ctx.metadata,
|
||||
});
|
||||
|
||||
return ZGenericSuccessResponse;
|
||||
return {
|
||||
success: true,
|
||||
id: envelope.id,
|
||||
recipients: envelope.recipients.map((recipient) => ({
|
||||
id: recipient.id,
|
||||
name: recipient.name,
|
||||
email: recipient.email,
|
||||
token: recipient.token,
|
||||
role: recipient.role,
|
||||
signingOrder: recipient.signingOrder,
|
||||
signingUrl: formatSigningLink(recipient.token),
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
@ -4,6 +4,7 @@ import { ZDocumentMetaUpdateSchema } from '@documenso/lib/types/document-meta';
|
||||
|
||||
import { ZSuccessResponseSchema } from '../schema';
|
||||
import type { TrpcRouteMeta } from '../trpc';
|
||||
import { ZRecipientWithSigningUrlSchema } from './schema';
|
||||
|
||||
export const distributeEnvelopeMeta: TrpcRouteMeta = {
|
||||
openapi: {
|
||||
@ -31,7 +32,10 @@ export const ZDistributeEnvelopeRequestSchema = z.object({
|
||||
}).optional(),
|
||||
});
|
||||
|
||||
export const ZDistributeEnvelopeResponseSchema = ZSuccessResponseSchema;
|
||||
export const ZDistributeEnvelopeResponseSchema = ZSuccessResponseSchema.extend({
|
||||
id: z.string().describe('The ID of the envelope that was sent.'),
|
||||
recipients: ZRecipientWithSigningUrlSchema.array(),
|
||||
});
|
||||
|
||||
export type TDistributeEnvelopeRequest = z.infer<typeof ZDistributeEnvelopeRequestSchema>;
|
||||
export type TDistributeEnvelopeResponse = z.infer<typeof ZDistributeEnvelopeResponseSchema>;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { resendDocument } from '@documenso/lib/server-only/document/resend-document';
|
||||
import { formatSigningLink } from '@documenso/lib/utils/recipients';
|
||||
|
||||
import { ZGenericSuccessResponse } from '../schema';
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
ZRedistributeEnvelopeRequestSchema,
|
||||
@ -23,7 +23,7 @@ export const redistributeEnvelopeRoute = authenticatedProcedure
|
||||
},
|
||||
});
|
||||
|
||||
await resendDocument({
|
||||
const envelope = await resendDocument({
|
||||
userId: ctx.user.id,
|
||||
teamId,
|
||||
id: {
|
||||
@ -34,5 +34,17 @@ export const redistributeEnvelopeRoute = authenticatedProcedure
|
||||
requestMetadata: ctx.metadata,
|
||||
});
|
||||
|
||||
return ZGenericSuccessResponse;
|
||||
return {
|
||||
success: true,
|
||||
id: envelope.id,
|
||||
recipients: envelope.recipients.map((recipient) => ({
|
||||
id: recipient.id,
|
||||
name: recipient.name,
|
||||
email: recipient.email,
|
||||
token: recipient.token,
|
||||
role: recipient.role,
|
||||
signingOrder: recipient.signingOrder,
|
||||
signingUrl: formatSigningLink(recipient.token),
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
@ -2,6 +2,7 @@ import { z } from 'zod';
|
||||
|
||||
import { ZSuccessResponseSchema } from '../schema';
|
||||
import type { TrpcRouteMeta } from '../trpc';
|
||||
import { ZRecipientWithSigningUrlSchema } from './schema';
|
||||
|
||||
export const redistributeEnvelopeMeta: TrpcRouteMeta = {
|
||||
openapi: {
|
||||
@ -22,7 +23,10 @@ export const ZRedistributeEnvelopeRequestSchema = z.object({
|
||||
.describe('The IDs of the recipients to redistribute the envelope to.'),
|
||||
});
|
||||
|
||||
export const ZRedistributeEnvelopeResponseSchema = ZSuccessResponseSchema;
|
||||
export const ZRedistributeEnvelopeResponseSchema = ZSuccessResponseSchema.extend({
|
||||
id: z.string().describe('The ID of the envelope that was redistributed.'),
|
||||
recipients: ZRecipientWithSigningUrlSchema.array(),
|
||||
});
|
||||
|
||||
export type TRedistributeEnvelopeRequest = z.infer<typeof ZRedistributeEnvelopeRequestSchema>;
|
||||
export type TRedistributeEnvelopeResponse = z.infer<typeof ZRedistributeEnvelopeResponseSchema>;
|
||||
|
||||
16
packages/trpc/server/envelope-router/schema.ts
Normal file
16
packages/trpc/server/envelope-router/schema.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import RecipientSchema from '@documenso/prisma/generated/zod/modelSchema/RecipientSchema';
|
||||
|
||||
// Common schemas between envelope routes.
|
||||
|
||||
export const ZRecipientWithSigningUrlSchema = RecipientSchema.pick({
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
token: true,
|
||||
role: true,
|
||||
signingOrder: true,
|
||||
}).extend({
|
||||
signingUrl: z.string().describe('The URL which the recipient uses to sign the document.'),
|
||||
});
|
||||
@ -6,6 +6,7 @@ import { sendDocument } from '@documenso/lib/server-only/document/send-document'
|
||||
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
|
||||
import { createDocumentFromTemplate } from '@documenso/lib/server-only/template/create-document-from-template';
|
||||
import { putNormalizedPdfFileServerSide } from '@documenso/lib/universal/upload/put-file.server';
|
||||
import { formatSigningLink } from '@documenso/lib/utils/recipients';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
@ -26,7 +27,7 @@ export const useEnvelopeRoute = authenticatedProcedure
|
||||
const {
|
||||
envelopeId,
|
||||
externalId,
|
||||
recipients,
|
||||
recipients = [],
|
||||
distributeDocument,
|
||||
customDocumentData = [],
|
||||
folderId,
|
||||
@ -166,5 +167,14 @@ export const useEnvelopeRoute = authenticatedProcedure
|
||||
|
||||
return {
|
||||
id: createdEnvelope.id,
|
||||
recipients: createdEnvelope.recipients.map((recipient) => ({
|
||||
id: recipient.id,
|
||||
name: recipient.name,
|
||||
email: recipient.email,
|
||||
token: recipient.token,
|
||||
role: recipient.role,
|
||||
signingOrder: recipient.signingOrder,
|
||||
signingUrl: formatSigningLink(recipient.token),
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
@ -19,6 +19,7 @@ import { ZFieldMetaPrefillFieldsSchema } from '@documenso/lib/types/field-meta';
|
||||
|
||||
import { zodFormData } from '../../utils/zod-form-data';
|
||||
import type { TrpcRouteMeta } from '../trpc';
|
||||
import { ZRecipientWithSigningUrlSchema } from './schema';
|
||||
|
||||
export const useEnvelopeMeta: TrpcRouteMeta = {
|
||||
openapi: {
|
||||
@ -44,7 +45,8 @@ export const ZUseEnvelopePayloadSchema = z.object({
|
||||
signingOrder: z.number().optional(),
|
||||
}),
|
||||
)
|
||||
.describe('The information of the recipients to create the document with.'),
|
||||
.describe('The information of the recipients to create the document with.')
|
||||
.optional(),
|
||||
distributeDocument: z
|
||||
.boolean()
|
||||
.describe('Whether to create the document as pending and distribute it to recipients.')
|
||||
@ -114,6 +116,7 @@ export const ZUseEnvelopeRequestSchema = zodFormData({
|
||||
|
||||
export const ZUseEnvelopeResponseSchema = z.object({
|
||||
id: z.string().describe('The ID of the created envelope.'),
|
||||
recipients: ZRecipientWithSigningUrlSchema.array(),
|
||||
});
|
||||
|
||||
export type TUseEnvelopePayload = z.infer<typeof ZUseEnvelopePayloadSchema>;
|
||||
|
||||
Reference in New Issue
Block a user