Feat(medusa, medusa-js, medusa-react): Include sales channels in related queries as an optional expand parameter (#1816)

**What**
- Add `transformQuery` to get endpoints for product, order and cart
  - ensure that the default relations (when getting a singular entity) includes sales channels when enabled
- Add `EmptyQueryParams` class in common types to prevent query parameters while using `transformQuery`
- update product-, order- and cartFactory to include sales channels if provided
- remove `packages/medusa/src/controllers/products/admin-list-products.ts`

**Testing** 
- expands sales channel for single order
- expands sales channels for orders with expand parameter 
- returns single product with sales channel 
- expands sales channels for products with expand parameter
- returns cart with sales channel for single cart

Fixes CORE-293

Co-authored-by: Sebastian Rindom <7554214+srindom@users.noreply.github.com>
Co-authored-by: Adrien de Peretti <25098370+adrien2p@users.noreply.github.com>
This commit is contained in:
Philip Korsholm
2022-07-11 16:45:01 +00:00
committed by GitHub
co-authored by Sebastian Rindom Adrien de Peretti
parent fb4cfc3c3c
commit 19f35ba6aa
22 changed files with 510 additions and 255 deletions
@@ -1,94 +0,0 @@
import { AwilixContainer } from "awilix"
import { AdminProductsListRes } from "../../api"
import { pickBy } from "lodash"
import { MedusaError } from "medusa-core-utils"
import { Product } from "../../models/product"
import { ProductService, PricingService } from "../../services"
import { getListConfig } from "../../utils/get-query-config"
import { FilterableProductProps } from "../../types/product"
import { PricedProduct } from "../../types/pricing"
type ListContext = {
limit: number
offset: number
order?: string
fields?: string
expand?: string
allowedFields?: string[]
defaultFields?: (keyof Product)[]
defaultRelations?: string[]
}
const listAndCount = async (
scope: AwilixContainer,
query: FilterableProductProps,
body?: object,
context: ListContext = { limit: 50, offset: 0 }
): Promise<AdminProductsListRes> => {
const { limit, offset, allowedFields, defaultFields, defaultRelations } =
context
const productService: ProductService = scope.resolve("productService")
const pricingService: PricingService = scope.resolve("pricingService")
let includeFields: (keyof Product)[] | undefined
if (context.fields) {
includeFields = context.fields.split(",") as (keyof Product)[]
}
let expandFields: string[] | undefined
if (context.expand) {
expandFields = context.expand.split(",")
}
let orderBy: { [k: symbol]: "DESC" | "ASC" } | undefined
if (typeof context.order !== "undefined") {
let orderField = context.order
if (context.order.startsWith("-")) {
const [, field] = context.order.split("-")
orderField = field
orderBy = { [field]: "DESC" }
} else {
orderBy = { [context.order]: "ASC" }
}
if (!(allowedFields || []).includes(orderField)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Order field must be a valid product field"
)
}
}
const listConfig = getListConfig<Product>(
defaultFields ?? [],
defaultRelations ?? [],
includeFields,
expandFields,
limit,
offset,
orderBy
)
const [rawProducts, count] = await productService.listAndCount(
pickBy(query, (val) => typeof val !== "undefined"),
listConfig
)
let products: (Product | PricedProduct)[] = rawProducts
const includesPricing = ["variants", "variants.prices"].every((relation) =>
listConfig?.relations?.includes(relation)
)
if (includesPricing) {
products = await pricingService.setProductPrices(rawProducts)
}
return {
products,
count,
offset,
limit,
}
}
export default listAndCount