Feat(medusa-js, medusa-react): Upload endpoints in medusa js and react (#1716)
* export types from admin uploads * add delete and download to medusa-js * add upload endpoints to hooks * remove upload from js and react * pr feedback * Apply suggestions from code review Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> * rename types for admin uploads Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
1 parent
bf47d1aecd
commit
9018d270be
11 files changed
+187
-25
No files matched your search
@@ -1,14 +1,21 @@
|
|||||||
import { AdminUploadRes, IAdminPostUploadsFile } from "@medusajs/medusa"
|
import {
|
||||||
|
AdminDeleteUploadsReq,
|
||||||
|
IAdminPostUploadsFileReq,
|
||||||
|
AdminDeleteUploadsRes,
|
||||||
|
AdminPostUploadsDownloadUrlReq,
|
||||||
|
AdminUploadsDownloadUrlRes,
|
||||||
|
AdminUploadsRes,
|
||||||
|
} from "@medusajs/medusa"
|
||||||
|
import FormData from "form-data"
|
||||||
import { ResponsePromise } from "../../typings"
|
import { ResponsePromise } from "../../typings"
|
||||||
import BaseResource from "../base"
|
import BaseResource from "../base"
|
||||||
import FormData from "form-data"
|
|
||||||
|
|
||||||
class AdminUploadsResource extends BaseResource {
|
class AdminUploadsResource extends BaseResource {
|
||||||
private headers = {
|
private headers = {
|
||||||
"Content-Type": "multipart/form-data",
|
"Content-Type": "multipart/form-data",
|
||||||
}
|
}
|
||||||
|
|
||||||
create(file: IAdminPostUploadsFile): ResponsePromise<AdminUploadRes> {
|
create(file: IAdminPostUploadsFileReq): ResponsePromise<AdminUploadsRes> {
|
||||||
const path = `/admin/uploads`
|
const path = `/admin/uploads`
|
||||||
|
|
||||||
const payload = new FormData()
|
const payload = new FormData()
|
||||||
@@ -16,6 +23,24 @@ class AdminUploadsResource extends BaseResource {
|
|||||||
|
|
||||||
return this.client.request("POST", path, payload, {}, this.headers)
|
return this.client.request("POST", path, payload, {}, this.headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete(
|
||||||
|
payload: AdminDeleteUploadsReq,
|
||||||
|
customHeaders: Record<string, any> = {}
|
||||||
|
): ResponsePromise<AdminDeleteUploadsRes> {
|
||||||
|
const path = `/admin/uploads`
|
||||||
|
|
||||||
|
return this.client.request("DELETE", path, payload, {}, customHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
getPresignedDownloadUrl(
|
||||||
|
payload: AdminPostUploadsDownloadUrlReq,
|
||||||
|
customHeaders: Record<string, any> = {}
|
||||||
|
): ResponsePromise<AdminUploadsDownloadUrlRes> {
|
||||||
|
const path = `/admin/uploads/download-url`
|
||||||
|
|
||||||
|
return this.client.request("POST", path, payload, {}, customHeaders)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AdminUploadsResource
|
export default AdminUploadsResource
|
||||||
@@ -1258,6 +1258,9 @@
|
|||||||
"updated_at": "2021-03-16T21:24:00.389Z",
|
"updated_at": "2021-03-16T21:24:00.389Z",
|
||||||
"created_at": "2021-03-16T21:24:00.389Z",
|
"created_at": "2021-03-16T21:24:00.389Z",
|
||||||
"deleted_at": null
|
"deleted_at": null
|
||||||
|
},
|
||||||
|
"upload": {
|
||||||
|
"url": "test-url"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { rest } from "msw"
|
import { rest } from "msw"
|
||||||
|
import { body } from "msw/lib/types/context"
|
||||||
import { fixtures } from "../data"
|
import { fixtures } from "../data"
|
||||||
|
|
||||||
export const adminHandlers = [
|
export const adminHandlers = [
|
||||||
@@ -1651,4 +1652,24 @@ export const adminHandlers = [
|
|||||||
rest.delete("/admin/auth", (req, res, ctx) => {
|
rest.delete("/admin/auth", (req, res, ctx) => {
|
||||||
return res(ctx.status(200))
|
return res(ctx.status(200))
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
rest.delete("/admin/uploads", (req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
id: (req.body as any).file_key,
|
||||||
|
object: "file",
|
||||||
|
deleted: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
rest.post("/admin/uploads/download-url", (req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
download_url: fixtures.get("upload").url,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
]
|
]
|
||||||
@@ -25,3 +25,4 @@ export * from "./swaps"
|
|||||||
export * from "./tax-rates"
|
export * from "./tax-rates"
|
||||||
export * from "./users"
|
export * from "./users"
|
||||||
export * from "./variants"
|
export * from "./variants"
|
||||||
|
export * from "./uploads"
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./mutations"
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import {
|
||||||
|
AdminDeleteUploadsReq,
|
||||||
|
IAdminPostUploadsFileReq,
|
||||||
|
AdminDeleteUploadsRes,
|
||||||
|
AdminPostUploadsDownloadUrlReq,
|
||||||
|
AdminUploadsDownloadUrlRes,
|
||||||
|
AdminUploadsRes,
|
||||||
|
} from "@medusajs/medusa"
|
||||||
|
import { Response } from "@medusajs/medusa-js"
|
||||||
|
import { useMutation, UseMutationOptions, useQueryClient } from "react-query"
|
||||||
|
import { useMedusa } from "../../../contexts"
|
||||||
|
import { buildOptions } from "../../utils/buildOptions"
|
||||||
|
|
||||||
|
export const useAdminUploadFile = (
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
Response<AdminUploadsRes>,
|
||||||
|
Error,
|
||||||
|
IAdminPostUploadsFileReq
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation((payload: IAdminPostUploadsFileReq) => {
|
||||||
|
return client.admin.uploads.create(payload)
|
||||||
|
}, buildOptions(queryClient, [], options))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAdminCreatePresignedDownloadUrl = (
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
Response<AdminUploadsDownloadUrlRes>,
|
||||||
|
Error,
|
||||||
|
AdminPostUploadsDownloadUrlReq
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
(payload: AdminPostUploadsDownloadUrlReq) =>
|
||||||
|
client.admin.uploads.getPresignedDownloadUrl(payload),
|
||||||
|
buildOptions(queryClient, [], options)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAdminDeleteFile = (
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
Response<AdminDeleteUploadsRes>,
|
||||||
|
Error,
|
||||||
|
AdminDeleteUploadsReq
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
(payload: AdminDeleteUploadsReq) => client.admin.uploads.delete(payload),
|
||||||
|
buildOptions(queryClient, [], options)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { renderHook } from "@testing-library/react-hooks"
|
||||||
|
import { fixtures } from "../../../../mocks/data"
|
||||||
|
import {
|
||||||
|
useAdminDeleteFile,
|
||||||
|
useAdminCreatePresignedDownloadUrl,
|
||||||
|
} from "../../../../src"
|
||||||
|
import { createWrapper } from "../../../utils"
|
||||||
|
|
||||||
|
describe("useAdminDeleteFile hook", () => {
|
||||||
|
test("Removes file with key and returns deleteresult", async () => {
|
||||||
|
const file_key = "test"
|
||||||
|
|
||||||
|
const { result, waitFor } = renderHook(() => useAdminDeleteFile(), {
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
})
|
||||||
|
|
||||||
|
result.current.mutate({ file_key })
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.data.response.status).toEqual(200)
|
||||||
|
expect(result.current.data).toEqual(
|
||||||
|
expect.objectContaining({ id: file_key, object: "file", deleted: true })
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("useAdminCreatePresignedDownloadUrl hook", () => {
|
||||||
|
test("", async () => {
|
||||||
|
const file_key = "test"
|
||||||
|
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useAdminCreatePresignedDownloadUrl(),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
result.current.mutate({ file_key })
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.data.response.status).toEqual(200)
|
||||||
|
expect(result.current.data.download_url).toEqual(fixtures.get("upload").url)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -37,7 +37,7 @@ export default async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IAdminPostUploadsFile {
|
export class IAdminPostUploadsFileReq {
|
||||||
originalName: string
|
originalName: string
|
||||||
path: string
|
path: string
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
import { IsString } from "class-validator"
|
import { IsString } from "class-validator"
|
||||||
import { validator } from "../../../../utils/validator"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [delete] /uploads
|
* [delete] /uploads
|
||||||
* operationId: "AdminDeleteUpload"
|
* operationId: "AdminDeleteUploads"
|
||||||
* summary: "Removes an uploaded file"
|
* summary: "Removes an uploaded file"
|
||||||
* description: "Removes an uploaded file using the installed fileservice"
|
* description: "Removes an uploaded file using the installed fileservice"
|
||||||
* x-authenticated: true
|
* x-authenticated: true
|
||||||
@@ -14,9 +13,7 @@ import { validator } from "../../../../utils/validator"
|
|||||||
* description: OK
|
* description: OK
|
||||||
*/
|
*/
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const validated = await validator(AdminDeleteUploadReq, req.body, {
|
const validated = req.validatedBody as AdminDeleteUploadsReq
|
||||||
forbidUnknownValues: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const fileService = req.scope.resolve("fileService")
|
const fileService = req.scope.resolve("fileService")
|
||||||
|
|
||||||
@@ -27,7 +24,7 @@ export default async (req, res) => {
|
|||||||
.send({ id: validated.file_key, object: "file", deleted: true })
|
.send({ id: validated.file_key, object: "file", deleted: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
class AdminDeleteUploadReq {
|
export class AdminDeleteUploadsReq {
|
||||||
@IsString()
|
@IsString()
|
||||||
file_key: string
|
file_key: string
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
import { IsString } from "class-validator"
|
import { IsString } from "class-validator"
|
||||||
import { AbstractFileService } from "../../../../interfaces"
|
import { AbstractFileService } from "../../../../interfaces"
|
||||||
import { validator } from "../../../../utils/validator"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [get] /uploads
|
* [post] /uploads/download-url
|
||||||
* operationId: "GetUploadsFileDownloadUrl"
|
* operationId: "PostUploadsDownloadUrl"
|
||||||
* summary: "Gets a presigned download url for a file"
|
* summary: "Creates a presigned download url for a file"
|
||||||
* description: "Gets a presigned download url for a file"
|
* description: "Creates a presigned download url for a file"
|
||||||
* x-authenticated: true
|
* x-authenticated: true
|
||||||
* requestBody:
|
* requestBody:
|
||||||
* content:
|
* content:
|
||||||
@@ -26,18 +25,16 @@ import { validator } from "../../../../utils/validator"
|
|||||||
* description: OK
|
* description: OK
|
||||||
*/
|
*/
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const validated = await validator(AdminGetUploadsFileDownloadUrlReq, req.body)
|
|
||||||
|
|
||||||
const fileService: AbstractFileService<any> = req.scope.resolve("fileService")
|
const fileService: AbstractFileService<any> = req.scope.resolve("fileService")
|
||||||
|
|
||||||
const url = await fileService.getPresignedDownloadUrl({
|
const url = await fileService.getPresignedDownloadUrl({
|
||||||
fileKey: validated.file_key,
|
fileKey: (req.validatedBody as AdminPostUploadsDownloadUrlReq).file_key,
|
||||||
})
|
})
|
||||||
|
|
||||||
res.status(200).send({ download_url: url })
|
res.status(200).send({ download_url: url })
|
||||||
}
|
}
|
||||||
|
|
||||||
class AdminGetUploadsFileDownloadUrlReq {
|
export class AdminPostUploadsDownloadUrlReq {
|
||||||
@IsString()
|
@IsString()
|
||||||
file_key: string
|
file_key: string
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,9 @@ import { Router } from "express"
|
|||||||
import multer from "multer"
|
import multer from "multer"
|
||||||
import { DeleteResponse } from "../../../../types/common"
|
import { DeleteResponse } from "../../../../types/common"
|
||||||
|
|
||||||
import middlewares from "../../../middlewares"
|
import middlewares, { transformBody } from "../../../middlewares"
|
||||||
|
import { AdminDeleteUploadsReq } from "./delete-upload"
|
||||||
|
import { AdminPostUploadsDownloadUrlReq } from "./get-download-url"
|
||||||
|
|
||||||
const route = Router()
|
const route = Router()
|
||||||
const upload = multer({ dest: "uploads/" })
|
const upload = multer({ dest: "uploads/" })
|
||||||
@@ -16,21 +18,30 @@ export default (app) => {
|
|||||||
middlewares.wrap(require("./create-upload").default)
|
middlewares.wrap(require("./create-upload").default)
|
||||||
)
|
)
|
||||||
|
|
||||||
route.delete("/", middlewares.wrap(require("./delete-upload").default))
|
route.delete(
|
||||||
|
"/",
|
||||||
|
transformBody(AdminDeleteUploadsReq),
|
||||||
|
middlewares.wrap(require("./delete-upload").default)
|
||||||
|
)
|
||||||
|
|
||||||
route.get(
|
route.post(
|
||||||
"/download-url",
|
"/download-url",
|
||||||
|
transformBody(AdminPostUploadsDownloadUrlReq),
|
||||||
middlewares.wrap(require("./get-download-url").default)
|
middlewares.wrap(require("./get-download-url").default)
|
||||||
)
|
)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AdminUploadRes = {
|
export type AdminUploadsRes = {
|
||||||
uploads: unknown[]
|
uploads: { url: string }[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AdminDeleteUploadRes = DeleteResponse
|
export type AdminDeleteUploadsRes = DeleteResponse
|
||||||
|
|
||||||
|
export type AdminUploadsDownloadUrlRes = {
|
||||||
|
download_url: string
|
||||||
|
}
|
||||||
|
|
||||||
export * from "./create-upload"
|
export * from "./create-upload"
|
||||||
export * from "./delete-upload"
|
export * from "./delete-upload"
|
||||||
|
|||||||
Reference in new issue
Block a user