feat: order export and upload stream (#14243)
* feat: order export * Merge branch 'develop' of https://github.com/medusajs/medusa into feat/order-export * normalize status * rm util * serialize totals * test * lock * comments * configurable order list
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,119 @@
|
||||
import { FileSystem } from "@medusajs/utils"
|
||||
import fs from "fs/promises"
|
||||
import path from "path"
|
||||
import { LocalFileService } from "../../src/services/local-file"
|
||||
|
||||
jest.setTimeout(10000)
|
||||
|
||||
describe("Local File Plugin", () => {
|
||||
let localService: LocalFileService
|
||||
|
||||
const fixtureImagePath =
|
||||
process.cwd() + "/integration-tests/__fixtures__/catphoto.jpg"
|
||||
|
||||
const uploadDir = path.join(
|
||||
process.cwd(),
|
||||
"integration-tests/__tests__/uploads"
|
||||
)
|
||||
|
||||
const fileSystem = new FileSystem(uploadDir)
|
||||
|
||||
beforeAll(async () => {
|
||||
localService = new LocalFileService(
|
||||
{
|
||||
logger: console as any,
|
||||
},
|
||||
{
|
||||
upload_dir: uploadDir,
|
||||
backend_url: "http://localhost:9000/static",
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await fileSystem.cleanup()
|
||||
})
|
||||
|
||||
it(`should upload, read, and then delete a public file successfully`, async () => {
|
||||
const fileContent = await fs.readFile(fixtureImagePath)
|
||||
const fixtureAsBase64 = fileContent.toString("base64")
|
||||
|
||||
const resp = await localService.upload({
|
||||
filename: "catphoto.jpg",
|
||||
mimeType: "image/jpeg",
|
||||
content: fileContent as any,
|
||||
access: "public",
|
||||
})
|
||||
|
||||
expect(resp).toEqual({
|
||||
key: expect.stringMatching(/catphoto.*\.jpg/),
|
||||
url: expect.stringMatching(
|
||||
/http:\/\/localhost:9000\/static\/.*catphoto.*\.jpg/
|
||||
),
|
||||
})
|
||||
|
||||
// For local file provider, we can verify the file exists on disk
|
||||
const fileKey = resp.key
|
||||
const baseDir = uploadDir
|
||||
const filePath = path.join(baseDir, fileKey)
|
||||
|
||||
const fileOnDisk = await fs.readFile(filePath)
|
||||
|
||||
const fileOnDiskAsBase64 = fileOnDisk.toString("base64")
|
||||
|
||||
expect(fileOnDiskAsBase64).toEqual(fixtureAsBase64)
|
||||
|
||||
const signedUrl = await localService.getPresignedDownloadUrl({
|
||||
fileKey: resp.key,
|
||||
})
|
||||
|
||||
expect(signedUrl).toEqual(resp.url)
|
||||
|
||||
const buffer = await localService.getAsBuffer({ fileKey: resp.key })
|
||||
expect(buffer).toEqual(fileContent)
|
||||
|
||||
await localService.delete({ fileKey: resp.key })
|
||||
|
||||
await expect(fs.access(filePath)).rejects.toThrow()
|
||||
})
|
||||
|
||||
it("uploads using stream", async () => {
|
||||
const fileContent = await fs.readFile(fixtureImagePath)
|
||||
|
||||
const { writeStream, promise } = await localService.getUploadStream({
|
||||
filename: "catphoto-stream.jpg",
|
||||
mimeType: "image/jpeg",
|
||||
access: "public",
|
||||
})
|
||||
|
||||
writeStream.write(fileContent)
|
||||
writeStream.end()
|
||||
|
||||
const resp = await promise
|
||||
|
||||
expect(resp).toEqual({
|
||||
key: expect.stringMatching(/catphoto-stream.*\.jpg/),
|
||||
url: expect.stringMatching(
|
||||
/http:\/\/localhost:9000\/static\/.*catphoto-stream.*\.jpg/
|
||||
),
|
||||
})
|
||||
|
||||
const fileKey = resp.key
|
||||
const filePath = path.join(uploadDir, fileKey)
|
||||
|
||||
const fileOnDisk = await fs.readFile(filePath)
|
||||
expect(fileOnDisk).toEqual(fileContent)
|
||||
|
||||
const signedUrl = await localService.getPresignedDownloadUrl({
|
||||
fileKey: resp.key,
|
||||
})
|
||||
|
||||
expect(signedUrl).toEqual(resp.url)
|
||||
|
||||
const buffer = await localService.getAsBuffer({ fileKey: resp.key })
|
||||
expect(buffer).toEqual(fileContent)
|
||||
|
||||
await localService.delete({ fileKey: resp.key })
|
||||
await expect(fs.access(filePath)).rejects.toThrow()
|
||||
})
|
||||
})
|
||||
@@ -21,6 +21,7 @@
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"test": "../../../../node_modules/.bin/jest --passWithNoTests src",
|
||||
"test:integration": "../../../../node_modules/.bin/jest --passWithNoTests --forceExit --testPathPattern=\"integration-tests/__tests__/[^/]*\\.spec\\.ts\"",
|
||||
"build": "yarn run -T rimraf dist && yarn run -T tsc --build ./tsconfig.json",
|
||||
"watch": "yarn run -T tsc --watch"
|
||||
},
|
||||
|
||||
@@ -3,10 +3,10 @@ import {
|
||||
AbstractFileProviderService,
|
||||
MedusaError,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { createReadStream } from "fs"
|
||||
import { createReadStream, createWriteStream } from "fs"
|
||||
import fs from "fs/promises"
|
||||
import path from "path"
|
||||
import type { Readable } from "stream"
|
||||
import type { Readable, Writable } from "stream"
|
||||
|
||||
export class LocalFileService extends AbstractFileProviderService {
|
||||
static identifier = "localfs"
|
||||
@@ -78,6 +78,59 @@ export class LocalFileService extends AbstractFileProviderService {
|
||||
}
|
||||
}
|
||||
|
||||
async getUploadStream(fileData: FileTypes.ProviderUploadStreamDTO): Promise<{
|
||||
writeStream: Writable
|
||||
promise: Promise<FileTypes.ProviderFileResultDTO>
|
||||
url: string
|
||||
fileKey: string
|
||||
}> {
|
||||
if (!fileData.filename) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`No filename provided`
|
||||
)
|
||||
}
|
||||
|
||||
const parsedFilename = path.parse(fileData.filename)
|
||||
const baseDir =
|
||||
fileData.access === "public" ? this.uploadDir_ : this.privateUploadDir_
|
||||
await this.ensureDirExists(baseDir, parsedFilename.dir)
|
||||
|
||||
const fileKey = path.join(
|
||||
parsedFilename.dir,
|
||||
// We prepend "private" to the file key so deletions and presigned URLs can know which folder to look into
|
||||
`${fileData.access === "public" ? "" : "private-"}${Date.now()}-${
|
||||
parsedFilename.base
|
||||
}`
|
||||
)
|
||||
|
||||
const filePath = this.getUploadFilePath(baseDir, fileKey)
|
||||
const fileUrl = this.getUploadFileUrl(fileKey)
|
||||
|
||||
const writeStream = createWriteStream(filePath)
|
||||
|
||||
const promise = new Promise<FileTypes.ProviderFileResultDTO>(
|
||||
(resolve, reject) => {
|
||||
writeStream.on("finish", () => {
|
||||
resolve({
|
||||
url: fileUrl,
|
||||
key: fileKey,
|
||||
})
|
||||
})
|
||||
writeStream.on("error", (err) => {
|
||||
reject(err)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
writeStream,
|
||||
promise,
|
||||
url: fileUrl,
|
||||
fileKey,
|
||||
}
|
||||
}
|
||||
|
||||
async delete(
|
||||
files: FileTypes.ProviderDeleteFileDTO | FileTypes.ProviderDeleteFileDTO[]
|
||||
): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user