Skip to content

Commit

Permalink
feat: prefill fields via api (documenso#1261)
Browse files Browse the repository at this point in the history
## 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 -->
  • Loading branch information
catalinpit authored Aug 26, 2024
1 parent 9223527 commit 0829311
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 34 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

packages/prisma/generated/types.ts

# dependencies
node_modules
.pnp
Expand Down
85 changes: 51 additions & 34 deletions packages/api/v1/implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { createDocumentFromTemplateLegacy } from '@documenso/lib/server-only/tem
import { deleteTemplate } from '@documenso/lib/server-only/template/delete-template';
import { findTemplates } from '@documenso/lib/server-only/template/find-templates';
import { getTemplateById } from '@documenso/lib/server-only/template/get-template-by-id';
import { ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { getFile } from '@documenso/lib/universal/upload/get-file';
import { putPdfFile } from '@documenso/lib/universal/upload/put-file';
Expand Down Expand Up @@ -869,7 +870,17 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {

createField: authenticatedMiddleware(async (args, user, team) => {
const { id: documentId } = args.params;
const { recipientId, type, pageNumber, pageWidth, pageHeight, pageX, pageY } = args.body;
const { recipientId, type, pageNumber, pageWidth, pageHeight, pageX, pageY, fieldMeta } =
args.body;

if (pageNumber <= 0) {
return {
status: 400,
body: {
message: 'Invalid page number',
},
};
}

const document = await getDocumentById({
id: Number(documentId),
Expand Down Expand Up @@ -918,41 +929,47 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
};
}

const field = await createField({
documentId: Number(documentId),
recipientId: Number(recipientId),
userId: user.id,
teamId: team?.id,
type,
pageNumber,
pageX,
pageY,
pageWidth,
pageHeight,
requestMetadata: extractNextApiRequestMetadata(args.req),
});
try {
const field = await createField({
documentId: Number(documentId),
recipientId: Number(recipientId),
userId: user.id,
teamId: team?.id,
type,
pageNumber,
pageX,
pageY,
pageWidth,
pageHeight,
fieldMeta,
requestMetadata: extractNextApiRequestMetadata(args.req),
});

const remappedField = {
id: field.id,
documentId: field.documentId,
recipientId: field.recipientId ?? -1,
type: field.type,
pageNumber: field.page,
pageX: Number(field.positionX),
pageY: Number(field.positionY),
pageWidth: Number(field.width),
pageHeight: Number(field.height),
customText: field.customText,
inserted: field.inserted,
};
const remappedField = {
id: field.id,
documentId: field.documentId,
recipientId: field.recipientId ?? -1,
type: field.type,
pageNumber: field.page,
pageX: Number(field.positionX),
pageY: Number(field.positionY),
pageWidth: Number(field.width),
pageHeight: Number(field.height),
customText: field.customText,
fieldMeta: ZFieldMetaSchema.parse(field.fieldMeta),
inserted: field.inserted,
};

return {
status: 200,
body: {
...remappedField,
documentId: Number(documentId),
},
};
return {
status: 200,
body: {
...remappedField,
documentId: Number(documentId),
},
};
} catch (err) {
return AppError.toRestAPIError(err);
}
}),

updateField: authenticatedMiddleware(async (args, user, team) => {
Expand Down
3 changes: 3 additions & 0 deletions packages/api/v1/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { DATE_FORMATS, DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/const
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,
Expand Down Expand Up @@ -300,6 +301,7 @@ export const ZCreateFieldMutationSchema = z.object({
pageY: z.number(),
pageWidth: z.number(),
pageHeight: z.number(),
fieldMeta: ZFieldMetaSchema,
});

export type TCreateFieldMutationSchema = z.infer<typeof ZCreateFieldMutationSchema>;
Expand All @@ -323,6 +325,7 @@ export const ZSuccessfulFieldResponseSchema = z.object({
pageWidth: z.number(),
pageHeight: z.number(),
customText: z.string(),
fieldMeta: ZFieldMetaSchema,
inserted: z.boolean(),
});

Expand Down
49 changes: 49 additions & 0 deletions packages/lib/server-only/field/create-field.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import { match } from 'ts-pattern';

import { prisma } from '@documenso/prisma';
import type { FieldType, Team } from '@documenso/prisma/client';

import {
ZCheckboxFieldMeta,
ZDropdownFieldMeta,
ZNumberFieldMeta,
ZRadioFieldMeta,
ZTextFieldMeta,
} from '../../types/field-meta';
import type { TFieldMetaSchema as FieldMeta } from '../../types/field-meta';
import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';

Expand All @@ -15,6 +25,7 @@ export type CreateFieldOptions = {
pageY: number;
pageWidth: number;
pageHeight: number;
fieldMeta?: FieldMeta;
requestMetadata?: RequestMetadata;
};

Expand All @@ -29,6 +40,7 @@ export const createField = async ({
pageY,
pageWidth,
pageHeight,
fieldMeta,
requestMetadata,
}: CreateFieldOptions) => {
const document = await prisma.document.findFirst({
Expand Down Expand Up @@ -85,6 +97,42 @@ export const createField = async ({
});
}

const advancedField = ['NUMBER', 'RADIO', 'CHECKBOX', 'DROPDOWN', 'TEXT'].includes(type);

if (advancedField && !fieldMeta) {
throw new Error(
'Field meta is required for this type of field. Please provide the appropriate field meta object.',
);
}

if (fieldMeta && fieldMeta.type.toLowerCase() !== String(type).toLowerCase()) {
throw new Error('Field meta type does not match the field type');
}

const result = match(type)
.with('RADIO', () => {
return ZRadioFieldMeta.safeParse(fieldMeta);
})
.with('CHECKBOX', () => {
return ZCheckboxFieldMeta.safeParse(fieldMeta);
})
.with('DROPDOWN', () => {
return ZDropdownFieldMeta.safeParse(fieldMeta);
})
.with('NUMBER', () => {
return ZNumberFieldMeta.safeParse(fieldMeta);
})
.with('TEXT', () => {
return ZTextFieldMeta.safeParse(fieldMeta);
})
.otherwise(() => {
return { success: false, data: {} };
});

if (!result.success) {
throw new Error('Field meta parsing failed');
}

const field = await prisma.field.create({
data: {
documentId,
Expand All @@ -97,6 +145,7 @@ export const createField = async ({
height: pageHeight,
customText: '',
inserted: false,
fieldMeta: advancedField ? result.data : undefined,
},
include: {
Recipient: true,
Expand Down

0 comments on commit 0829311

Please sign in to comment.