feat(medusa): Convert FulfillmentService to TypeScript (#1962)

This commit is contained in:
Philip Korsholm
2022-08-22 19:19:41 +02:00
committed by GitHub
parent 8cbebef403
commit c97ccd3fb5
7 changed files with 226 additions and 117 deletions

View File

@@ -0,0 +1,7 @@
---
"medusa-fulfillment-manual": patch
"medusa-interfaces": patch
"@medusajs/medusa": patch
---
Convert FulfillmentService to TypeScript

View File

@@ -19,7 +19,7 @@ class ManualFulfillmentService extends FulfillmentService {
]
}
validateFulfillmentData(data, cart) {
validateFulfillmentData(_, data, cart) {
return data
}

View File

@@ -24,7 +24,9 @@ class BaseFulfillmentService extends BaseService {
* to create shipping options in Medusa that can be chosen between by the
* customer.
*/
getFulfillmentOptions() {}
getFulfillmentOptions() {
throw Error("getFulfillmentOptions must be overridden by the child class")
}
/**
* Called before a shipping method is set on a cart to ensure that the data
@@ -32,12 +34,13 @@ class BaseFulfillmentService extends BaseService {
* data about the shipment such as an id of a drop point. It is up to the
* fulfillment provider to enforce that the correct data is being sent
* through.
* @param {object} optionData - the data to validate
* @param {object} data - the data to validate
* @param {object} cart - the cart to which the shipping method will be applied
* @param {object | undefined} cart - the cart to which the shipping method will be applied
* @return {object} the data to populate `cart.shipping_methods.$.data` this
* is usually important for future actions like generating shipping labels
*/
validateFulfillmentData(data, cart) {
validateFulfillmentData(optionData, data, cart) {
throw Error("validateFulfillmentData must be overridden by the child class")
}
@@ -56,12 +59,16 @@ class BaseFulfillmentService extends BaseService {
/**
* Used to calculate a price for a given shipping option.
*/
calculatePrice(data, cart) {
calculatePrice(optionData, data, cart) {
throw Error("calculatePrice must be overridden by the child class")
}
createFulfillment() {
throw Error("createOrder must be overridden by the child class")
createFulfillment(data, items, order, fulfillment) {
throw Error("createFulfillment must be overridden by the child class")
}
cancelFulfillment(fulfillment) {
throw Error("cancelFulfillment must be overridden by the child class")
}
/**
@@ -94,6 +101,10 @@ class BaseFulfillmentService extends BaseService {
getShipmentDocuments(data) {
return []
}
retrieveDocuments(fulfillmentData, documentType) {
throw Error("retrieveDocuments must be overridden by the child class")
}
}
export default BaseFulfillmentService

View File

@@ -1,108 +0,0 @@
import { MedusaError } from "medusa-core-utils"
/**
* Helps retrive fulfillment providers
*/
class FulfillmentProviderService {
constructor(container) {
/** @private {logger} */
this.container_ = container
}
async registerInstalledProviders(providers) {
const { manager, fulfillmentProviderRepository } = this.container_
const model = manager.getCustomRepository(fulfillmentProviderRepository)
await model.update({}, { is_installed: false })
for (const p of providers) {
const n = model.create({ id: p, is_installed: true })
await model.save(n)
}
}
async list() {
const { manager, fulfillmentProviderRepository } = this.container_
const fpRepo = manager.getCustomRepository(fulfillmentProviderRepository)
return await fpRepo.find({})
}
async listFulfillmentOptions(providers) {
const result = await Promise.all(
providers.map(async (p) => {
const provider = await this.retrieveProvider(p)
return {
provider_id: p,
options: await provider.getFulfillmentOptions(),
}
})
)
return result
}
/**
* @param {string} provider_id - the provider id
* @return {FulfillmentService} the payment fulfillment provider
*/
retrieveProvider(provider_id) {
try {
return this.container_[`fp_${provider_id}`]
} catch (err) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Could not find a fulfillment provider with id: ${provider_id}`
)
}
}
async createFulfillment(method, items, order, fulfillment) {
const provider = this.retrieveProvider(method.shipping_option.provider_id)
return provider.createFulfillment(method.data, items, order, fulfillment)
}
async canCalculate(option) {
const provider = this.retrieveProvider(option.provider_id)
return provider.canCalculate(option.data)
}
async validateFulfillmentData(option, data, cart) {
const provider = this.retrieveProvider(option.provider_id)
return provider.validateFulfillmentData(option.data, data, cart)
}
async cancelFulfillment(fulfillment) {
const provider = this.retrieveProvider(fulfillment.provider_id)
return provider.cancelFulfillment(fulfillment.data)
}
async calculatePrice(option, data, cart) {
const provider = this.retrieveProvider(option.provider_id)
return provider.calculatePrice(option.data, data, cart)
}
async validateOption(option) {
const provider = this.retrieveProvider(option.provider_id)
return provider.validateOption(option.data)
}
async createReturn(returnOrder) {
const option = returnOrder.shipping_method.shipping_option
const provider = this.retrieveProvider(option.provider_id)
return provider.createReturn(returnOrder)
}
/**
* Fetches documents from the fulfillment provider
* @param {string} providerId - the id of the provider
* @param {object} fulfillmentData - the data relating to the fulfillment
* @param {"invoice" | "label"} documentType - the typ of
* document to fetch
*/
async retrieveDocuments(providerId, fulfillmentData, documentType) {
const provider = this.retrieveProvider(providerId)
return provider.retrieveDocuments(fulfillmentData, documentType)
}
}
export default FulfillmentProviderService

View File

@@ -0,0 +1,191 @@
import { MedusaError } from "medusa-core-utils"
import BaseFulfillmentService from "medusa-interfaces/dist/fulfillment-service"
import { EntityManager } from "typeorm"
import { TransactionBaseService } from "../interfaces"
import {
Cart,
Fulfillment,
FulfillmentProvider,
LineItem,
Order,
Return,
ShippingMethod,
ShippingOption,
} from "../models"
import { FulfillmentProviderRepository } from "../repositories/fulfillment-provider"
import { CreateFulfillmentOrder } from "../types/fulfillment"
import {
CreateReturnType,
FulfillmentOptions,
} from "../types/fulfillment-provider"
import { MedusaContainer } from "../types/global"
type FulfillmentProviderKey = `fp_${string}`
type FulfillmentProviderContainer = MedusaContainer & {
fulfillmentProviderRepository: typeof FulfillmentProviderRepository
manager: EntityManager
} & {
[key in `${FulfillmentProviderKey}`]: BaseFulfillmentService
}
/**
* Helps retrive fulfillment providers
*/
class FulfillmentProviderService extends TransactionBaseService {
protected manager_: EntityManager
protected transactionManager_: EntityManager | undefined
protected readonly container_: FulfillmentProviderContainer
protected readonly fulfillmentProviderRepository_: typeof FulfillmentProviderRepository
constructor(container: FulfillmentProviderContainer) {
super(container)
const { manager, fulfillmentProviderRepository } = container
this.container_ = container
this.manager_ = manager
this.fulfillmentProviderRepository_ = fulfillmentProviderRepository
}
async registerInstalledProviders(providers: string[]): Promise<void> {
return await this.atomicPhase_(async (manager) => {
const fulfillmentProviderRepo = manager.getCustomRepository(
this.fulfillmentProviderRepository_
)
await fulfillmentProviderRepo.update({}, { is_installed: false })
for (const p of providers) {
const n = fulfillmentProviderRepo.create({ id: p, is_installed: true })
await fulfillmentProviderRepo.save(n)
}
})
}
async list(): Promise<FulfillmentProvider[]> {
const fpRepo = this.manager_.getCustomRepository(
this.fulfillmentProviderRepository_
)
return await fpRepo.find({})
}
async listFulfillmentOptions(
providerIds: string[]
): Promise<FulfillmentOptions[]> {
return await Promise.all(
providerIds.map(async (p) => {
const provider = await this.retrieveProvider(p)
return {
provider_id: p,
options:
(await provider.getFulfillmentOptions()) as unknown as Record<
string,
unknown
>[],
}
})
)
}
/**
* @param providerId - the provider id
* @return the payment fulfillment provider
*/
retrieveProvider(providerId: string): BaseFulfillmentService {
try {
return this.container_[`fp_${providerId}`]
} catch (err) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Could not find a fulfillment provider with id: ${providerId}`
)
}
}
async createFulfillment(
method: ShippingMethod,
items: LineItem[],
order: CreateFulfillmentOrder,
fulfillment: Omit<Fulfillment, "beforeInsert">
): Promise<Record<string, unknown>> {
const provider = this.retrieveProvider(method.shipping_option.provider_id)
return provider.createFulfillment(
method.data,
items,
order,
fulfillment
) as unknown as Record<string, unknown>
}
async canCalculate(option: ShippingOption): Promise<boolean> {
const provider = this.retrieveProvider(option.provider_id)
return provider.canCalculate(option.data) as unknown as boolean
}
async validateFulfillmentData(
option: ShippingOption,
data: Record<string, unknown>,
cart: Cart | Record<string, unknown>
): Promise<Record<string, unknown>> {
const provider = this.retrieveProvider(option.provider_id)
return provider.validateFulfillmentData(
option.data,
data,
cart
) as unknown as Record<string, unknown>
}
async cancelFulfillment(fulfillment: Fulfillment): Promise<Fulfillment> {
const provider = this.retrieveProvider(fulfillment.provider_id)
return provider.cancelFulfillment(
fulfillment.data
) as unknown as Fulfillment
}
async calculatePrice(
option: ShippingOption,
data: Record<string, unknown>,
cart?: Order | Cart
): Promise<number> {
const provider = this.retrieveProvider(option.provider_id)
return provider.calculatePrice(option.data, data, cart) as unknown as number
}
async validateOption(option: ShippingOption): Promise<boolean> {
const provider = this.retrieveProvider(option.provider_id)
return provider.validateOption(option.data) as unknown as boolean
}
async createReturn(
returnOrder: CreateReturnType
): Promise<Record<string, unknown>> {
const option = returnOrder.shipping_method.shipping_option
const provider = this.retrieveProvider(option.provider_id)
return provider.createReturn(returnOrder) as unknown as Record<
string,
unknown
>
}
/**
* Fetches documents from the fulfillment provider
* @param providerId - the id of the provider
* @param fulfillmentData - the data relating to the fulfillment
* @param documentType - the typ of
* @returns document to fetch
*/
// TODO: consider removal in favor of "getReturnDocuments" and "getShipmentDocuments"
async retrieveDocuments(
providerId: string,
fulfillmentData: Record<string, unknown>,
documentType: "invoice" | "label"
): Promise<any> {
const provider = this.retrieveProvider(providerId)
return provider.retrieveDocuments(fulfillmentData, documentType)
}
}
export default FulfillmentProviderService

View File

@@ -252,7 +252,7 @@ class ShippingOptionService extends TransactionBaseService {
*/
async createShippingMethod(
optionId: string,
data: object,
data: Record<string, unknown>,
config: CreateShippingMethodDto
): Promise<ShippingMethod> {
return await this.atomicPhase_(async (manager) => {
@@ -678,7 +678,7 @@ class ShippingOptionService extends TransactionBaseService {
*/
async getPrice_(
option: ShippingOption,
data: object,
data: Record<string, unknown>,
cart: Cart | Order | undefined
): Promise<number> {
if (option.price_type === "calculated") {

View File

@@ -0,0 +1,8 @@
import { Return } from "../models"
export type FulfillmentOptions = {
provider_id: string
options: Record<string, unknown>[]
}
export type CreateReturnType = Omit<Return, "beforeInsert">