Skip to content

Commit

Permalink
Create a new object for upload
Browse files Browse the repository at this point in the history
  • Loading branch information
cregourd committed Jun 26, 2024
1 parent f571490 commit 8104c6d
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 15 deletions.
6 changes: 3 additions & 3 deletions apps/docs/pages/docs/api-docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ For the `edit` property, it can take the following:
| `input` | a React Element that should receive [CustomInputProps](#custominputprops). For App Router, this element must be a client component. |
| `handler` | an object that can take the following properties |
| `handler.get` | a function that takes the field value as a parameter and returns a transformed value displayed in the form |
| `handler.upload` | an async function that is used only for formats `file` and `data-url`. It takes a File object as parameter and must return a string. Useful to upload a file to a remote provider |
| `handler.upload` | an async function that is used only for formats `file` and `data-url`. It takes a Buffer object and and informations object containing `name` and `type` property as parameter and must return a string. Useful to upload a file to a remote provider |
| `handler.uploadErrorMessage` | an optional string displayed in the input field as an error message in case of a failure during the upload handler |
| `optionFormatter` | only for relation fields, a function that takes the field values as a parameter and returns a string. Useful to display your record in related list |
| `tooltip` | a tooltip content to show for the field |
Expand All @@ -299,7 +299,7 @@ For the `edit` property, it can take the following:
| `display` | only for relation fields, indicate which display format to use between `list`, `table` or `select`. Default `select` |
| `required` | a true value to force a field to be required in the form, note that if the field is required by the Prisma schema, you cannot set `required` to false |
| `relationOptionFormatter` | same as `optionFormatter`, but used to format data that comes from an [explicit many-to-many](https://www.prisma.io/docs/orm/prisma-schema/data-model/relations/many-to-many-relations#explicit-many-to-many-relations) relationship. See [handling explicit many-to-many](/docs/code-snippets#explicit-many-to-many) |
| `orderField` | the field to use for relationship sorting. This allow to drag and drop the related records in the `list` display. |
| `orderField` | the field to use for relationship sorting. This allow to drag and drop the related records in the `list` display. |
| `relationshipSearchField` | a field name of the explicit many-to-many relation table to apply the search on. See [handling explicit many-to-many](/docs/code-snippets#explicit-many-to-many) |

##### `filters` property
Expand Down Expand Up @@ -430,7 +430,7 @@ const options: NextAdminOptions = {
avatar: {
format: "file",
handler: {
upload: async (file: File) => {
upload: async (buffer, infos) => {
return "https://www.gravatar.com/avatar/00000000000000000000000000000000";
},
},
Expand Down
2 changes: 1 addition & 1 deletion apps/example/options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export const options: NextAdminOptions = {
* for example you can upload the file to an S3 bucket.
* Make sure to return a string.
*/
upload: async (buffer: Buffer) => {
upload: async (buffer, infos) => {
return "https://www.gravatar.com/avatar/00000000000000000000000000000000";
},
},
Expand Down
2 changes: 1 addition & 1 deletion apps/example/pageRouterOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export const options: NextAdminOptions = {
* for example you can upload the file to an S3 bucket.
* Make sure to return a string.
*/
upload: async (file: Buffer) => {
upload: async (file, infos) => {
return "https://www.gravatar.com/avatar/00000000000000000000000000000000";
},
},
Expand Down
12 changes: 11 additions & 1 deletion packages/next-admin/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,13 +239,23 @@ export type Handler<
* @param file
* @returns
*/
upload?: (file: Buffer) => Promise<string>;
upload?: (buffer: Buffer, infos: {
name: string;
type: string | null;
}) => Promise<string>;
/**
* an optional string displayed in the input field as an error message in case of a failure during the upload handler.
*/
uploadErrorMessage?: string;
};

export type UploadParameters = Parameters<(buffer: Buffer, infos: {
name: string;
type: string | null;
}) => Promise<string>>



export type RichTextFormat = "html" | "json";

export type FormatOptions<T> = T extends string
Expand Down
25 changes: 16 additions & 9 deletions packages/next-admin/src/utils/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ import {
ObjectField,
ScalarField,
Schema,
UploadParameters,
} from "../types";
import { isNativeFunction, pipe } from "./tools";
import { isNativeFunction, isUploadParameters, pipe } from "./tools";

export const models: readonly Prisma.DMMF.Model[] = Prisma.dmmf.datamodel
.models as Prisma.DMMF.Model[];
Expand Down Expand Up @@ -757,7 +758,7 @@ export const formattedFormData = async <M extends ModelName>(
["data-url", "file"].includes(
editOptions?.[dmmfPropertyName]?.format ?? ""
) &&
formData[dmmfPropertyName] instanceof Buffer
isUploadParameters(formData[dmmfPropertyName])
) {
const uploadHandler =
editOptions?.[dmmfPropertyName]?.handler?.upload;
Expand All @@ -771,7 +772,7 @@ export const formattedFormData = async <M extends ModelName>(
} else {
try {
const uploadResult = await uploadHandler(
formData[dmmfPropertyName] as unknown as Buffer
...(formData[dmmfPropertyName] as unknown as UploadParameters)
);
if (typeof uploadResult !== "string") {
console.warn(
Expand Down Expand Up @@ -1002,9 +1003,9 @@ export const getFormDataValues = async (req: IncomingMessage) => {
});
},
});
return new Promise<Record<string, string | File | null>>(
return new Promise<Record<string, string | UploadParameters | null>>(
(resolve, reject) => {
const files = {} as Record<string, File[] | [null]>;
const files = {} as Record<string, UploadParameters[] | [null]>;

form.on("fileBegin", (name, file) => {
// @ts-expect-error
Expand All @@ -1019,7 +1020,10 @@ export const getFormDataValues = async (req: IncomingMessage) => {
if (!file.originalFilename) {
files[name] = [null];
} else {
files[name] = [new File([Buffer.concat(chunks)], file.originalFilename)];
files[name] = [[Buffer.concat(chunks), {
name: file.originalFilename,
type: file.mimetype,
}]];
}
callback();
},
Expand All @@ -1038,7 +1042,7 @@ export const getFormDataValues = async (req: IncomingMessage) => {
}
return acc;
},
{} as Record<string, string | File | null>
{} as Record<string, string | UploadParameters | null>
);
resolve(joinedFormData);
});
Expand All @@ -1055,7 +1059,7 @@ export const getFormValuesFromFormData = async (formData: FormData) => {
tmpFormValues[key] = val;
});

const formValues = {} as Record<string, string | Buffer | null>;
const formValues = {} as Record<string, string | UploadParameters | null>;

await Promise.allSettled(
Object.entries(tmpFormValues).map(async ([key, value]) => {
Expand All @@ -1066,7 +1070,10 @@ export const getFormValuesFromFormData = async (formData: FormData) => {
return;
}
const buffer = await file.arrayBuffer();
formValues[key] = Buffer.from(buffer);
formValues[key] = [Buffer.from(buffer), {
name: file.name,
type: file.type,
}];
} else {
formValues[key] = value as string;
}
Expand Down
13 changes: 13 additions & 0 deletions packages/next-admin/src/utils/tools.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { UploadParameters } from "../types";

export const capitalize = <T extends string>(str: T): Capitalize<T> => {
let capitalizedStr = str.charAt(0).toLocaleUpperCase() + str.slice(1);
return capitalizedStr as Capitalize<T>;
Expand Down Expand Up @@ -63,3 +65,14 @@ export const formatLabel = (label: string) => {

return capitalize(spacedLabel.toLowerCase());
};

//Create a function that check if object satifies UploadParameters
export const isUploadParameters = (obj: any): obj is UploadParameters => {
return (
obj.length === 2 &&
Buffer.isBuffer(obj[0]) &&
typeof obj[1] === 'object' &&
'name' in obj[1] &&
'type' in obj[1]
);
}

0 comments on commit 8104c6d

Please sign in to comment.