feat: implement direct upload (#12328)

* feat: implement direct upload

* feat: add direct-upload endpoint

* refactor: implement feedback

* refactor: have a dedicated endpoint for direct uploads

* refactor: convert responses to snakecase

* refactor: rename method to createImport

* test: add tests for the presigned-urls endpoint
This commit is contained in:
Harminder Virk
2025-05-04 16:28:10 +05:30
committed by GitHub
parent ed55d17a36
commit 84f58f9058
22 changed files with 537 additions and 129 deletions

View File

@@ -0,0 +1,5 @@
Product Id,Product Handle,Product Title,Product Status,Product Description,Product Subtitle,Product External Id,Product Thumbnail,Product Collection Id,Product Type Id,Product Category 1,Product Created At,Product Deleted At,Product Discountable,Product Height,Product Hs Code,Product Image 1,Product Image 2,Product Is Giftcard,Product Length,Product Material,Product Mid Code,Product Origin Country,Product Tag 1,Product Tag 2,Product Updated At,Product Weight,Product Width,Variant Id,Variant Title,Variant Sku,Variant Upc,Variant Ean,Variant Hs Code,Variant Mid Code,Variant Manage Inventory,Variant Allow Backorder,Variant Barcode,Variant Created At,Variant Deleted At,Variant Height,Variant Length,Variant Material,Variant Metadata,Variant Option 1 Name,Variant Option 1 Value,Variant Option 2 Name,Variant Option 2 Value,Variant Origin Country,Variant Price DKK,Variant Price EUR,Variant Price USD,Variant Product Id,Variant Updated At,Variant Variant Rank,Variant Weight,Variant Width,Shipping Profile Id
prod_01J44RRKH4HH2SANJ0S05YM853,base-product,Base product,draft,"test-product-description
test line 2",,,test-image.png,pcol_01J44RRKG4P1RZ3CKAMBS760TD,ptyp_01J44RRKGHPQMJ1CRMW7MEN3P1,pcat_01J44RRKGS2N86A8V37ZJD877K,2024-07-31T16:07:55.681Z,,true,,,test-image.png,test-image-2.png,false,,,,,123,456,2024-07-31T16:07:55.681Z,,,variant_01J44RRKHRQ7WJ902X4936GEK7,Test variant,,,,,,true,false,,2024-07-31T16:07:55.704Z,,,,,,size,large,color,green,,30,45,100,prod_01J44RRKH4HH2SANJ0S05YM853,2024-07-31T16:07:55.704Z,0,,,import-shipping-profile
prod_01J44RRKH4HH2SANJ0S05YM853,base-product,Base product,draft,"test-product-description
test line 2",,,test-image.png,pcol_01J44RRKG4P1RZ3CKAMBS760TD,ptyp_01J44RRKGHPQMJ1CRMW7MEN3P1,pcat_01J44RRKGS2N86A8V37ZJD877K,2024-07-31T16:07:55.681Z,,true,,,test-image.png,test-image-2.png,false,,,,,123,456,2024-07-31T16:07:55.681Z,,,variant_01J44RRKHRAYY7Q7NTXNNMDA1S,Test variant 2,,,,,,true,false,,2024-07-31T16:07:55.704Z,,,,,,size,small,color,green,,50,65,200,prod_01J44RRKH4HH2SANJ0S05YM853,2024-07-31T16:07:55.704Z,0,,,import-shipping-profile
1 Product Id Product Handle Product Title Product Status Product Description Product Subtitle Product External Id Product Thumbnail Product Collection Id Product Type Id Product Category 1 Product Created At Product Deleted At Product Discountable Product Height Product Hs Code Product Image 1 Product Image 2 Product Is Giftcard Product Length Product Material Product Mid Code Product Origin Country Product Tag 1 Product Tag 2 Product Updated At Product Weight Product Width Variant Id Variant Title Variant Sku Variant Upc Variant Ean Variant Hs Code Variant Mid Code Variant Manage Inventory Variant Allow Backorder Variant Barcode Variant Created At Variant Deleted At Variant Height Variant Length Variant Material Variant Metadata Variant Option 1 Name Variant Option 1 Value Variant Option 2 Name Variant Option 2 Value Variant Origin Country Variant Price DKK Variant Price EUR Variant Price USD Variant Product Id Variant Updated At Variant Variant Rank Variant Weight Variant Width Shipping Profile Id
2 prod_01J44RRKH4HH2SANJ0S05YM853 base-product Base product draft test-product-description test line 2 test-image.png pcol_01J44RRKG4P1RZ3CKAMBS760TD ptyp_01J44RRKGHPQMJ1CRMW7MEN3P1 pcat_01J44RRKGS2N86A8V37ZJD877K 2024-07-31T16:07:55.681Z true test-image.png test-image-2.png false 123 456 2024-07-31T16:07:55.681Z variant_01J44RRKHRQ7WJ902X4936GEK7 Test variant true false 2024-07-31T16:07:55.704Z size large color green 30 45 100 prod_01J44RRKH4HH2SANJ0S05YM853 2024-07-31T16:07:55.704Z 0 import-shipping-profile
3 prod_01J44RRKH4HH2SANJ0S05YM853 base-product Base product draft test-product-description test line 2 test-image.png pcol_01J44RRKG4P1RZ3CKAMBS760TD ptyp_01J44RRKGHPQMJ1CRMW7MEN3P1 pcat_01J44RRKGS2N86A8V37ZJD877K 2024-07-31T16:07:55.681Z true test-image.png test-image-2.png false 123 456 2024-07-31T16:07:55.681Z variant_01J44RRKHRAYY7Q7NTXNNMDA1S Test variant 2 true false 2024-07-31T16:07:55.704Z size small color green 50 65 200 prod_01J44RRKH4HH2SANJ0S05YM853 2024-07-31T16:07:55.704Z 0 import-shipping-profile

View File

@@ -0,0 +1,84 @@
import { join } from "path"
import { readFile } from "fs/promises"
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
import {
adminHeaders,
createAdminUser,
} from "../../../../helpers/create-admin-user"
import { AdminUploadPreSignedUrlRequest } from "@medusajs/types"
jest.setTimeout(30000)
const PRODUCTS_FILE_PATH = join(__dirname, "./__fixtures__", "products.csv")
const getUploadReq = (file: File) => {
return {
body: {
mime_type: file.type,
originalname: file.name,
size: file.size,
access: "public",
} satisfies AdminUploadPreSignedUrlRequest,
meta: {
headers: {
...adminHeaders.headers,
},
},
}
}
medusaIntegrationTestRunner({
testSuite: ({ dbConnection, getContainer, api }) => {
beforeEach(async () => {
await createAdminUser(dbConnection, adminHeaders, getContainer())
})
describe("POST /admin/uploads/presigned-urls", () => {
it("should generate a signed URL to upload a file", async () => {
const file = new File(
[await readFile(PRODUCTS_FILE_PATH)],
"products.csv",
{
type: "text/csv",
}
)
const { body, meta } = getUploadReq(file)
const response = await api.post(
"/admin/uploads/presigned-urls",
body,
meta
)
expect(response.data).toEqual(
expect.objectContaining({
filename: expect.stringContaining(".csv"),
extension: "csv",
mime_type: "text/csv",
size: file.size,
url: expect.stringContaining(response.data.filename),
})
)
expect(response.status).toEqual(200)
})
it("should return error when mime type is invalid", async () => {
const file = new File(
[await readFile(PRODUCTS_FILE_PATH)],
"products.csv"
)
const { body, meta } = getUploadReq(file)
try {
await api.post("/admin/uploads/presigned-urls", body, meta)
} catch (error) {
expect(error.response.data).toEqual({
code: "invalid_data",
type: "invalid_data",
message: 'Invalid file type ""',
})
expect(error.response.status).toEqual(400)
}
})
})
},
})