fix: add regression test

This commit is contained in:
David Nguyen
2025-10-31 12:38:14 +11:00
parent f6bdb34b56
commit 9020bbc753
41 changed files with 3191 additions and 74 deletions

View File

@ -13,7 +13,7 @@ export const createAttachmentRoute = authenticatedProcedure
path: '/envelope/attachment/create',
summary: 'Create attachment',
description: 'Create a new attachment for an envelope',
tags: ['Envelope'],
tags: ['Envelope Attachment'],
},
})
.input(ZCreateAttachmentRequestSchema)

View File

@ -13,7 +13,7 @@ export const deleteAttachmentRoute = authenticatedProcedure
path: '/envelope/attachment/delete',
summary: 'Delete attachment',
description: 'Delete an attachment from an envelope',
tags: ['Envelope'],
tags: ['Envelope Attachment'],
},
})
.input(ZDeleteAttachmentRequestSchema)

View File

@ -2,20 +2,20 @@ import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { findAttachmentsByEnvelopeId } from '@documenso/lib/server-only/envelope-attachment/find-attachments-by-envelope-id';
import { findAttachmentsByToken } from '@documenso/lib/server-only/envelope-attachment/find-attachments-by-token';
import { procedure } from '../../trpc';
import { maybeAuthenticatedProcedure } from '../../trpc';
import {
ZFindAttachmentsRequestSchema,
ZFindAttachmentsResponseSchema,
} from './find-attachments.types';
export const findAttachmentsRoute = procedure
export const findAttachmentsRoute = maybeAuthenticatedProcedure
.meta({
openapi: {
method: 'GET',
path: '/envelope/attachment',
summary: 'Find attachments',
description: 'Find all attachments for an envelope',
tags: ['Envelope'],
tags: ['Envelope Attachment'],
},
})
.input(ZFindAttachmentsRequestSchema)

View File

@ -13,7 +13,7 @@ export const updateAttachmentRoute = authenticatedProcedure
path: '/envelope/attachment/update',
summary: 'Update attachment',
description: 'Update an existing attachment',
tags: ['Envelope'],
tags: ['Envelope Attachment'],
},
})
.input(ZUpdateAttachmentRequestSchema)

View File

@ -14,15 +14,15 @@ import {
export const createEnvelopeItemsRoute = authenticatedProcedure
// Todo: Envelopes - Pending direct uploads
// .meta({
// openapi: {
// method: 'POST',
// path: '/envelope/item/create-many',
// summary: 'Create envelope items',
// description: 'Create multiple envelope items for an envelope',
// tags: ['Envelope Item'],
// },
// })
.meta({
openapi: {
method: 'POST',
path: '/envelope/item/create-many',
summary: 'Create envelope items',
description: 'Create multiple envelope items for an envelope',
tags: ['Envelope Item'],
},
})
.input(ZCreateEnvelopeItemsRequestSchema)
.output(ZCreateEnvelopeItemsResponseSchema)
.mutation(async ({ input, ctx }) => {

View File

@ -2,8 +2,8 @@ import { z } from 'zod';
import {
ZClampedFieldHeightSchema,
ZClampedFieldPageXSchema,
ZClampedFieldPageYSchema,
ZClampedFieldPositionXSchema,
ZClampedFieldPositionYSchema,
ZClampedFieldWidthSchema,
ZFieldPageNumberSchema,
ZFieldSchema,
@ -19,9 +19,9 @@ const ZCreateFieldSchema = ZFieldAndMetaSchema.and(
.describe(
'The ID of the envelope item to put the field on. If not provided, field will be placed on the first item.',
),
pageNumber: ZFieldPageNumberSchema,
pageX: ZClampedFieldPageXSchema,
pageY: ZClampedFieldPageYSchema,
page: ZFieldPageNumberSchema,
positionX: ZClampedFieldPositionXSchema,
positionY: ZClampedFieldPositionYSchema,
width: ZClampedFieldWidthSchema,
height: ZClampedFieldHeightSchema,
}),

View File

@ -2,8 +2,8 @@ import { z } from 'zod';
import {
ZClampedFieldHeightSchema,
ZClampedFieldPageXSchema,
ZClampedFieldPageYSchema,
ZClampedFieldPositionXSchema,
ZClampedFieldPositionYSchema,
ZClampedFieldWidthSchema,
ZFieldPageNumberSchema,
ZFieldSchema,
@ -19,9 +19,9 @@ const ZUpdateFieldSchema = ZFieldAndMetaSchema.and(
.describe(
'The ID of the envelope item to put the field on. If not provided, field will be placed on the first item.',
),
pageNumber: ZFieldPageNumberSchema.optional(),
pageX: ZClampedFieldPageXSchema.optional(),
pageY: ZClampedFieldPageYSchema.optional(),
page: ZFieldPageNumberSchema.optional(),
positionX: ZClampedFieldPositionXSchema.optional(),
positionY: ZClampedFieldPositionYSchema.optional(),
width: ZClampedFieldWidthSchema.optional(),
height: ZClampedFieldHeightSchema.optional(),
}),

View File

@ -27,14 +27,18 @@ import { signEnvelopeFieldRoute } from './sign-envelope-field';
import { updateEnvelopeRoute } from './update-envelope';
import { updateEnvelopeItemsRoute } from './update-envelope-items';
/**
* Note: The order of the routes is important for public API routes.
*
* Example: GET /envelope/attachment must appear before GET /envelope/:id
*/
export const envelopeRouter = router({
get: getEnvelopeRoute,
create: createEnvelopeRoute,
update: updateEnvelopeRoute,
delete: deleteEnvelopeRoute,
duplicate: duplicateEnvelopeRoute,
distribute: distributeEnvelopeRoute,
redistribute: redistributeEnvelopeRoute,
attachment: {
find: findAttachmentsRoute,
create: createAttachmentRoute,
update: updateAttachmentRoute,
delete: deleteAttachmentRoute,
},
item: {
getMany: getEnvelopeItemsRoute,
getManyByToken: getEnvelopeItemsByTokenRoute,
@ -57,10 +61,11 @@ export const envelopeRouter = router({
set: setEnvelopeFieldsRoute,
sign: signEnvelopeFieldRoute,
},
attachment: {
find: findAttachmentsRoute,
create: createAttachmentRoute,
update: updateAttachmentRoute,
delete: deleteAttachmentRoute,
},
get: getEnvelopeRoute,
create: createEnvelopeRoute,
update: updateEnvelopeRoute,
delete: deleteEnvelopeRoute,
duplicate: duplicateEnvelopeRoute,
distribute: distributeEnvelopeRoute,
redistribute: redistributeEnvelopeRoute,
});

View File

@ -3,8 +3,8 @@ import { z } from 'zod';
import {
ZClampedFieldHeightSchema,
ZClampedFieldPageXSchema,
ZClampedFieldPageYSchema,
ZClampedFieldPositionXSchema,
ZClampedFieldPositionYSchema,
ZClampedFieldWidthSchema,
} from '@documenso/lib/types/field';
import { ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
@ -26,8 +26,8 @@ export const ZSetEnvelopeFieldsRequestSchema = z.object({
.number()
.min(1)
.describe('The page number of the field on the envelope. Starts from 1.'),
positionX: ZClampedFieldPageXSchema,
positionY: ZClampedFieldPageYSchema,
positionX: ZClampedFieldPositionXSchema,
positionY: ZClampedFieldPositionYSchema,
width: ZClampedFieldWidthSchema,
height: ZClampedFieldHeightSchema,
fieldMeta: ZFieldMetaSchema,

View File

@ -108,7 +108,14 @@ export const fieldRouter = router({
type: 'documentId',
id: documentId,
},
fields: [field],
fields: [
{
...field,
page: field.pageNumber,
positionX: field.pageX,
positionY: field.pageY,
},
],
requestMetadata: ctx.metadata,
});
@ -147,7 +154,12 @@ export const fieldRouter = router({
type: 'documentId',
id: documentId,
},
fields,
fields: fields.map((field) => ({
...field,
page: field.pageNumber,
positionX: field.pageX,
positionY: field.pageY,
})),
requestMetadata: ctx.metadata,
});
}),
@ -335,7 +347,14 @@ export const fieldRouter = router({
type: 'templateId',
id: templateId,
},
fields: [field],
fields: [
{
...field,
page: field.pageNumber,
positionX: field.pageX,
positionY: field.pageY,
},
],
requestMetadata: ctx.metadata,
});
@ -408,7 +427,12 @@ export const fieldRouter = router({
type: 'templateId',
id: templateId,
},
fields,
fields: fields.map((field) => ({
...field,
page: field.pageNumber,
positionX: field.pageX,
positionY: field.pageY,
})),
requestMetadata: ctx.metadata,
});
}),

View File

@ -164,14 +164,62 @@ export const maybeAuthenticatedMiddleware = t.middleware(async ({ ctx, next, pat
nonBatchedRequestId: alphaid(),
});
ctx.logger.info({
const infoToLog: TrpcApiLog = {
path,
auth: ctx.metadata.auth,
source: ctx.metadata.source,
userId: ctx.user?.id,
apiTokenId: null,
trpcMiddleware: 'maybeAuthenticated',
unverifiedTeamId: ctx.teamId,
};
const authorizationHeader = ctx.req.headers.get('authorization');
// Taken from `authenticatedMiddleware` in `@documenso/api/v1/middleware/authenticated.ts`.
if (authorizationHeader) {
// Support for both "Authorization: Bearer api_xxx" and "Authorization: api_xxx"
const [token] = (authorizationHeader || '').split('Bearer ').filter((s) => s.length > 0);
if (!token) {
throw new Error('Token was not provided for authenticated middleware');
}
const apiToken = await getApiTokenByToken({ token });
ctx.logger.info({
...infoToLog,
userId: apiToken.user.id,
apiTokenId: apiToken.id,
} satisfies TrpcApiLog);
return await next({
ctx: {
...ctx,
user: apiToken.user,
teamId: apiToken.teamId,
session: null,
metadata: {
...ctx.metadata,
auditUser: apiToken.team
? {
id: null,
email: null,
name: apiToken.team.name,
}
: {
id: apiToken.user.id,
email: apiToken.user.email,
name: apiToken.user.name,
},
auth: 'api',
} satisfies ApiRequestMetadata,
},
});
}
trpcSessionLogger.info({
...infoToLog,
userId: ctx.user?.id,
apiTokenId: null,
} satisfies TrpcApiLog);
return await next({