chore(): Reorganize modules (#7210)
**What** Move all modules to the modules directory
This commit is contained in:
committed by
GitHub
parent
7a351eef09
commit
4eae25e1ef
@@ -0,0 +1,17 @@
|
||||
import { asValue } from "awilix"
|
||||
|
||||
export const nonExistingProductId = "non-existing-id"
|
||||
|
||||
export const productRepositoryMock = {
|
||||
productRepository: asValue({
|
||||
find: jest.fn().mockImplementation(async ({ where: { id } }) => {
|
||||
if (id === nonExistingProductId) {
|
||||
return []
|
||||
}
|
||||
|
||||
return [{}]
|
||||
}),
|
||||
findAndCount: jest.fn().mockResolvedValue([[], 0]),
|
||||
getFreshManager: jest.fn().mockResolvedValue({}),
|
||||
}),
|
||||
}
|
||||
227
packages/modules/product/src/services/__tests__/product.spec.ts
Normal file
227
packages/modules/product/src/services/__tests__/product.spec.ts
Normal file
@@ -0,0 +1,227 @@
|
||||
import {
|
||||
nonExistingProductId,
|
||||
productRepositoryMock,
|
||||
} from "../__fixtures__/product"
|
||||
import { createMedusaContainer } from "@medusajs/utils"
|
||||
import { asValue } from "awilix"
|
||||
import ContainerLoader from "../../loaders/container"
|
||||
|
||||
describe("Product service", function () {
|
||||
let container
|
||||
|
||||
beforeEach(async function () {
|
||||
jest.clearAllMocks()
|
||||
|
||||
container = createMedusaContainer()
|
||||
container.register("manager", asValue({}))
|
||||
|
||||
await ContainerLoader({ container })
|
||||
|
||||
container.register(productRepositoryMock)
|
||||
})
|
||||
|
||||
it("should retrieve a product", async function () {
|
||||
const productService = container.resolve("productService")
|
||||
const productRepository = container.resolve("productRepository")
|
||||
const productId = "existing-product"
|
||||
|
||||
await productService.retrieve(productId)
|
||||
|
||||
expect(productRepository.find).toHaveBeenCalledWith(
|
||||
{
|
||||
where: {
|
||||
id: productId,
|
||||
},
|
||||
options: {
|
||||
fields: undefined,
|
||||
limit: undefined,
|
||||
offset: 0,
|
||||
populate: [],
|
||||
withDeleted: undefined,
|
||||
},
|
||||
},
|
||||
expect.any(Object)
|
||||
)
|
||||
})
|
||||
|
||||
it("should fail to retrieve a product", async function () {
|
||||
const productService = container.resolve("productService")
|
||||
const productRepository = container.resolve("productRepository")
|
||||
|
||||
const err = await productService
|
||||
.retrieve(nonExistingProductId)
|
||||
.catch((e) => e)
|
||||
|
||||
expect(productRepository.find).toHaveBeenCalledWith(
|
||||
{
|
||||
where: {
|
||||
id: nonExistingProductId,
|
||||
},
|
||||
options: {
|
||||
fields: undefined,
|
||||
limit: undefined,
|
||||
offset: 0,
|
||||
populate: [],
|
||||
withDeleted: undefined,
|
||||
},
|
||||
},
|
||||
expect.any(Object)
|
||||
)
|
||||
|
||||
expect(err.message).toBe(
|
||||
`Product with id: ${nonExistingProductId} was not found`
|
||||
)
|
||||
})
|
||||
|
||||
it("should list products", async function () {
|
||||
const productService = container.resolve("productService")
|
||||
const productRepository = container.resolve("productRepository")
|
||||
|
||||
const filters = {}
|
||||
const config = {
|
||||
relations: [],
|
||||
}
|
||||
|
||||
await productService.list(filters, config)
|
||||
|
||||
expect(productRepository.find).toHaveBeenCalledWith(
|
||||
{
|
||||
where: {},
|
||||
options: {
|
||||
fields: undefined,
|
||||
limit: 15,
|
||||
offset: 0,
|
||||
orderBy: {
|
||||
id: "ASC",
|
||||
},
|
||||
populate: [],
|
||||
withDeleted: undefined,
|
||||
},
|
||||
},
|
||||
expect.any(Object)
|
||||
)
|
||||
})
|
||||
|
||||
it("should list products with filters", async function () {
|
||||
const productService = container.resolve("productService")
|
||||
const productRepository = container.resolve("productRepository")
|
||||
|
||||
const filters = {
|
||||
tags: {
|
||||
value: {
|
||||
$in: ["test"],
|
||||
},
|
||||
},
|
||||
}
|
||||
const config = {
|
||||
relations: [],
|
||||
}
|
||||
|
||||
await productService.list(filters, config)
|
||||
|
||||
expect(productRepository.find).toHaveBeenCalledWith(
|
||||
{
|
||||
where: {
|
||||
tags: {
|
||||
value: {
|
||||
$in: ["test"],
|
||||
},
|
||||
},
|
||||
},
|
||||
options: {
|
||||
fields: undefined,
|
||||
limit: 15,
|
||||
offset: 0,
|
||||
orderBy: {
|
||||
id: "ASC",
|
||||
},
|
||||
populate: [],
|
||||
withDeleted: undefined,
|
||||
},
|
||||
},
|
||||
expect.any(Object)
|
||||
)
|
||||
})
|
||||
|
||||
it("should list products with filters and relations", async function () {
|
||||
const productService = container.resolve("productService")
|
||||
const productRepository = container.resolve("productRepository")
|
||||
|
||||
const filters = {
|
||||
tags: {
|
||||
value: {
|
||||
$in: ["test"],
|
||||
},
|
||||
},
|
||||
}
|
||||
const config = {
|
||||
relations: ["tags"],
|
||||
}
|
||||
|
||||
await productService.list(filters, config)
|
||||
|
||||
expect(productRepository.find).toHaveBeenCalledWith(
|
||||
{
|
||||
where: {
|
||||
tags: {
|
||||
value: {
|
||||
$in: ["test"],
|
||||
},
|
||||
},
|
||||
},
|
||||
options: {
|
||||
fields: undefined,
|
||||
limit: 15,
|
||||
offset: 0,
|
||||
orderBy: {
|
||||
id: "ASC",
|
||||
},
|
||||
withDeleted: undefined,
|
||||
populate: ["tags"],
|
||||
},
|
||||
},
|
||||
expect.any(Object)
|
||||
)
|
||||
})
|
||||
|
||||
it("should list and count the products with filters and relations", async function () {
|
||||
const productService = container.resolve("productService")
|
||||
const productRepository = container.resolve("productRepository")
|
||||
|
||||
const filters = {
|
||||
tags: {
|
||||
value: {
|
||||
$in: ["test"],
|
||||
},
|
||||
},
|
||||
}
|
||||
const config = {
|
||||
relations: ["tags"],
|
||||
}
|
||||
|
||||
await productService.listAndCount(filters, config)
|
||||
|
||||
expect(productRepository.findAndCount).toHaveBeenCalledWith(
|
||||
{
|
||||
where: {
|
||||
tags: {
|
||||
value: {
|
||||
$in: ["test"],
|
||||
},
|
||||
},
|
||||
},
|
||||
options: {
|
||||
fields: undefined,
|
||||
limit: 15,
|
||||
offset: 0,
|
||||
orderBy: {
|
||||
id: "ASC",
|
||||
},
|
||||
withDeleted: undefined,
|
||||
populate: ["tags"],
|
||||
},
|
||||
},
|
||||
expect.any(Object)
|
||||
)
|
||||
})
|
||||
})
|
||||
3
packages/modules/product/src/services/index.ts
Normal file
3
packages/modules/product/src/services/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { default as ProductService } from "./product"
|
||||
export { default as ProductCategoryService } from "./product-category"
|
||||
export { default as ProductModuleService } from "./product-module-service"
|
||||
169
packages/modules/product/src/services/product-category.ts
Normal file
169
packages/modules/product/src/services/product-category.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import { Context, DAL, FindConfig, ProductTypes } from "@medusajs/types"
|
||||
import {
|
||||
FreeTextSearchFilterKey,
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
ModulesSdkUtils,
|
||||
isDefined,
|
||||
} from "@medusajs/utils"
|
||||
import { ProductCategory } from "@models"
|
||||
import { ProductCategoryRepository } from "@repositories"
|
||||
|
||||
type InjectedDependencies = {
|
||||
productCategoryRepository: DAL.TreeRepositoryService
|
||||
}
|
||||
|
||||
export default class ProductCategoryService<
|
||||
TEntity extends ProductCategory = ProductCategory
|
||||
> {
|
||||
protected readonly productCategoryRepository_: DAL.TreeRepositoryService
|
||||
|
||||
constructor({ productCategoryRepository }: InjectedDependencies) {
|
||||
this.productCategoryRepository_ = productCategoryRepository
|
||||
}
|
||||
|
||||
@InjectManager("productCategoryRepository_")
|
||||
async retrieve(
|
||||
productCategoryId: string,
|
||||
config: FindConfig<ProductTypes.ProductCategoryDTO> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity> {
|
||||
if (!isDefined(productCategoryId)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`"productCategoryId" must be defined`
|
||||
)
|
||||
}
|
||||
|
||||
const queryOptions = ModulesSdkUtils.buildQuery<ProductCategory>(
|
||||
{
|
||||
id: productCategoryId,
|
||||
},
|
||||
config
|
||||
)
|
||||
|
||||
const transformOptions = {
|
||||
includeDescendantsTree: true,
|
||||
}
|
||||
|
||||
const productCategories = await this.productCategoryRepository_.find(
|
||||
queryOptions,
|
||||
transformOptions,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
if (!productCategories?.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`ProductCategory with id: ${productCategoryId} was not found`
|
||||
)
|
||||
}
|
||||
|
||||
return productCategories[0] as TEntity
|
||||
}
|
||||
|
||||
@InjectManager("productCategoryRepository_")
|
||||
async list(
|
||||
filters: ProductTypes.FilterableProductCategoryProps = {},
|
||||
config: FindConfig<ProductTypes.ProductCategoryDTO> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity[]> {
|
||||
const transformOptions = {
|
||||
includeDescendantsTree: filters?.include_descendants_tree || false,
|
||||
includeAncestorsTree: filters?.include_ancestors_tree || false,
|
||||
}
|
||||
delete filters.include_descendants_tree
|
||||
delete filters.include_ancestors_tree
|
||||
|
||||
// Apply free text search filter
|
||||
if (filters?.q) {
|
||||
config.filters ??= {}
|
||||
config.filters[FreeTextSearchFilterKey] = {
|
||||
value: filters.q,
|
||||
fromEntity: ProductCategory.name,
|
||||
}
|
||||
|
||||
delete filters.q
|
||||
}
|
||||
|
||||
const queryOptions = ModulesSdkUtils.buildQuery<ProductCategory>(
|
||||
filters,
|
||||
config
|
||||
)
|
||||
queryOptions.where ??= {}
|
||||
|
||||
return (await this.productCategoryRepository_.find(
|
||||
queryOptions,
|
||||
transformOptions,
|
||||
sharedContext
|
||||
)) as TEntity[]
|
||||
}
|
||||
|
||||
@InjectManager("productCategoryRepository_")
|
||||
async listAndCount(
|
||||
filters: ProductTypes.FilterableProductCategoryProps = {},
|
||||
config: FindConfig<ProductTypes.ProductCategoryDTO> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<[TEntity[], number]> {
|
||||
const transformOptions = {
|
||||
includeDescendantsTree: filters?.include_descendants_tree || false,
|
||||
includeAncestorsTree: filters?.include_ancestors_tree || false,
|
||||
}
|
||||
delete filters.include_descendants_tree
|
||||
delete filters.include_ancestors_tree
|
||||
|
||||
// Apply free text search filter
|
||||
if (filters?.q) {
|
||||
config.filters ??= {}
|
||||
config.filters[FreeTextSearchFilterKey] = {
|
||||
value: filters.q,
|
||||
fromEntity: ProductCategory.name,
|
||||
}
|
||||
|
||||
delete filters.q
|
||||
}
|
||||
|
||||
const queryOptions = ModulesSdkUtils.buildQuery<ProductCategory>(
|
||||
filters,
|
||||
config
|
||||
)
|
||||
queryOptions.where ??= {}
|
||||
|
||||
return (await this.productCategoryRepository_.findAndCount(
|
||||
queryOptions,
|
||||
transformOptions,
|
||||
sharedContext
|
||||
)) as [TEntity[], number]
|
||||
}
|
||||
|
||||
@InjectTransactionManager("productCategoryRepository_")
|
||||
async create(
|
||||
data: ProductTypes.CreateProductCategoryDTO,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity> {
|
||||
return (await (
|
||||
this.productCategoryRepository_ as unknown as ProductCategoryRepository
|
||||
).create(data, sharedContext)) as TEntity
|
||||
}
|
||||
|
||||
@InjectTransactionManager("productCategoryRepository_")
|
||||
async update(
|
||||
id: string,
|
||||
data: ProductTypes.UpdateProductCategoryDTO,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity> {
|
||||
return (await (
|
||||
this.productCategoryRepository_ as unknown as ProductCategoryRepository
|
||||
).update(id, data, sharedContext)) as TEntity
|
||||
}
|
||||
|
||||
@InjectTransactionManager("productCategoryRepository_")
|
||||
async delete(
|
||||
id: string,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<void> {
|
||||
await this.productCategoryRepository_.delete(id, sharedContext)
|
||||
}
|
||||
}
|
||||
1463
packages/modules/product/src/services/product-module-service.ts
Normal file
1463
packages/modules/product/src/services/product-module-service.ts
Normal file
File diff suppressed because it is too large
Load Diff
82
packages/modules/product/src/services/product.ts
Normal file
82
packages/modules/product/src/services/product.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import {
|
||||
Context,
|
||||
DAL,
|
||||
FindConfig,
|
||||
ProductTypes,
|
||||
BaseFilterable,
|
||||
FilterableProductProps,
|
||||
} from "@medusajs/types"
|
||||
import { InjectManager, MedusaContext, ModulesSdkUtils } from "@medusajs/utils"
|
||||
import { Product } from "@models"
|
||||
|
||||
type InjectedDependencies = {
|
||||
productRepository: DAL.RepositoryService
|
||||
}
|
||||
|
||||
type NormalizedFilterableProductProps = ProductTypes.FilterableProductProps & {
|
||||
categories?: {
|
||||
id: string | { $in: string[] }
|
||||
}
|
||||
}
|
||||
|
||||
export default class ProductService<
|
||||
TEntity extends Product = Product
|
||||
> extends ModulesSdkUtils.internalModuleServiceFactory<InjectedDependencies>(
|
||||
Product
|
||||
)<TEntity> {
|
||||
protected readonly productRepository_: DAL.RepositoryService<TEntity>
|
||||
|
||||
constructor({ productRepository }: InjectedDependencies) {
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
super(...arguments)
|
||||
|
||||
this.productRepository_ = productRepository
|
||||
}
|
||||
|
||||
@InjectManager("productRepository_")
|
||||
async list(
|
||||
filters: ProductTypes.FilterableProductProps = {},
|
||||
config: FindConfig<TEntity> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity[]> {
|
||||
return await super.list(
|
||||
ProductService.normalizeFilters(filters),
|
||||
config,
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
|
||||
@InjectManager("productRepository_")
|
||||
async listAndCount(
|
||||
filters: ProductTypes.FilterableProductProps = {},
|
||||
config: FindConfig<any> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<[TEntity[], number]> {
|
||||
return await super.listAndCount(
|
||||
ProductService.normalizeFilters(filters),
|
||||
config,
|
||||
sharedContext
|
||||
)
|
||||
}
|
||||
|
||||
protected static normalizeFilters(
|
||||
filters: FilterableProductProps = {}
|
||||
): NormalizedFilterableProductProps {
|
||||
const normalized = filters as NormalizedFilterableProductProps
|
||||
if (normalized.category_id) {
|
||||
if (Array.isArray(normalized.category_id)) {
|
||||
normalized.categories = {
|
||||
id: { $in: normalized.category_id },
|
||||
}
|
||||
} else {
|
||||
normalized.categories = {
|
||||
id: normalized.category_id as string,
|
||||
}
|
||||
}
|
||||
delete normalized.category_id
|
||||
}
|
||||
|
||||
return normalized
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user