-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add product export endpoint and a dummy workflow #8178
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,398 @@ | ||
import { medusaIntegrationTestRunner } from "medusa-test-utils" | ||
import { | ||
adminHeaders, | ||
createAdminUser, | ||
} from "../../../../helpers/create-admin-user" | ||
import { getProductFixture } from "../../../../helpers/fixtures" | ||
|
||
jest.setTimeout(50000) | ||
|
||
medusaIntegrationTestRunner({ | ||
testSuite: ({ dbConnection, getContainer, api }) => { | ||
let baseProduct | ||
let proposedProduct | ||
|
||
let baseCollection | ||
let publishedCollection | ||
|
||
let baseType | ||
|
||
beforeEach(async () => { | ||
await createAdminUser(dbConnection, adminHeaders, getContainer()) | ||
|
||
baseCollection = ( | ||
await api.post( | ||
"/admin/collections", | ||
{ title: "base-collection" }, | ||
adminHeaders | ||
) | ||
).data.collection | ||
|
||
publishedCollection = ( | ||
await api.post( | ||
"/admin/collections", | ||
{ title: "proposed-collection" }, | ||
adminHeaders | ||
) | ||
).data.collection | ||
|
||
baseType = ( | ||
await api.post( | ||
"/admin/product-types", | ||
{ value: "test-type" }, | ||
adminHeaders | ||
) | ||
).data.product_type | ||
|
||
baseProduct = ( | ||
await api.post( | ||
"/admin/products", | ||
getProductFixture({ | ||
title: "Base product", | ||
collection_id: baseCollection.id, | ||
type_id: baseType.id, | ||
}), | ||
adminHeaders | ||
) | ||
).data.product | ||
|
||
proposedProduct = ( | ||
await api.post( | ||
"/admin/products", | ||
getProductFixture({ | ||
title: "Proposed product", | ||
status: "proposed", | ||
tags: [{ value: "new-tag" }], | ||
type_id: baseType.id, | ||
}), | ||
adminHeaders | ||
) | ||
).data.product | ||
}) | ||
|
||
describe("POST /admin/products/export", () => { | ||
it("should export a csv file containing the expected products", async () => { | ||
// BREAKING: The batch endpoints moved to the domain routes (admin/batch-jobs -> /admin/products/export). The payload and response changed as well. | ||
const batchJobRes = await api.post( | ||
"/admin/products/export", | ||
{}, | ||
adminHeaders | ||
) | ||
|
||
const workflowId = batchJobRes.data.workflow_id | ||
expect(workflowId).toBeTruthy() | ||
|
||
// Pull to check the status until it is completed | ||
while (true) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. General comment from someone that is not aware of all the underlying mechanism and not only valid for the test here: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah agreed, I just copy-pasted the tests from the original batch tests, and I will be changing them as I am implementing the exports. I'll try using what you suggested instead of the while loop 👍 |
||
// const res = await api.get( | ||
// `/admin/batch-jobs/${batchJobId}`, | ||
// adminReqConfig | ||
// ) | ||
// await new Promise((resolve, _) => { | ||
// setTimeout(resolve, 1000) | ||
// }) | ||
// batchJob = res.data.batch_job | ||
// shouldContinuePulling = !( | ||
// batchJob.status === "completed" || batchJob.status === "failed" | ||
// ) | ||
break | ||
} | ||
|
||
// expect(batchJob.status).toBe("completed") | ||
|
||
// exportFilePath = path.resolve(__dirname, batchJob.result.file_key) | ||
// const isFileExists = (await fs.stat(exportFilePath)).isFile() | ||
|
||
// expect(isFileExists).toBeTruthy() | ||
|
||
// const fileSize = (await fs.stat(exportFilePath)).size | ||
// expect(batchJob.result?.file_size).toBe(fileSize) | ||
|
||
// const data = (await fs.readFile(exportFilePath)).toString() | ||
// const [, ...lines] = data.split("\r\n").filter((l) => l) | ||
|
||
// expect(lines.length).toBe(1) | ||
|
||
// const lineColumn = lines[0].split(";") | ||
|
||
// expect(lineColumn[0]).toBe(productId) | ||
// expect(lineColumn[2]).toBe(productPayload.title) | ||
// expect(lineColumn[4]).toBe(productPayload.description) | ||
// expect(lineColumn[23]).toBe(variantId) | ||
// expect(lineColumn[24]).toBe(productPayload.variants[0].title) | ||
// expect(lineColumn[25]).toBe(productPayload.variants[0].sku) | ||
}) | ||
|
||
// it("should export a csv file containing the expected products including new line char in the cells", async () => { | ||
// const api = useApi() | ||
|
||
// const productPayload = { | ||
// title: "Test export product", | ||
// description: "test-product-description\ntest line 2", | ||
// type: { value: "test-type" }, | ||
// images: ["test-image.png", "test-image-2.png"], | ||
// collection_id: "test-collection", | ||
// tags: [{ value: "123" }, { value: "456" }], | ||
// options: [{ title: "size" }, { title: "color" }], | ||
// variants: [ | ||
// { | ||
// title: "Test variant", | ||
// inventory_quantity: 10, | ||
// sku: "test-variant-sku-product-export", | ||
// prices: [ | ||
// { | ||
// currency_code: "usd", | ||
// amount: 100, | ||
// }, | ||
// { | ||
// currency_code: "eur", | ||
// amount: 45, | ||
// }, | ||
// { | ||
// currency_code: "dkk", | ||
// amount: 30, | ||
// }, | ||
// ], | ||
// options: [{ value: "large" }, { value: "green" }], | ||
// }, | ||
// ], | ||
// } | ||
// const createProductRes = await api.post( | ||
// "/admin/products", | ||
// productPayload, | ||
// adminReqConfig | ||
// ) | ||
// const productId = createProductRes.data.product.id | ||
// const variantId = createProductRes.data.product.variants[0].id | ||
|
||
// const batchPayload = { | ||
// type: "product-export", | ||
// context: { | ||
// filterable_fields: { | ||
// title: "Test export product", | ||
// }, | ||
// }, | ||
// } | ||
// const batchJobRes = await api.post( | ||
// "/admin/batch-jobs", | ||
// batchPayload, | ||
// adminReqConfig | ||
// ) | ||
// const batchJobId = batchJobRes.data.batch_job.id | ||
|
||
// expect(batchJobId).toBeTruthy() | ||
|
||
// // Pull to check the status until it is completed | ||
// let batchJob | ||
// let shouldContinuePulling = true | ||
// while (shouldContinuePulling) { | ||
// const res = await api.get( | ||
// `/admin/batch-jobs/${batchJobId}`, | ||
// adminReqConfig | ||
// ) | ||
|
||
// await new Promise((resolve, _) => { | ||
// setTimeout(resolve, 1000) | ||
// }) | ||
|
||
// batchJob = res.data.batch_job | ||
// shouldContinuePulling = !( | ||
// batchJob.status === "completed" || batchJob.status === "failed" | ||
// ) | ||
// } | ||
|
||
// expect(batchJob.status).toBe("completed") | ||
|
||
// exportFilePath = path.resolve(__dirname, batchJob.result.file_key) | ||
// const isFileExists = (await fs.stat(exportFilePath)).isFile() | ||
|
||
// expect(isFileExists).toBeTruthy() | ||
|
||
// const fileSize = (await fs.stat(exportFilePath)).size | ||
// expect(batchJob.result?.file_size).toBe(fileSize) | ||
|
||
// const data = (await fs.readFile(exportFilePath)).toString() | ||
// const [, ...lines] = data.split("\r\n").filter((l) => l) | ||
|
||
// expect(lines.length).toBe(1) | ||
|
||
// const lineColumn = lines[0].split(";") | ||
|
||
// expect(lineColumn[0]).toBe(productId) | ||
// expect(lineColumn[2]).toBe(productPayload.title) | ||
// expect(lineColumn[4]).toBe(`"${productPayload.description}"`) | ||
// expect(lineColumn[23]).toBe(variantId) | ||
// expect(lineColumn[24]).toBe(productPayload.variants[0].title) | ||
// expect(lineColumn[25]).toBe(productPayload.variants[0].sku) | ||
// }) | ||
|
||
// it("should export a csv file containing a limited number of products", async () => { | ||
// const api = useApi() | ||
|
||
// const batchPayload = { | ||
// type: "product-export", | ||
// context: { | ||
// batch_size: 1, | ||
// filterable_fields: { collection_id: "test-collection" }, | ||
// order: "created_at", | ||
// }, | ||
// } | ||
|
||
// const batchJobRes = await api.post( | ||
// "/admin/batch-jobs", | ||
// batchPayload, | ||
// adminReqConfig | ||
// ) | ||
// const batchJobId = batchJobRes.data.batch_job.id | ||
|
||
// expect(batchJobId).toBeTruthy() | ||
|
||
// // Pull to check the status until it is completed | ||
// let batchJob | ||
// let shouldContinuePulling = true | ||
// while (shouldContinuePulling) { | ||
// const res = await api.get( | ||
// `/admin/batch-jobs/${batchJobId}`, | ||
// adminReqConfig | ||
// ) | ||
|
||
// await new Promise((resolve, _) => { | ||
// setTimeout(resolve, 1000) | ||
// }) | ||
|
||
// batchJob = res.data.batch_job | ||
// shouldContinuePulling = !( | ||
// batchJob.status === "completed" || batchJob.status === "failed" | ||
// ) | ||
// } | ||
|
||
// expect(batchJob.status).toBe("completed") | ||
|
||
// exportFilePath = path.resolve(__dirname, batchJob.result.file_key) | ||
// const isFileExists = (await fs.stat(exportFilePath)).isFile() | ||
|
||
// expect(isFileExists).toBeTruthy() | ||
|
||
// const data = (await fs.readFile(exportFilePath)).toString() | ||
// const [, ...lines] = data.split("\r\n").filter((l) => l) | ||
|
||
// expect(lines.length).toBe(4) | ||
|
||
// const csvLine = lines[0].split(";") | ||
// expect(csvLine[0]).toBe("test-product") | ||
// }) | ||
|
||
// it("should be able to import an exported csv file", async () => { | ||
// const api = useApi() | ||
|
||
// const batchPayload = { | ||
// type: "product-export", | ||
// context: { | ||
// batch_size: 1, | ||
// filterable_fields: { collection_id: "test-collection" }, | ||
// order: "created_at", | ||
// }, | ||
// } | ||
|
||
// const batchJobRes = await api.post( | ||
// "/admin/batch-jobs", | ||
// batchPayload, | ||
// adminReqConfig | ||
// ) | ||
// let batchJobId = batchJobRes.data.batch_job.id | ||
|
||
// expect(batchJobId).toBeTruthy() | ||
|
||
// // Pull to check the status until it is completed | ||
// let batchJob | ||
// let shouldContinuePulling = true | ||
// while (shouldContinuePulling) { | ||
// const res = await api.get( | ||
// `/admin/batch-jobs/${batchJobId}`, | ||
// adminReqConfig | ||
// ) | ||
|
||
// await new Promise((resolve, _) => { | ||
// setTimeout(resolve, 1000) | ||
// }) | ||
|
||
// batchJob = res.data.batch_job | ||
// shouldContinuePulling = !( | ||
// batchJob.status === "completed" || batchJob.status === "failed" | ||
// ) | ||
// } | ||
|
||
// expect(batchJob.status).toBe("completed") | ||
|
||
// exportFilePath = path.resolve(__dirname, batchJob.result.file_key) | ||
// const isFileExists = (await fs.stat(exportFilePath)).isFile() | ||
|
||
// expect(isFileExists).toBeTruthy() | ||
|
||
// const data = (await fs.readFile(exportFilePath)).toString() | ||
// const [header, ...lines] = data.split("\r\n").filter((l) => l) | ||
|
||
// expect(lines.length).toBe(4) | ||
|
||
// const csvLine = lines[0].split(";") | ||
// expect(csvLine[0]).toBe("test-product") | ||
// expect(csvLine[2]).toBe("Test product") | ||
|
||
// csvLine[2] = "Updated test product" | ||
// lines.splice(0, 1, csvLine.join(";")) | ||
|
||
// await fs.writeFile(exportFilePath, [header, ...lines].join("\r\n")) | ||
|
||
// const importBatchJobRes = await api.post( | ||
// "/admin/batch-jobs", | ||
// { | ||
// type: "product-import", | ||
// context: { | ||
// fileKey: exportFilePath, | ||
// }, | ||
// }, | ||
// adminReqConfig | ||
// ) | ||
|
||
// batchJobId = importBatchJobRes.data.batch_job.id | ||
|
||
// expect(batchJobId).toBeTruthy() | ||
|
||
// shouldContinuePulling = true | ||
// while (shouldContinuePulling) { | ||
// const res = await api.get( | ||
// `/admin/batch-jobs/${batchJobId}`, | ||
// adminReqConfig | ||
// ) | ||
|
||
// await new Promise((resolve, _) => { | ||
// setTimeout(resolve, 1000) | ||
// }) | ||
|
||
// batchJob = res.data.batch_job | ||
|
||
// shouldContinuePulling = !( | ||
// batchJob.status === "completed" || batchJob.status === "failed" | ||
// ) | ||
// } | ||
|
||
// expect(batchJob.status).toBe("completed") | ||
|
||
// const productsResponse = await api.get( | ||
// "/admin/products", | ||
// adminReqConfig | ||
// ) | ||
// expect(productsResponse.data.count).toBe(5) | ||
// expect(productsResponse.data.products).toEqual( | ||
// expect.arrayContaining([ | ||
// expect.objectContaining({ | ||
// id: csvLine[0], | ||
// handle: csvLine[1], | ||
// title: csvLine[2], | ||
// }), | ||
// ]) | ||
// ) | ||
// }) | ||
}) | ||
}, | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tests will be enabled once the workflow is implemented (follow-up PRs)