Files
sign/packages/api/v1/schema.ts
Catalin Pit 0829311214 feat: prefill fields via api (#1261)
## Description

Configure the advanced field via API.

## Checklist

<!--- Please check the boxes that apply to this pull request. -->
<!--- You can add or remove items as needed. -->

- [x] I have tested these changes locally and they work as expected.
- [ ] I have added/updated tests that prove the effectiveness of these
changes.
- [x] I have updated the documentation to reflect these changes, if
applicable.
- [x] I have followed the project's coding style guidelines.
- [ ] I have addressed the code review feedback from the previous
submission, if applicable.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Enhanced API functionality to support field metadata during field
creation.
- Introduced validation checks for field metadata to ensure necessary
information is provided for advanced field types.

- **Bug Fixes**
- Improved error handling during field creation to return properly
formatted error responses.

- **Documentation**
- Updated API schemas to include field metadata, enhancing data
validation and response structures.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-08-26 12:37:56 +03:00

467 lines
13 KiB
TypeScript

import { extendZodWithOpenApi } from '@anatine/zod-openapi';
import { z } from 'zod';
import { DATE_FORMATS, DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
import '@documenso/lib/constants/time-zones';
import { DEFAULT_DOCUMENT_TIME_ZONE, TIME_ZONES } from '@documenso/lib/constants/time-zones';
import { ZUrlSchema } from '@documenso/lib/schemas/common';
import { ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
import {
DocumentDataType,
FieldType,
ReadStatus,
RecipientRole,
SendStatus,
SigningStatus,
TemplateType,
} from '@documenso/prisma/client';
extendZodWithOpenApi(z);
export const ZNoBodyMutationSchema = null;
/**
* Documents
*/
export const ZGetDocumentsQuerySchema = z.object({
page: z.coerce.number().min(1).optional().default(1),
perPage: z.coerce.number().min(1).optional().default(1),
});
export type TGetDocumentsQuerySchema = z.infer<typeof ZGetDocumentsQuerySchema>;
export const ZDeleteDocumentMutationSchema = null;
export type TDeleteDocumentMutationSchema = typeof ZDeleteDocumentMutationSchema;
export const ZSuccessfulDocumentResponseSchema = z.object({
id: z.number(),
externalId: z.string().nullish(),
userId: z.number(),
teamId: z.number().nullish(),
title: z.string(),
status: z.string(),
documentDataId: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
completedAt: z.date().nullable(),
});
export const ZSuccessfulGetDocumentResponseSchema = ZSuccessfulDocumentResponseSchema.extend({
recipients: z.lazy(() => z.array(ZSuccessfulRecipientResponseSchema)),
});
export type TSuccessfulGetDocumentResponseSchema = z.infer<
typeof ZSuccessfulGetDocumentResponseSchema
>;
export type TSuccessfulDocumentResponseSchema = z.infer<typeof ZSuccessfulDocumentResponseSchema>;
export const ZSendDocumentForSigningMutationSchema = z
.object({
sendEmail: z.boolean().optional().default(true),
})
.or(z.literal('').transform(() => ({ sendEmail: true })));
export type TSendDocumentForSigningMutationSchema = typeof ZSendDocumentForSigningMutationSchema;
export const ZResendDocumentForSigningMutationSchema = z.object({
recipients: z.array(z.number()),
});
export type TResendDocumentForSigningMutationSchema = z.infer<
typeof ZResendDocumentForSigningMutationSchema
>;
export const ZSuccessfulResendDocumentResponseSchema = z.object({
message: z.string(),
});
export type TResendDocumentResponseSchema = z.infer<typeof ZSuccessfulResendDocumentResponseSchema>;
export const ZUploadDocumentSuccessfulSchema = z.object({
url: z.string(),
key: z.string(),
});
export const ZDownloadDocumentSuccessfulSchema = z.object({
downloadUrl: z.string(),
});
export type TUploadDocumentSuccessfulSchema = z.infer<typeof ZUploadDocumentSuccessfulSchema>;
export const ZCreateDocumentMutationSchema = z.object({
title: z.string().min(1),
externalId: z.string().nullish(),
recipients: z.array(
z.object({
name: z.string().min(1),
email: z.string().email().min(1),
role: z.nativeEnum(RecipientRole).optional().default(RecipientRole.SIGNER),
}),
),
meta: z
.object({
subject: z.string(),
message: z.string(),
timezone: z.string().default(DEFAULT_DOCUMENT_TIME_ZONE).openapi({
description:
'The timezone of the date. Must be one of the options listed in the list below.',
enum: TIME_ZONES,
}),
dateFormat: z
.string()
.default(DEFAULT_DOCUMENT_DATE_FORMAT)
.openapi({
description:
'The format of the date. Must be one of the options listed in the list below.',
enum: DATE_FORMATS.map((format) => format.value),
}),
redirectUrl: z.string(),
})
.partial(),
formValues: z.record(z.string(), z.union([z.string(), z.boolean(), z.number()])).optional(),
});
export type TCreateDocumentMutationSchema = z.infer<typeof ZCreateDocumentMutationSchema>;
export const ZCreateDocumentMutationResponseSchema = z.object({
uploadUrl: z.string().min(1),
documentId: z.number(),
externalId: z.string().nullish(),
recipients: z.array(
z.object({
recipientId: z.number(),
name: z.string(),
email: z.string().email().min(1),
token: z.string(),
role: z.nativeEnum(RecipientRole),
signingUrl: z.string(),
}),
),
});
export type TCreateDocumentMutationResponseSchema = z.infer<
typeof ZCreateDocumentMutationResponseSchema
>;
export const ZCreateDocumentFromTemplateMutationSchema = z.object({
title: z.string().min(1),
externalId: z.string().nullish(),
recipients: z.array(
z.object({
name: z.string().min(1),
email: z.string().email().min(1),
role: z.nativeEnum(RecipientRole).optional().default(RecipientRole.SIGNER),
}),
),
meta: z
.object({
subject: z.string(),
message: z.string(),
timezone: z.string(),
dateFormat: z.string(),
redirectUrl: z.string(),
})
.partial()
.optional(),
formValues: z.record(z.string(), z.union([z.string(), z.boolean(), z.number()])).optional(),
});
export type TCreateDocumentFromTemplateMutationSchema = z.infer<
typeof ZCreateDocumentFromTemplateMutationSchema
>;
export const ZCreateDocumentFromTemplateMutationResponseSchema = z.object({
documentId: z.number(),
externalId: z.string().nullish(),
recipients: z.array(
z.object({
recipientId: z.number(),
name: z.string(),
email: z.string().email().min(1),
token: z.string(),
role: z.nativeEnum(RecipientRole).optional().default(RecipientRole.SIGNER),
signingUrl: z.string(),
}),
),
});
export type TCreateDocumentFromTemplateMutationResponseSchema = z.infer<
typeof ZCreateDocumentFromTemplateMutationResponseSchema
>;
export const ZGenerateDocumentFromTemplateMutationSchema = z.object({
title: z.string().optional(),
externalId: z.string().nullish(),
recipients: z
.array(
z.object({
id: z.number(),
name: z.string().optional(),
email: z.string().email().min(1),
}),
)
.refine(
(schema) => {
const emails = schema.map((signer) => signer.email.toLowerCase());
const ids = schema.map((signer) => signer.id);
return new Set(emails).size === emails.length && new Set(ids).size === ids.length;
},
{ message: 'Recipient IDs and emails must be unique' },
),
meta: z
.object({
subject: z.string(),
message: z.string(),
timezone: z.string(),
dateFormat: z.string(),
redirectUrl: ZUrlSchema,
})
.partial()
.optional(),
formValues: z.record(z.string(), z.union([z.string(), z.boolean(), z.number()])).optional(),
});
export type TGenerateDocumentFromTemplateMutationSchema = z.infer<
typeof ZGenerateDocumentFromTemplateMutationSchema
>;
export const ZGenerateDocumentFromTemplateMutationResponseSchema = z.object({
documentId: z.number(),
externalId: z.string().nullish(),
recipients: z.array(
z.object({
recipientId: z.number(),
name: z.string(),
email: z.string().email().min(1),
token: z.string(),
role: z.nativeEnum(RecipientRole),
signingUrl: z.string(),
}),
),
});
export type TGenerateDocumentFromTemplateMutationResponseSchema = z.infer<
typeof ZGenerateDocumentFromTemplateMutationResponseSchema
>;
export const ZCreateRecipientMutationSchema = z.object({
name: z.string().min(1),
email: z.string().email().min(1),
role: z.nativeEnum(RecipientRole).optional().default(RecipientRole.SIGNER),
});
/**
* Recipients
*/
export type TCreateRecipientMutationSchema = z.infer<typeof ZCreateRecipientMutationSchema>;
export const ZUpdateRecipientMutationSchema = ZCreateRecipientMutationSchema.partial();
export type TUpdateRecipientMutationSchema = z.infer<typeof ZUpdateRecipientMutationSchema>;
export const ZDeleteRecipientMutationSchema = null;
export type TDeleteRecipientMutationSchema = typeof ZDeleteRecipientMutationSchema;
export const ZSuccessfulRecipientResponseSchema = z.object({
id: z.number(),
// !: This handles the fact that we have null documentId's for templates
// !: while we won't need the default we must add it to satisfy typescript
documentId: z.number().nullish().default(-1),
email: z.string().email().min(1),
name: z.string(),
role: z.nativeEnum(RecipientRole),
token: z.string(),
// !: Not used for now
// expired: z.string(),
signedAt: z.date().nullable(),
readStatus: z.nativeEnum(ReadStatus),
signingStatus: z.nativeEnum(SigningStatus),
sendStatus: z.nativeEnum(SendStatus),
signingUrl: z.string(),
});
export type TSuccessfulRecipientResponseSchema = z.infer<typeof ZSuccessfulRecipientResponseSchema>;
/**
* Fields
*/
export const ZCreateFieldMutationSchema = z.object({
recipientId: z.number(),
type: z.nativeEnum(FieldType),
pageNumber: z.number(),
pageX: z.number(),
pageY: z.number(),
pageWidth: z.number(),
pageHeight: z.number(),
fieldMeta: ZFieldMetaSchema,
});
export type TCreateFieldMutationSchema = z.infer<typeof ZCreateFieldMutationSchema>;
export const ZUpdateFieldMutationSchema = ZCreateFieldMutationSchema.partial();
export type TUpdateFieldMutationSchema = z.infer<typeof ZUpdateFieldMutationSchema>;
export const ZDeleteFieldMutationSchema = null;
export type TDeleteFieldMutationSchema = typeof ZDeleteFieldMutationSchema;
export const ZSuccessfulFieldResponseSchema = z.object({
id: z.number(),
documentId: z.number(),
recipientId: z.number(),
type: z.nativeEnum(FieldType),
pageNumber: z.number(),
pageX: z.number(),
pageY: z.number(),
pageWidth: z.number(),
pageHeight: z.number(),
customText: z.string(),
fieldMeta: ZFieldMetaSchema,
inserted: z.boolean(),
});
export type TSuccessfulFieldResponseSchema = z.infer<typeof ZSuccessfulFieldResponseSchema>;
export const ZSuccessfulResponseSchema = z.object({
documents: ZSuccessfulDocumentResponseSchema.array(),
totalPages: z.number(),
});
export type TSuccessfulResponseSchema = z.infer<typeof ZSuccessfulResponseSchema>;
export const ZSuccessfulSigningResponseSchema = z
.object({
message: z.string(),
})
.and(ZSuccessfulGetDocumentResponseSchema);
export type TSuccessfulSigningResponseSchema = z.infer<typeof ZSuccessfulSigningResponseSchema>;
/**
* General
*/
export const ZAuthorizationHeadersSchema = z.object({
authorization: z.string(),
});
export type TAuthorizationHeadersSchema = z.infer<typeof ZAuthorizationHeadersSchema>;
export const ZUnsuccessfulResponseSchema = z.object({
message: z.string(),
});
export type TUnsuccessfulResponseSchema = z.infer<typeof ZUnsuccessfulResponseSchema>;
export const ZTemplateMetaSchema = z.object({
id: z.string(),
subject: z.string().nullish(),
message: z.string().nullish(),
timezone: z.string().nullish(),
dateFormat: z.string().nullish(),
templateId: z.number(),
redirectUrl: z.string().nullish(),
});
export const ZTemplateSchema = z.object({
id: z.number(),
externalId: z.string().nullish(),
type: z.nativeEnum(TemplateType),
title: z.string(),
userId: z.number(),
teamId: z.number().nullish(),
templateDocumentDataId: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
});
export const ZRecipientSchema = z.object({
id: z.number(),
documentId: z.number().nullish(),
templateId: z.number().nullish(),
email: z.string().email().min(1),
name: z.string(),
token: z.string(),
documentDeletedAt: z.date().nullish(),
expired: z.date().nullish(),
signedAt: z.date().nullish(),
authOptions: z.unknown(),
role: z.nativeEnum(RecipientRole),
readStatus: z.nativeEnum(ReadStatus),
signingStatus: z.nativeEnum(SigningStatus),
sendStatus: z.nativeEnum(SendStatus),
});
export const ZFieldSchema = z.object({
id: z.number(),
secondaryId: z.string(),
documentId: z.number().nullish(),
templateId: z.number().nullish(),
recipientId: z.number(),
type: z.nativeEnum(FieldType),
page: z.number(),
positionX: z.unknown(),
positionY: z.unknown(),
width: z.unknown(),
height: z.unknown(),
customText: z.string(),
inserted: z.boolean(),
});
export const ZTemplateWithDataSchema = ZTemplateSchema.extend({
templateMeta: ZTemplateMetaSchema.nullish(),
directLink: z
.object({
token: z.string(),
enabled: z.boolean(),
})
.nullable(),
templateDocumentData: z.object({
id: z.string(),
type: z.nativeEnum(DocumentDataType),
data: z.string(),
}),
Field: ZFieldSchema.pick({
id: true,
recipientId: true,
type: true,
page: true,
positionX: true,
positionY: true,
width: true,
height: true,
}).array(),
Recipient: ZRecipientSchema.pick({
id: true,
email: true,
name: true,
authOptions: true,
role: true,
}).array(),
});
export const ZSuccessfulGetTemplateResponseSchema = ZTemplateWithDataSchema;
export const ZSuccessfulDeleteTemplateResponseSchema = ZTemplateSchema;
export const ZSuccessfulGetTemplatesResponseSchema = z.object({
templates: ZTemplateWithDataSchema.omit({
templateDocumentData: true,
templateMeta: true,
}).array(),
totalPages: z.number(),
});
export const ZGetTemplatesQuerySchema = z.object({
page: z.coerce.number().min(1).optional().default(1),
perPage: z.coerce.number().min(1).optional().default(1),
});