feat(medusa-file-local): implement missing streaming methods (#4788)
This commit is contained in:
@@ -1,35 +1,11 @@
|
||||
import stream from "stream"
|
||||
import { TransactionBaseService } from "./transaction-base-service"
|
||||
|
||||
export type FileServiceUploadResult = {
|
||||
url: string
|
||||
}
|
||||
|
||||
export type FileServiceGetUploadStreamResult = {
|
||||
writeStream: stream.PassThrough
|
||||
promise: Promise<any>
|
||||
url: string
|
||||
fileKey: string
|
||||
[x: string]: unknown
|
||||
}
|
||||
|
||||
export type GetUploadedFileType = {
|
||||
fileKey: string
|
||||
isPrivate?: boolean
|
||||
[x: string]: unknown
|
||||
}
|
||||
|
||||
export type DeleteFileType = {
|
||||
fileKey: string
|
||||
[x: string]: unknown
|
||||
}
|
||||
|
||||
export type UploadStreamDescriptorType = {
|
||||
name: string
|
||||
ext?: string
|
||||
isPrivate?: boolean
|
||||
[x: string]: unknown
|
||||
}
|
||||
import {
|
||||
DeleteFileType,
|
||||
FileServiceGetUploadStreamResult,
|
||||
FileServiceUploadResult,
|
||||
GetUploadedFileType,
|
||||
UploadStreamDescriptorType,
|
||||
} from "@medusajs/types"
|
||||
|
||||
export interface IFileService extends TransactionBaseService {
|
||||
/**
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { AbstractFileService } from "../interfaces"
|
||||
import {
|
||||
AbstractFileService,
|
||||
FileServiceGetUploadStreamResult,
|
||||
FileServiceUploadResult,
|
||||
GetUploadedFileType,
|
||||
UploadStreamDescriptorType,
|
||||
} from "../interfaces"
|
||||
} from "@medusajs/types"
|
||||
|
||||
class DefaultFileService extends AbstractFileService {
|
||||
async upload(
|
||||
|
||||
@@ -21,6 +21,9 @@ import {
|
||||
TBuiltPriceListImportLine,
|
||||
TParsedPriceListImportRowData,
|
||||
} from "./types"
|
||||
import { BatchJob } from "../../../models"
|
||||
import { TParsedProductImportRowData } from "../product/types"
|
||||
import { PriceListPriceCreateInput } from "../../../types/price-list"
|
||||
|
||||
/*
|
||||
* Default strategy class used for a batch import of products/variants.
|
||||
@@ -298,7 +301,7 @@ class PriceListImportStrategy extends AbstractBatchJobStrategy {
|
||||
|
||||
// Upload new prices for price list
|
||||
const priceImportOperations = await this.downloadImportOpsFile(
|
||||
batchJobId,
|
||||
batchJob,
|
||||
OperationType.PricesCreate
|
||||
)
|
||||
|
||||
@@ -306,7 +309,7 @@ class PriceListImportStrategy extends AbstractBatchJobStrategy {
|
||||
priceImportOperations.map(async (op) => {
|
||||
await txPriceListService.addPrices(
|
||||
priceListId,
|
||||
op.prices.map((p) => {
|
||||
(op.prices as PriceListPriceCreateInput[]).map((p) => {
|
||||
return {
|
||||
...p,
|
||||
variant_id: op.variant_id,
|
||||
@@ -328,14 +331,16 @@ class PriceListImportStrategy extends AbstractBatchJobStrategy {
|
||||
*/
|
||||
protected async uploadImportOpsFile(
|
||||
batchJobId: string,
|
||||
results: Record<OperationType, PriceListImportOperation[]>
|
||||
results: Record<OperationType, TParsedProductImportRowData[]>
|
||||
): Promise<void> {
|
||||
const uploadPromises: Promise<void>[] = []
|
||||
const transactionManager = this.transactionManager_ ?? this.manager_
|
||||
|
||||
const files: Record<string, string> = {}
|
||||
|
||||
for (const op in results) {
|
||||
if (results[op]?.length) {
|
||||
const { writeStream, promise } = await this.fileService_
|
||||
const { writeStream, fileKey, promise } = await this.fileService_
|
||||
.withTransaction(transactionManager)
|
||||
.getUploadStreamDescriptor({
|
||||
name: PriceListImportStrategy.buildFilename(batchJobId, op),
|
||||
@@ -344,33 +349,38 @@ class PriceListImportStrategy extends AbstractBatchJobStrategy {
|
||||
|
||||
uploadPromises.push(promise)
|
||||
|
||||
files[op] = fileKey
|
||||
writeStream.write(JSON.stringify(results[op]))
|
||||
writeStream.end()
|
||||
}
|
||||
}
|
||||
|
||||
await this.batchJobService_
|
||||
.withTransaction(transactionManager)
|
||||
.update(batchJobId, {
|
||||
result: { files },
|
||||
})
|
||||
|
||||
await Promise.all(uploadPromises)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove parsed ops JSON file.
|
||||
* Download parsed ops JSON file.
|
||||
*
|
||||
* @param batchJobId - An id of the current batch job being processed.
|
||||
* @param batchJob - the current batch job being processed
|
||||
* @param op - Type of import operation.
|
||||
*/
|
||||
protected async downloadImportOpsFile(
|
||||
batchJobId: string,
|
||||
batchJob: BatchJob,
|
||||
op: OperationType
|
||||
): Promise<PriceListImportOperation[]> {
|
||||
): Promise<TParsedProductImportRowData[]> {
|
||||
let data = ""
|
||||
const transactionManager = this.transactionManager_ ?? this.manager_
|
||||
|
||||
const readableStream = await this.fileService_
|
||||
.withTransaction(transactionManager)
|
||||
.getDownloadStream({
|
||||
fileKey: PriceListImportStrategy.buildFilename(batchJobId, op, {
|
||||
appendExt: ".json",
|
||||
}),
|
||||
fileKey: batchJob.result.files![op],
|
||||
})
|
||||
|
||||
return await new Promise((resolve) => {
|
||||
@@ -382,7 +392,7 @@ class PriceListImportStrategy extends AbstractBatchJobStrategy {
|
||||
})
|
||||
readableStream.on("error", () => {
|
||||
// TODO: maybe should throw
|
||||
resolve([] as PriceListImportOperation[])
|
||||
resolve([] as TParsedProductImportRowData[])
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -390,18 +400,16 @@ class PriceListImportStrategy extends AbstractBatchJobStrategy {
|
||||
/**
|
||||
* Delete parsed CSV ops files.
|
||||
*
|
||||
* @param batchJobId - An id of the current batch job being processed.
|
||||
* @param batchJob - the current batch job being processed
|
||||
*/
|
||||
protected async deleteOpsFiles(batchJobId: string): Promise<void> {
|
||||
protected async deleteOpsFiles(batchJob: BatchJob): Promise<void> {
|
||||
const transactionManager = this.transactionManager_ ?? this.manager_
|
||||
|
||||
const fileServiceTx = this.fileService_.withTransaction(transactionManager)
|
||||
for (const op of Object.values(OperationType)) {
|
||||
for (const fileName of Object.values(batchJob.result.files!)) {
|
||||
try {
|
||||
await fileServiceTx.delete({
|
||||
fileKey: PriceListImportStrategy.buildFilename(batchJobId, op, {
|
||||
appendExt: ".json",
|
||||
}),
|
||||
fileKey: fileName,
|
||||
})
|
||||
} catch (e) {
|
||||
// noop
|
||||
@@ -432,7 +440,7 @@ class PriceListImportStrategy extends AbstractBatchJobStrategy {
|
||||
.withTransaction(transactionManager)
|
||||
.delete({ fileKey })
|
||||
|
||||
await this.deleteOpsFiles(batchJob.id)
|
||||
await this.deleteOpsFiles(batchJob)
|
||||
}
|
||||
|
||||
private static buildFilename(
|
||||
|
||||
@@ -8,30 +8,30 @@ import ProductCategoryFeatureFlag from "../../../loaders/feature-flags/product-c
|
||||
import SalesChannelFeatureFlag from "../../../loaders/feature-flags/sales-channels"
|
||||
import { BatchJob, SalesChannel } from "../../../models"
|
||||
import {
|
||||
BatchJobService,
|
||||
ProductCategoryService,
|
||||
ProductCollectionService,
|
||||
ProductService,
|
||||
ProductVariantService,
|
||||
RegionService,
|
||||
SalesChannelService,
|
||||
ShippingProfileService,
|
||||
BatchJobService,
|
||||
ProductCategoryService,
|
||||
ProductCollectionService,
|
||||
ProductService,
|
||||
ProductVariantService,
|
||||
RegionService,
|
||||
SalesChannelService,
|
||||
ShippingProfileService,
|
||||
} from "../../../services"
|
||||
import CsvParser from "../../../services/csv-parser"
|
||||
import { CreateProductInput } from "../../../types/product"
|
||||
import { CreateProductVariantInput } from "../../../types/product-variant"
|
||||
import {
|
||||
OperationType,
|
||||
ProductImportBatchJob,
|
||||
ProductImportCsvSchema,
|
||||
ProductImportInjectedProps,
|
||||
ProductImportJobContext,
|
||||
TParsedProductImportRowData,
|
||||
OperationType,
|
||||
ProductImportBatchJob,
|
||||
ProductImportCsvSchema,
|
||||
ProductImportInjectedProps,
|
||||
ProductImportJobContext,
|
||||
TParsedProductImportRowData,
|
||||
} from "./types"
|
||||
import {
|
||||
productImportColumnsDefinition,
|
||||
productImportProductCategoriesColumnsDefinition,
|
||||
productImportSalesChannelsColumnsDefinition,
|
||||
productImportColumnsDefinition,
|
||||
productImportProductCategoriesColumnsDefinition,
|
||||
productImportSalesChannelsColumnsDefinition,
|
||||
} from "./types/columns-definition"
|
||||
import { transformProductData, transformVariantData } from "./utils"
|
||||
|
||||
@@ -420,7 +420,7 @@ class ProductImportStrategy extends AbstractBatchJobStrategy {
|
||||
const transactionManager = this.transactionManager_ ?? this.manager_
|
||||
|
||||
const productOps = await this.downloadImportOpsFile(
|
||||
batchJob.id,
|
||||
batchJob,
|
||||
OperationType.ProductCreate
|
||||
)
|
||||
|
||||
@@ -492,7 +492,7 @@ class ProductImportStrategy extends AbstractBatchJobStrategy {
|
||||
|
||||
const transactionManager = this.transactionManager_ ?? this.manager_
|
||||
const productOps = await this.downloadImportOpsFile(
|
||||
batchJob.id,
|
||||
batchJob,
|
||||
OperationType.ProductUpdate
|
||||
)
|
||||
|
||||
@@ -568,7 +568,7 @@ class ProductImportStrategy extends AbstractBatchJobStrategy {
|
||||
const transactionManager = this.transactionManager_ ?? this.manager_
|
||||
|
||||
const variantOps = await this.downloadImportOpsFile(
|
||||
batchJob.id,
|
||||
batchJob,
|
||||
OperationType.VariantCreate
|
||||
)
|
||||
|
||||
@@ -624,7 +624,7 @@ class ProductImportStrategy extends AbstractBatchJobStrategy {
|
||||
const transactionManager = this.transactionManager_ ?? this.manager_
|
||||
|
||||
const variantOps = await this.downloadImportOpsFile(
|
||||
batchJob.id,
|
||||
batchJob,
|
||||
OperationType.VariantUpdate
|
||||
)
|
||||
|
||||
@@ -691,9 +691,11 @@ class ProductImportStrategy extends AbstractBatchJobStrategy {
|
||||
const uploadPromises: Promise<void>[] = []
|
||||
const transactionManager = this.transactionManager_ ?? this.manager_
|
||||
|
||||
const files: Record<string, string> = {}
|
||||
|
||||
for (const op in results) {
|
||||
if (results[op]?.length) {
|
||||
const { writeStream, promise } = await this.fileService_
|
||||
const { writeStream, fileKey, promise } = await this.fileService_
|
||||
.withTransaction(transactionManager)
|
||||
.getUploadStreamDescriptor({
|
||||
name: ProductImportStrategy.buildFilename(batchJobId, op),
|
||||
@@ -702,22 +704,29 @@ class ProductImportStrategy extends AbstractBatchJobStrategy {
|
||||
|
||||
uploadPromises.push(promise)
|
||||
|
||||
files[op] = fileKey
|
||||
writeStream.write(JSON.stringify(results[op]))
|
||||
writeStream.end()
|
||||
}
|
||||
}
|
||||
|
||||
await this.batchJobService_
|
||||
.withTransaction(transactionManager)
|
||||
.update(batchJobId, {
|
||||
result: { files },
|
||||
})
|
||||
|
||||
await Promise.all(uploadPromises)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove parsed ops JSON file.
|
||||
* Download parsed ops JSON file.
|
||||
*
|
||||
* @param batchJobId - An id of the current batch job being processed.
|
||||
* @param batchJob - the current batch job being processed
|
||||
* @param op - Type of import operation.
|
||||
*/
|
||||
protected async downloadImportOpsFile(
|
||||
batchJobId: string,
|
||||
batchJob: BatchJob,
|
||||
op: OperationType
|
||||
): Promise<TParsedProductImportRowData[]> {
|
||||
let data = ""
|
||||
@@ -726,9 +735,7 @@ class ProductImportStrategy extends AbstractBatchJobStrategy {
|
||||
const readableStream = await this.fileService_
|
||||
.withTransaction(transactionManager)
|
||||
.getDownloadStream({
|
||||
fileKey: ProductImportStrategy.buildFilename(batchJobId, op, {
|
||||
appendExt: ".json",
|
||||
}),
|
||||
fileKey: batchJob.result.files![op],
|
||||
})
|
||||
|
||||
return await new Promise((resolve) => {
|
||||
@@ -748,18 +755,16 @@ class ProductImportStrategy extends AbstractBatchJobStrategy {
|
||||
/**
|
||||
* Delete parsed CSV ops files.
|
||||
*
|
||||
* @param batchJobId - An id of the current batch job being processed.
|
||||
* @param batchJob - the current batch job being processed
|
||||
*/
|
||||
protected async deleteOpsFiles(batchJobId: string): Promise<void> {
|
||||
protected async deleteOpsFiles(batchJob: BatchJob): Promise<void> {
|
||||
const transactionManager = this.transactionManager_ ?? this.manager_
|
||||
|
||||
const fileServiceTx = this.fileService_.withTransaction(transactionManager)
|
||||
for (const op of Object.values(OperationType)) {
|
||||
for (const fileName of Object.values(batchJob.result.files!)) {
|
||||
try {
|
||||
await fileServiceTx.delete({
|
||||
fileKey: ProductImportStrategy.buildFilename(batchJobId, op, {
|
||||
appendExt: ".json",
|
||||
}),
|
||||
fileKey: fileName,
|
||||
})
|
||||
} catch (e) {
|
||||
// noop
|
||||
@@ -790,7 +795,7 @@ class ProductImportStrategy extends AbstractBatchJobStrategy {
|
||||
.withTransaction(transactionManager)
|
||||
.delete({ fileKey })
|
||||
|
||||
await this.deleteOpsFiles(batchJob.id)
|
||||
await this.deleteOpsFiles(batchJob)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user