feat(pricing, types, utils, medusa-sdk): Pricing Module Setup + Currency (#4860)
What: - Setups the skeleton for pricing module - Creates service/model/repository for currency model - Setups types - Setups DB - Moved some utils to a common place RESOLVES CORE-1477 RESOLVES CORE-1476
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import { CurrencyService } from "@services"
|
||||
import { asClass, asValue, createContainer } from "awilix"
|
||||
|
||||
export const nonExistingCurrencyCode = "non-existing-code"
|
||||
export const mockContainer = createContainer()
|
||||
|
||||
mockContainer.register({
|
||||
transaction: asValue(async (task) => await task()),
|
||||
currencyRepository: asValue({
|
||||
find: jest.fn().mockImplementation(async ({ where: { code } }) => {
|
||||
if (code === nonExistingCurrencyCode) {
|
||||
return []
|
||||
}
|
||||
|
||||
return [{}]
|
||||
}),
|
||||
findAndCount: jest.fn().mockResolvedValue([[], 0]),
|
||||
getFreshManager: jest.fn().mockResolvedValue({}),
|
||||
}),
|
||||
currencyService: asClass(CurrencyService),
|
||||
})
|
||||
@@ -0,0 +1,203 @@
|
||||
import {
|
||||
mockContainer,
|
||||
nonExistingCurrencyCode,
|
||||
} from "../__fixtures__/currency"
|
||||
|
||||
const code = "existing-currency"
|
||||
|
||||
describe("Currency service", function () {
|
||||
beforeEach(function () {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("should retrieve a currency", async function () {
|
||||
const currencyService = mockContainer.resolve("currencyService")
|
||||
const currencyRepository = mockContainer.resolve("currencyRepository")
|
||||
|
||||
await currencyService.retrieve(code)
|
||||
|
||||
expect(currencyRepository.find).toHaveBeenCalledWith(
|
||||
{
|
||||
where: {
|
||||
code,
|
||||
},
|
||||
options: {
|
||||
fields: undefined,
|
||||
limit: 15,
|
||||
offset: undefined,
|
||||
populate: [],
|
||||
},
|
||||
},
|
||||
expect.any(Object)
|
||||
)
|
||||
})
|
||||
|
||||
it("should fail to retrieve a currency", async function () {
|
||||
const currencyService = mockContainer.resolve("currencyService")
|
||||
const currencyRepository = mockContainer.resolve("currencyRepository")
|
||||
|
||||
const err = await currencyService
|
||||
.retrieve(nonExistingCurrencyCode)
|
||||
.catch((e) => e)
|
||||
|
||||
expect(currencyRepository.find).toHaveBeenCalledWith(
|
||||
{
|
||||
where: {
|
||||
code: nonExistingCurrencyCode,
|
||||
},
|
||||
options: {
|
||||
fields: undefined,
|
||||
limit: 15,
|
||||
offset: undefined,
|
||||
populate: [],
|
||||
withDeleted: undefined,
|
||||
},
|
||||
},
|
||||
expect.any(Object)
|
||||
)
|
||||
|
||||
expect(err.message).toBe(
|
||||
`Currency with code: ${nonExistingCurrencyCode} was not found`
|
||||
)
|
||||
})
|
||||
|
||||
it("should list currencys", async function () {
|
||||
const currencyService = mockContainer.resolve("currencyService")
|
||||
const currencyRepository = mockContainer.resolve("currencyRepository")
|
||||
|
||||
const filters = {}
|
||||
const config = {
|
||||
relations: [],
|
||||
}
|
||||
|
||||
await currencyService.list(filters, config)
|
||||
|
||||
expect(currencyRepository.find).toHaveBeenCalledWith(
|
||||
{
|
||||
where: {},
|
||||
options: {
|
||||
fields: undefined,
|
||||
limit: 15,
|
||||
offset: undefined,
|
||||
populate: [],
|
||||
withDeleted: undefined,
|
||||
},
|
||||
},
|
||||
expect.any(Object)
|
||||
)
|
||||
})
|
||||
|
||||
it("should list currencys with filters", async function () {
|
||||
const currencyService = mockContainer.resolve("currencyService")
|
||||
const currencyRepository = mockContainer.resolve("currencyRepository")
|
||||
|
||||
const filters = {
|
||||
tags: {
|
||||
value: {
|
||||
$in: ["test"],
|
||||
},
|
||||
},
|
||||
}
|
||||
const config = {
|
||||
relations: [],
|
||||
}
|
||||
|
||||
await currencyService.list(filters, config)
|
||||
|
||||
expect(currencyRepository.find).toHaveBeenCalledWith(
|
||||
{
|
||||
where: {
|
||||
tags: {
|
||||
value: {
|
||||
$in: ["test"],
|
||||
},
|
||||
},
|
||||
},
|
||||
options: {
|
||||
fields: undefined,
|
||||
limit: 15,
|
||||
offset: undefined,
|
||||
populate: [],
|
||||
withDeleted: undefined,
|
||||
},
|
||||
},
|
||||
expect.any(Object)
|
||||
)
|
||||
})
|
||||
|
||||
it("should list currencys with filters and relations", async function () {
|
||||
const currencyService = mockContainer.resolve("currencyService")
|
||||
const currencyRepository = mockContainer.resolve("currencyRepository")
|
||||
|
||||
const filters = {
|
||||
tags: {
|
||||
value: {
|
||||
$in: ["test"],
|
||||
},
|
||||
},
|
||||
}
|
||||
const config = {
|
||||
relations: ["tags"],
|
||||
}
|
||||
|
||||
await currencyService.list(filters, config)
|
||||
|
||||
expect(currencyRepository.find).toHaveBeenCalledWith(
|
||||
{
|
||||
where: {
|
||||
tags: {
|
||||
value: {
|
||||
$in: ["test"],
|
||||
},
|
||||
},
|
||||
},
|
||||
options: {
|
||||
fields: undefined,
|
||||
limit: 15,
|
||||
offset: undefined,
|
||||
withDeleted: undefined,
|
||||
populate: ["tags"],
|
||||
},
|
||||
},
|
||||
expect.any(Object)
|
||||
)
|
||||
})
|
||||
|
||||
it("should list and count the currencys with filters and relations", async function () {
|
||||
const currencyService = mockContainer.resolve("currencyService")
|
||||
const currencyRepository = mockContainer.resolve("currencyRepository")
|
||||
|
||||
const filters = {
|
||||
tags: {
|
||||
value: {
|
||||
$in: ["test"],
|
||||
},
|
||||
},
|
||||
}
|
||||
const config = {
|
||||
relations: ["tags"],
|
||||
}
|
||||
|
||||
await currencyService.listAndCount(filters, config)
|
||||
|
||||
expect(currencyRepository.findAndCount).toHaveBeenCalledWith(
|
||||
{
|
||||
where: {
|
||||
tags: {
|
||||
value: {
|
||||
$in: ["test"],
|
||||
},
|
||||
},
|
||||
},
|
||||
options: {
|
||||
fields: undefined,
|
||||
limit: 15,
|
||||
offset: undefined,
|
||||
withDeleted: undefined,
|
||||
populate: ["tags"],
|
||||
},
|
||||
},
|
||||
expect.any(Object)
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,107 @@
|
||||
import { Context, DAL, FindConfig, PricingTypes } from "@medusajs/types"
|
||||
import {
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
ModulesSdkUtils,
|
||||
retrieveEntity,
|
||||
} from "@medusajs/utils"
|
||||
import { Currency } from "@models"
|
||||
import { CurrencyRepository } from "@repositories"
|
||||
|
||||
import { doNotForceTransaction, shouldForceTransaction } from "@medusajs/utils"
|
||||
|
||||
type InjectedDependencies = {
|
||||
currencyRepository: DAL.RepositoryService
|
||||
}
|
||||
|
||||
export default class CurrencyService<TEntity extends Currency = Currency> {
|
||||
protected readonly currencyRepository_: DAL.RepositoryService
|
||||
|
||||
constructor({ currencyRepository }: InjectedDependencies) {
|
||||
this.currencyRepository_ = currencyRepository
|
||||
}
|
||||
|
||||
@InjectManager("currencyRepository_")
|
||||
async retrieve(
|
||||
currencyCode: string,
|
||||
config: FindConfig<PricingTypes.CurrencyDTO> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity> {
|
||||
return (await retrieveEntity<Currency, PricingTypes.CurrencyDTO>({
|
||||
id: currencyCode,
|
||||
identifierColumn: "code",
|
||||
entityName: Currency.name,
|
||||
repository: this.currencyRepository_,
|
||||
config,
|
||||
sharedContext,
|
||||
})) as TEntity
|
||||
}
|
||||
|
||||
@InjectManager("currencyRepository_")
|
||||
async list(
|
||||
filters: PricingTypes.FilterableCurrencyProps = {},
|
||||
config: FindConfig<PricingTypes.CurrencyDTO> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity[]> {
|
||||
return (await this.currencyRepository_.find(
|
||||
this.buildQueryForList(filters, config),
|
||||
sharedContext
|
||||
)) as TEntity[]
|
||||
}
|
||||
|
||||
@InjectManager("currencyRepository_")
|
||||
async listAndCount(
|
||||
filters: PricingTypes.FilterableCurrencyProps = {},
|
||||
config: FindConfig<PricingTypes.CurrencyDTO> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<[TEntity[], number]> {
|
||||
return (await this.currencyRepository_.findAndCount(
|
||||
this.buildQueryForList(filters, config),
|
||||
sharedContext
|
||||
)) as [TEntity[], number]
|
||||
}
|
||||
|
||||
private buildQueryForList(
|
||||
filters: PricingTypes.FilterableCurrencyProps = {},
|
||||
config: FindConfig<PricingTypes.CurrencyDTO> = {}
|
||||
) {
|
||||
const queryOptions = ModulesSdkUtils.buildQuery<Currency>(filters, config)
|
||||
|
||||
if (filters.code) {
|
||||
queryOptions.where["code"] = { $in: filters.code }
|
||||
}
|
||||
|
||||
return queryOptions
|
||||
}
|
||||
|
||||
@InjectTransactionManager(shouldForceTransaction, "currencyRepository_")
|
||||
async create(
|
||||
data: PricingTypes.CreateCurrencyDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity[]> {
|
||||
return (await (this.currencyRepository_ as CurrencyRepository).create(
|
||||
data,
|
||||
sharedContext
|
||||
)) as TEntity[]
|
||||
}
|
||||
|
||||
@InjectTransactionManager(shouldForceTransaction, "currencyRepository_")
|
||||
async update(
|
||||
data: PricingTypes.UpdateCurrencyDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity[]> {
|
||||
return (await (this.currencyRepository_ as CurrencyRepository).update(
|
||||
data,
|
||||
sharedContext
|
||||
)) as TEntity[]
|
||||
}
|
||||
|
||||
@InjectTransactionManager(doNotForceTransaction, "currencyRepository_")
|
||||
async delete(
|
||||
ids: string[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<void> {
|
||||
await this.currencyRepository_.delete(ids, sharedContext)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as CurrencyService } from "./currency"
|
||||
export { default as PricingModuleService } from "./pricing-module"
|
||||
@@ -0,0 +1,141 @@
|
||||
import {
|
||||
Context,
|
||||
DAL,
|
||||
FindConfig,
|
||||
InternalModuleDeclaration,
|
||||
JoinerServiceConfig,
|
||||
PricingTypes,
|
||||
} from "@medusajs/types"
|
||||
import { Currency } from "@models"
|
||||
import { CurrencyService } from "@services"
|
||||
|
||||
import {
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
import { shouldForceTransaction } from "@medusajs/utils"
|
||||
import { joinerConfig } from "../joiner-config"
|
||||
|
||||
type InjectedDependencies = {
|
||||
baseRepository: DAL.RepositoryService
|
||||
currencyService: CurrencyService<any>
|
||||
}
|
||||
|
||||
export default class PricingModuleService<TCurrency extends Currency = Currency>
|
||||
implements PricingTypes.IPricingModuleService
|
||||
{
|
||||
protected baseRepository_: DAL.RepositoryService
|
||||
protected readonly currencyService_: CurrencyService<TCurrency>
|
||||
|
||||
constructor(
|
||||
{ baseRepository, currencyService }: InjectedDependencies,
|
||||
protected readonly moduleDeclaration: InternalModuleDeclaration
|
||||
) {
|
||||
this.baseRepository_ = baseRepository
|
||||
this.currencyService_ = currencyService
|
||||
}
|
||||
|
||||
__joinerConfig(): JoinerServiceConfig {
|
||||
return joinerConfig
|
||||
}
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async retrieveCurrency(
|
||||
code: string,
|
||||
config: FindConfig<PricingTypes.CurrencyDTO> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<PricingTypes.CurrencyDTO> {
|
||||
const currency = await this.currencyService_.retrieve(
|
||||
code,
|
||||
config,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return this.baseRepository_.serialize<PricingTypes.CurrencyDTO>(currency, {
|
||||
populate: true,
|
||||
})
|
||||
}
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async listCurrencies(
|
||||
filters: PricingTypes.FilterableCurrencyProps = {},
|
||||
config: FindConfig<PricingTypes.CurrencyDTO> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<PricingTypes.CurrencyDTO[]> {
|
||||
const currencies = await this.currencyService_.list(
|
||||
filters,
|
||||
config,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return this.baseRepository_.serialize<PricingTypes.CurrencyDTO[]>(
|
||||
currencies,
|
||||
{
|
||||
populate: true,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async listAndCountCurrencies(
|
||||
filters: PricingTypes.FilterableCurrencyProps = {},
|
||||
config: FindConfig<PricingTypes.CurrencyDTO> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<[PricingTypes.CurrencyDTO[], number]> {
|
||||
const [currencies, count] = await this.currencyService_.listAndCount(
|
||||
filters,
|
||||
config,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return [
|
||||
await this.baseRepository_.serialize<PricingTypes.CurrencyDTO[]>(
|
||||
currencies,
|
||||
{
|
||||
populate: true,
|
||||
}
|
||||
),
|
||||
count,
|
||||
]
|
||||
}
|
||||
|
||||
@InjectTransactionManager(shouldForceTransaction, "baseRepository_")
|
||||
async createCurrencies(
|
||||
data: PricingTypes.CreateCurrencyDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
) {
|
||||
const currencies = await this.currencyService_.create(data, sharedContext)
|
||||
|
||||
return this.baseRepository_.serialize<PricingTypes.CurrencyDTO[]>(
|
||||
currencies,
|
||||
{
|
||||
populate: true,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@InjectTransactionManager(shouldForceTransaction, "baseRepository_")
|
||||
async updateCurrencies(
|
||||
data: PricingTypes.UpdateCurrencyDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
) {
|
||||
const currencies = await this.currencyService_.update(data, sharedContext)
|
||||
|
||||
return this.baseRepository_.serialize<PricingTypes.CurrencyDTO[]>(
|
||||
currencies,
|
||||
{
|
||||
populate: true,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@InjectTransactionManager(shouldForceTransaction, "baseRepository_")
|
||||
async deleteCurrencies(
|
||||
currencyCodes: string[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<void> {
|
||||
await this.currencyService_.delete(currencyCodes, sharedContext)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user