mirror of
https://github.com/documenso/documenso.git
synced 2025-11-26 14:34:05 +10:00
Compare commits
2 Commits
v2.1.0
...
feat/find-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5b7522ea0 | ||
|
|
4d29a66ba1 |
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -17,5 +17,6 @@
|
|||||||
},
|
},
|
||||||
"[typescriptreact]": {
|
"[typescriptreact]": {
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
}
|
},
|
||||||
|
"prisma.pinToPrisma6": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { expect, test } from '@playwright/test';
|
import { type APIRequestContext, expect, test } from '@playwright/test';
|
||||||
import type { Team, User } from '@prisma/client';
|
import type { Team, User } from '@prisma/client';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
@@ -24,6 +24,7 @@ import type {
|
|||||||
TCreateEnvelopeResponse,
|
TCreateEnvelopeResponse,
|
||||||
} from '@documenso/trpc/server/envelope-router/create-envelope.types';
|
} from '@documenso/trpc/server/envelope-router/create-envelope.types';
|
||||||
import type { TCreateEnvelopeRecipientsRequest } from '@documenso/trpc/server/envelope-router/envelope-recipients/create-envelope-recipients.types';
|
import type { TCreateEnvelopeRecipientsRequest } from '@documenso/trpc/server/envelope-router/envelope-recipients/create-envelope-recipients.types';
|
||||||
|
import type { TFindEnvelopesResponse } from '@documenso/trpc/server/envelope-router/find-envelopes.types';
|
||||||
import type { TGetEnvelopeResponse } from '@documenso/trpc/server/envelope-router/get-envelope.types';
|
import type { TGetEnvelopeResponse } from '@documenso/trpc/server/envelope-router/get-envelope.types';
|
||||||
import type { TUpdateEnvelopeRequest } from '@documenso/trpc/server/envelope-router/update-envelope.types';
|
import type { TUpdateEnvelopeRequest } from '@documenso/trpc/server/envelope-router/update-envelope.types';
|
||||||
|
|
||||||
@@ -164,6 +165,9 @@ test.describe('API V2 Envelopes', () => {
|
|||||||
positionY: 0,
|
positionY: 0,
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
|
fieldMeta: {
|
||||||
|
type: 'signature',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: FieldType.SIGNATURE,
|
type: FieldType.SIGNATURE,
|
||||||
@@ -173,6 +177,9 @@ test.describe('API V2 Envelopes', () => {
|
|||||||
positionY: 0,
|
positionY: 0,
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
|
fieldMeta: {
|
||||||
|
type: 'signature',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -557,4 +564,198 @@ test.describe('API V2 Envelopes', () => {
|
|||||||
userEmail: userA.email,
|
userEmail: userA.email,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.describe('Envelope find endpoint', () => {
|
||||||
|
const createEnvelope = async (
|
||||||
|
request: APIRequestContext,
|
||||||
|
token: string,
|
||||||
|
payload: TCreateEnvelopePayload,
|
||||||
|
) => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('payload', JSON.stringify(payload));
|
||||||
|
|
||||||
|
const pdfData = fs.readFileSync(
|
||||||
|
path.join(__dirname, '../../../../../assets/field-font-alignment.pdf'),
|
||||||
|
);
|
||||||
|
formData.append('files', new File([pdfData], 'test.pdf', { type: 'application/pdf' }));
|
||||||
|
|
||||||
|
const res = await request.post(`${baseUrl}/envelope/create`, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
multipart: formData,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.ok()).toBeTruthy();
|
||||||
|
return (await res.json()) as TCreateEnvelopeResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
test('should find envelopes with pagination', async ({ request }) => {
|
||||||
|
// Create 3 envelopes
|
||||||
|
await createEnvelope(request, tokenA, {
|
||||||
|
type: EnvelopeType.DOCUMENT,
|
||||||
|
title: 'Document 1',
|
||||||
|
});
|
||||||
|
await createEnvelope(request, tokenA, {
|
||||||
|
type: EnvelopeType.DOCUMENT,
|
||||||
|
title: 'Document 2',
|
||||||
|
});
|
||||||
|
await createEnvelope(request, tokenA, {
|
||||||
|
type: EnvelopeType.TEMPLATE,
|
||||||
|
title: 'Template 1',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find all envelopes
|
||||||
|
const res = await request.get(`${baseUrl}/envelope`, {
|
||||||
|
headers: { Authorization: `Bearer ${tokenA}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.ok()).toBeTruthy();
|
||||||
|
expect(res.status()).toBe(200);
|
||||||
|
|
||||||
|
const response = (await res.json()) as TFindEnvelopesResponse;
|
||||||
|
|
||||||
|
expect(response.data.length).toBe(3);
|
||||||
|
expect(response.count).toBe(3);
|
||||||
|
expect(response.currentPage).toBe(1);
|
||||||
|
expect(response.totalPages).toBe(1);
|
||||||
|
|
||||||
|
// Test pagination
|
||||||
|
const paginatedRes = await request.get(`${baseUrl}/envelope?perPage=2&page=1`, {
|
||||||
|
headers: { Authorization: `Bearer ${tokenA}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(paginatedRes.ok()).toBeTruthy();
|
||||||
|
const paginatedResponse = (await paginatedRes.json()) as TFindEnvelopesResponse;
|
||||||
|
|
||||||
|
expect(paginatedResponse.data.length).toBe(2);
|
||||||
|
expect(paginatedResponse.count).toBe(3);
|
||||||
|
expect(paginatedResponse.totalPages).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should filter envelopes by type', async ({ request }) => {
|
||||||
|
await createEnvelope(request, tokenA, {
|
||||||
|
type: EnvelopeType.DOCUMENT,
|
||||||
|
title: 'Document Only',
|
||||||
|
});
|
||||||
|
await createEnvelope(request, tokenA, {
|
||||||
|
type: EnvelopeType.TEMPLATE,
|
||||||
|
title: 'Template Only',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Filter by DOCUMENT type
|
||||||
|
const documentRes = await request.get(`${baseUrl}/envelope?type=DOCUMENT`, {
|
||||||
|
headers: { Authorization: `Bearer ${tokenA}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(documentRes.ok()).toBeTruthy();
|
||||||
|
const documentResponse = (await documentRes.json()) as TFindEnvelopesResponse;
|
||||||
|
|
||||||
|
expect(documentResponse.data.every((e) => e.type === EnvelopeType.DOCUMENT)).toBe(true);
|
||||||
|
|
||||||
|
// Filter by TEMPLATE type
|
||||||
|
const templateRes = await request.get(`${baseUrl}/envelope?type=TEMPLATE`, {
|
||||||
|
headers: { Authorization: `Bearer ${tokenA}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(templateRes.ok()).toBeTruthy();
|
||||||
|
const templateResponse = (await templateRes.json()) as TFindEnvelopesResponse;
|
||||||
|
|
||||||
|
expect(templateResponse.data.every((e) => e.type === EnvelopeType.TEMPLATE)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should filter envelopes by status', async ({ request }) => {
|
||||||
|
await createEnvelope(request, tokenA, {
|
||||||
|
type: EnvelopeType.DOCUMENT,
|
||||||
|
title: 'Draft Document',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Filter by DRAFT status (default for new envelopes)
|
||||||
|
const res = await request.get(`${baseUrl}/envelope?status=DRAFT`, {
|
||||||
|
headers: { Authorization: `Bearer ${tokenA}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.ok()).toBeTruthy();
|
||||||
|
const response = (await res.json()) as TFindEnvelopesResponse;
|
||||||
|
|
||||||
|
expect(response.data.every((e) => e.status === DocumentStatus.DRAFT)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should search envelopes by query', async ({ request }) => {
|
||||||
|
await createEnvelope(request, tokenA, {
|
||||||
|
type: EnvelopeType.DOCUMENT,
|
||||||
|
title: 'Unique Searchable Title',
|
||||||
|
});
|
||||||
|
await createEnvelope(request, tokenA, {
|
||||||
|
type: EnvelopeType.DOCUMENT,
|
||||||
|
title: 'Another Document',
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await request.get(`${baseUrl}/envelope?query=Unique%20Searchable`, {
|
||||||
|
headers: { Authorization: `Bearer ${tokenA}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.ok()).toBeTruthy();
|
||||||
|
const response = (await res.json()) as TFindEnvelopesResponse;
|
||||||
|
|
||||||
|
expect(response.data.length).toBe(1);
|
||||||
|
expect(response.data[0].title).toBe('Unique Searchable Title');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not return envelopes from other users', async ({ request }) => {
|
||||||
|
// Create envelope for userA
|
||||||
|
await createEnvelope(request, tokenA, {
|
||||||
|
type: EnvelopeType.DOCUMENT,
|
||||||
|
title: 'UserA Document',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create envelope for userB
|
||||||
|
await createEnvelope(request, tokenB, {
|
||||||
|
type: EnvelopeType.DOCUMENT,
|
||||||
|
title: 'UserB Document',
|
||||||
|
});
|
||||||
|
|
||||||
|
// userA should only see their own envelopes
|
||||||
|
const resA = await request.get(`${baseUrl}/envelope`, {
|
||||||
|
headers: { Authorization: `Bearer ${tokenA}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(resA.ok()).toBeTruthy();
|
||||||
|
const responseA = (await resA.json()) as TFindEnvelopesResponse;
|
||||||
|
|
||||||
|
expect(responseA.data.every((e) => e.title !== 'UserB Document')).toBe(true);
|
||||||
|
|
||||||
|
// userB should only see their own envelopes
|
||||||
|
const resB = await request.get(`${baseUrl}/envelope`, {
|
||||||
|
headers: { Authorization: `Bearer ${tokenB}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(resB.ok()).toBeTruthy();
|
||||||
|
const responseB = (await resB.json()) as TFindEnvelopesResponse;
|
||||||
|
|
||||||
|
expect(responseB.data.every((e) => e.title !== 'UserA Document')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return envelope with expected schema fields', async ({ request }) => {
|
||||||
|
await createEnvelope(request, tokenA, {
|
||||||
|
type: EnvelopeType.DOCUMENT,
|
||||||
|
title: 'Schema Test Document',
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await request.get(`${baseUrl}/envelope`, {
|
||||||
|
headers: { Authorization: `Bearer ${tokenA}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.ok()).toBeTruthy();
|
||||||
|
const response = (await res.json()) as TFindEnvelopesResponse;
|
||||||
|
|
||||||
|
const envelope = response.data.find((e) => e.title === 'Schema Test Document');
|
||||||
|
|
||||||
|
expect(envelope).toBeDefined();
|
||||||
|
expect(envelope?.id).toBeDefined();
|
||||||
|
expect(envelope?.type).toBe(EnvelopeType.DOCUMENT);
|
||||||
|
expect(envelope?.status).toBe(DocumentStatus.DRAFT);
|
||||||
|
expect(envelope?.recipients).toBeDefined();
|
||||||
|
expect(envelope?.user).toBeDefined();
|
||||||
|
expect(envelope?.team).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
202
packages/lib/server-only/envelope/find-envelopes.ts
Normal file
202
packages/lib/server-only/envelope/find-envelopes.ts
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
import type {
|
||||||
|
DocumentSource,
|
||||||
|
DocumentStatus,
|
||||||
|
Envelope,
|
||||||
|
EnvelopeType,
|
||||||
|
Prisma,
|
||||||
|
} from '@prisma/client';
|
||||||
|
|
||||||
|
import { prisma } from '@documenso/prisma';
|
||||||
|
|
||||||
|
import { TEAM_DOCUMENT_VISIBILITY_MAP } from '../../constants/teams';
|
||||||
|
import type { FindResultResponse } from '../../types/search-params';
|
||||||
|
import { maskRecipientTokensForDocument } from '../../utils/mask-recipient-tokens-for-document';
|
||||||
|
import { getTeamById } from '../team/get-team';
|
||||||
|
|
||||||
|
export type FindEnvelopesOptions = {
|
||||||
|
userId: number;
|
||||||
|
teamId: number;
|
||||||
|
type?: EnvelopeType;
|
||||||
|
templateId?: number;
|
||||||
|
source?: DocumentSource;
|
||||||
|
status?: DocumentStatus;
|
||||||
|
page?: number;
|
||||||
|
perPage?: number;
|
||||||
|
orderBy?: {
|
||||||
|
column: keyof Pick<Envelope, 'createdAt'>;
|
||||||
|
direction: 'asc' | 'desc';
|
||||||
|
};
|
||||||
|
query?: string;
|
||||||
|
folderId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const findEnvelopes = async ({
|
||||||
|
userId,
|
||||||
|
teamId,
|
||||||
|
type,
|
||||||
|
templateId,
|
||||||
|
source,
|
||||||
|
status,
|
||||||
|
page = 1,
|
||||||
|
perPage = 10,
|
||||||
|
orderBy,
|
||||||
|
query = '',
|
||||||
|
folderId,
|
||||||
|
}: FindEnvelopesOptions) => {
|
||||||
|
const user = await prisma.user.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
email: true,
|
||||||
|
name: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const team = await getTeamById({
|
||||||
|
userId,
|
||||||
|
teamId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const orderByColumn = orderBy?.column ?? 'createdAt';
|
||||||
|
const orderByDirection = orderBy?.direction ?? 'desc';
|
||||||
|
|
||||||
|
const searchFilter: Prisma.EnvelopeWhereInput = query
|
||||||
|
? {
|
||||||
|
OR: [
|
||||||
|
{ title: { contains: query, mode: 'insensitive' } },
|
||||||
|
{ externalId: { contains: query, mode: 'insensitive' } },
|
||||||
|
{ recipients: { some: { name: { contains: query, mode: 'insensitive' } } } },
|
||||||
|
{ recipients: { some: { email: { contains: query, mode: 'insensitive' } } } },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
: {};
|
||||||
|
|
||||||
|
const visibilityFilter: Prisma.EnvelopeWhereInput = {
|
||||||
|
visibility: {
|
||||||
|
in: TEAM_DOCUMENT_VISIBILITY_MAP[team.currentTeamRole],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const teamEmailFilters: Prisma.EnvelopeWhereInput[] = [];
|
||||||
|
|
||||||
|
if (team.teamEmail) {
|
||||||
|
teamEmailFilters.push(
|
||||||
|
{
|
||||||
|
user: {
|
||||||
|
email: team.teamEmail.email,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
recipients: {
|
||||||
|
some: {
|
||||||
|
email: team.teamEmail.email,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const whereClause: Prisma.EnvelopeWhereInput = {
|
||||||
|
AND: [
|
||||||
|
searchFilter,
|
||||||
|
{
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
teamId: team.id,
|
||||||
|
...visibilityFilter,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
...teamEmailFilters,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (type) {
|
||||||
|
whereClause.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (templateId) {
|
||||||
|
whereClause.templateId = templateId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
whereClause.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
whereClause.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (folderId !== undefined) {
|
||||||
|
whereClause.folderId = folderId;
|
||||||
|
} else {
|
||||||
|
whereClause.folderId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [data, count] = await Promise.all([
|
||||||
|
prisma.envelope.findMany({
|
||||||
|
where: whereClause,
|
||||||
|
skip: Math.max(page - 1, 0) * perPage,
|
||||||
|
take: perPage,
|
||||||
|
orderBy: {
|
||||||
|
[orderByColumn]: orderByDirection,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
user: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
email: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
recipients: {
|
||||||
|
orderBy: {
|
||||||
|
id: 'asc',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
team: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
prisma.envelope.count({
|
||||||
|
where: whereClause,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const maskedData = data.map((envelope) =>
|
||||||
|
maskRecipientTokensForDocument({
|
||||||
|
document: envelope,
|
||||||
|
user,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const mappedData = maskedData.map((envelope) => ({
|
||||||
|
...envelope,
|
||||||
|
recipients: envelope.Recipient,
|
||||||
|
user: {
|
||||||
|
id: envelope.user.id,
|
||||||
|
name: envelope.user.name || '',
|
||||||
|
email: envelope.user.email,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: mappedData,
|
||||||
|
count,
|
||||||
|
currentPage: Math.max(page, 1),
|
||||||
|
perPage,
|
||||||
|
totalPages: Math.ceil(count / perPage),
|
||||||
|
} satisfies FindResultResponse<typeof mappedData>;
|
||||||
|
};
|
||||||
@@ -115,5 +115,40 @@ export type TEnvelopeLite = z.infer<typeof ZEnvelopeLiteSchema>;
|
|||||||
/**
|
/**
|
||||||
* A version of the envelope response schema when returning multiple envelopes at once from a single API endpoint.
|
* A version of the envelope response schema when returning multiple envelopes at once from a single API endpoint.
|
||||||
*/
|
*/
|
||||||
// export const ZEnvelopeManySchema = X
|
export const ZEnvelopeManySchema = EnvelopeSchema.pick({
|
||||||
// export type TEnvelopeMany = z.infer<typeof ZEnvelopeManySchema>;
|
internalVersion: true,
|
||||||
|
type: true,
|
||||||
|
status: true,
|
||||||
|
source: true,
|
||||||
|
visibility: true,
|
||||||
|
templateType: true,
|
||||||
|
id: true,
|
||||||
|
secondaryId: true,
|
||||||
|
externalId: true,
|
||||||
|
createdAt: true,
|
||||||
|
updatedAt: true,
|
||||||
|
completedAt: true,
|
||||||
|
deletedAt: true,
|
||||||
|
title: true,
|
||||||
|
authOptions: true,
|
||||||
|
formValues: true,
|
||||||
|
publicTitle: true,
|
||||||
|
publicDescription: true,
|
||||||
|
userId: true,
|
||||||
|
teamId: true,
|
||||||
|
folderId: true,
|
||||||
|
templateId: true,
|
||||||
|
}).extend({
|
||||||
|
user: z.object({
|
||||||
|
id: z.number(),
|
||||||
|
name: z.string(),
|
||||||
|
email: z.string(),
|
||||||
|
}),
|
||||||
|
recipients: ZEnvelopeRecipientLiteSchema.array(),
|
||||||
|
team: TeamSchema.pick({
|
||||||
|
id: true,
|
||||||
|
url: true,
|
||||||
|
}).nullable(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TEnvelopeMany = z.infer<typeof ZEnvelopeManySchema>;
|
||||||
|
|||||||
56
packages/trpc/server/envelope-router/find-envelopes.ts
Normal file
56
packages/trpc/server/envelope-router/find-envelopes.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { findEnvelopes } from '@documenso/lib/server-only/envelope/find-envelopes';
|
||||||
|
|
||||||
|
import { authenticatedProcedure } from '../trpc';
|
||||||
|
import {
|
||||||
|
ZFindEnvelopesRequestSchema,
|
||||||
|
ZFindEnvelopesResponseSchema,
|
||||||
|
findEnvelopesMeta,
|
||||||
|
} from './find-envelopes.types';
|
||||||
|
|
||||||
|
export const findEnvelopesRoute = authenticatedProcedure
|
||||||
|
.meta(findEnvelopesMeta)
|
||||||
|
.input(ZFindEnvelopesRequestSchema)
|
||||||
|
.output(ZFindEnvelopesResponseSchema)
|
||||||
|
.query(async ({ input, ctx }) => {
|
||||||
|
const { user, teamId } = ctx;
|
||||||
|
|
||||||
|
const {
|
||||||
|
query,
|
||||||
|
type,
|
||||||
|
templateId,
|
||||||
|
page,
|
||||||
|
perPage,
|
||||||
|
orderByDirection,
|
||||||
|
orderByColumn,
|
||||||
|
source,
|
||||||
|
status,
|
||||||
|
folderId,
|
||||||
|
} = input;
|
||||||
|
|
||||||
|
ctx.logger.info({
|
||||||
|
input: {
|
||||||
|
query,
|
||||||
|
type,
|
||||||
|
templateId,
|
||||||
|
source,
|
||||||
|
status,
|
||||||
|
folderId,
|
||||||
|
page,
|
||||||
|
perPage,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return await findEnvelopes({
|
||||||
|
userId: user.id,
|
||||||
|
teamId,
|
||||||
|
type,
|
||||||
|
templateId,
|
||||||
|
query,
|
||||||
|
source,
|
||||||
|
status,
|
||||||
|
page,
|
||||||
|
perPage,
|
||||||
|
folderId,
|
||||||
|
orderBy: orderByColumn ? { column: orderByColumn, direction: orderByDirection } : undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
46
packages/trpc/server/envelope-router/find-envelopes.types.ts
Normal file
46
packages/trpc/server/envelope-router/find-envelopes.types.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { DocumentSource, DocumentStatus, EnvelopeType } from '@prisma/client';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { ZEnvelopeManySchema } from '@documenso/lib/types/envelope';
|
||||||
|
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
|
||||||
|
|
||||||
|
import type { TrpcRouteMeta } from '../trpc';
|
||||||
|
|
||||||
|
export const findEnvelopesMeta: TrpcRouteMeta = {
|
||||||
|
openapi: {
|
||||||
|
method: 'GET',
|
||||||
|
path: '/envelope',
|
||||||
|
summary: 'Find envelopes',
|
||||||
|
description: 'Find envelopes based on search criteria',
|
||||||
|
tags: ['Envelope'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ZFindEnvelopesRequestSchema = ZFindSearchParamsSchema.extend({
|
||||||
|
type: z
|
||||||
|
.nativeEnum(EnvelopeType)
|
||||||
|
.describe('Filter envelopes by type (DOCUMENT or TEMPLATE).')
|
||||||
|
.optional(),
|
||||||
|
templateId: z
|
||||||
|
.number()
|
||||||
|
.describe('Filter envelopes by the template ID used to create it.')
|
||||||
|
.optional(),
|
||||||
|
source: z
|
||||||
|
.nativeEnum(DocumentSource)
|
||||||
|
.describe('Filter envelopes by how it was created.')
|
||||||
|
.optional(),
|
||||||
|
status: z
|
||||||
|
.nativeEnum(DocumentStatus)
|
||||||
|
.describe('Filter envelopes by the current status.')
|
||||||
|
.optional(),
|
||||||
|
folderId: z.string().describe('Filter envelopes by folder ID.').optional(),
|
||||||
|
orderByColumn: z.enum(['createdAt']).optional(),
|
||||||
|
orderByDirection: z.enum(['asc', 'desc']).describe('Sort direction.').default('desc'),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ZFindEnvelopesResponseSchema = ZFindResultResponse.extend({
|
||||||
|
data: ZEnvelopeManySchema.array(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TFindEnvelopesRequest = z.infer<typeof ZFindEnvelopesRequestSchema>;
|
||||||
|
export type TFindEnvelopesResponse = z.infer<typeof ZFindEnvelopesResponseSchema>;
|
||||||
@@ -18,6 +18,7 @@ import { createEnvelopeRecipientsRoute } from './envelope-recipients/create-enve
|
|||||||
import { deleteEnvelopeRecipientRoute } from './envelope-recipients/delete-envelope-recipient';
|
import { deleteEnvelopeRecipientRoute } from './envelope-recipients/delete-envelope-recipient';
|
||||||
import { getEnvelopeRecipientRoute } from './envelope-recipients/get-envelope-recipient';
|
import { getEnvelopeRecipientRoute } from './envelope-recipients/get-envelope-recipient';
|
||||||
import { updateEnvelopeRecipientsRoute } from './envelope-recipients/update-envelope-recipients';
|
import { updateEnvelopeRecipientsRoute } from './envelope-recipients/update-envelope-recipients';
|
||||||
|
import { findEnvelopesRoute } from './find-envelopes';
|
||||||
import { getEnvelopeRoute } from './get-envelope';
|
import { getEnvelopeRoute } from './get-envelope';
|
||||||
import { getEnvelopeItemsRoute } from './get-envelope-items';
|
import { getEnvelopeItemsRoute } from './get-envelope-items';
|
||||||
import { getEnvelopeItemsByTokenRoute } from './get-envelope-items-by-token';
|
import { getEnvelopeItemsByTokenRoute } from './get-envelope-items-by-token';
|
||||||
@@ -65,6 +66,7 @@ export const envelopeRouter = router({
|
|||||||
set: setEnvelopeFieldsRoute,
|
set: setEnvelopeFieldsRoute,
|
||||||
sign: signEnvelopeFieldRoute,
|
sign: signEnvelopeFieldRoute,
|
||||||
},
|
},
|
||||||
|
find: findEnvelopesRoute,
|
||||||
get: getEnvelopeRoute,
|
get: getEnvelopeRoute,
|
||||||
create: createEnvelopeRoute,
|
create: createEnvelopeRoute,
|
||||||
use: useEnvelopeRoute,
|
use: useEnvelopeRoute,
|
||||||
|
|||||||
Reference in New Issue
Block a user