feat: Add support for uploading a file directly to the file provider from the client (#12224)

* feat: Add support for uploading a file directly to the file provider from the client

* fix: Add missing types and add a couple of module tests

* fix: Allow nested routes, add test for it
This commit is contained in:
Stevche Radevski
2025-04-18 10:22:00 +02:00
committed by GitHub
parent 6b1d8cd3d4
commit c4a0b63778
11 changed files with 314 additions and 5 deletions
@@ -27,6 +27,15 @@ export class FileProviderServiceFixtures extends AbstractFileProviderService {
return ""
}
async getPresignedUploadUrl(
fileData: FileTypes.ProviderGetPresignedUploadUrlDTO
): Promise<FileTypes.ProviderFileResultDTO> {
return {
url: "presigned-url/" + fileData.filename,
key: fileData.filename,
}
}
}
export const services = [FileProviderServiceFixtures]
@@ -63,6 +63,31 @@ moduleIntegrationTestRunner<IFileModuleService>({
const downloadUrl = await service.retrieveFile("test.jpg")
expect(await new Response(downloadUrl.url).text()).toEqual("test")
})
it("generates a presigned upload URL", async () => {
const res = await service.getUploadFileUrls({
filename: "test.jpg",
mimeType: "image/jpeg",
})
expect(res).toEqual({
url: "presigned-url/test.jpg",
key: "test.jpg",
})
})
it("fails to get a presigned upload URL if a filename isn't provided", async () => {
const err = await service
.getUploadFileUrls({
filename: "",
mimeType: "image/jpeg",
})
.catch((err) => err)
expect(err.message).toEqual(
"File name is required to get a presigned upload URL"
)
})
})
},
})
@@ -1,7 +1,9 @@
import {
Context,
CreateFileDTO,
GetUploadFileUrlDTO,
FileDTO,
UploadFileUrlDTO,
FileTypes,
FilterableFileProps,
FindConfig,
@@ -49,6 +51,27 @@ export default class FileModuleService implements FileTypes.IFileModuleService {
return Array.isArray(data) ? result : result[0]
}
getUploadFileUrls(
data: GetUploadFileUrlDTO[],
sharedContext?: Context
): Promise<UploadFileUrlDTO[]>
getUploadFileUrls(
data: GetUploadFileUrlDTO,
sharedContext?: Context
): Promise<UploadFileUrlDTO>
async getUploadFileUrls(
data: GetUploadFileUrlDTO[] | GetUploadFileUrlDTO
): Promise<UploadFileUrlDTO[] | UploadFileUrlDTO> {
const input = Array.isArray(data) ? data : [data]
const result = await Promise.all(
input.map((file) => this.fileProviderService_.getPresignedUploadUrl(file))
)
return Array.isArray(data) ? result : result[0]
}
async deleteFiles(ids: string[], sharedContext?: Context): Promise<void>
async deleteFiles(id: string, sharedContext?: Context): Promise<void>
async deleteFiles(ids: string[] | string): Promise<void> {
@@ -48,4 +48,24 @@ export default class FileProviderService {
): Promise<string> {
return this.fileProvider_.getPresignedDownloadUrl(fileData)
}
getPresignedUploadUrl(
fileData: FileTypes.ProviderGetPresignedUploadUrlDTO
): Promise<FileTypes.ProviderFileResultDTO> {
if (!this.fileProvider_.getPresignedUploadUrl) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Provider does not support presigned upload URLs"
)
}
if (!fileData.filename) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"File name is required to get a presigned upload URL"
)
}
return this.fileProvider_.getPresignedUploadUrl(fileData)
}
}