feat(medusa): Allow to query order/product by SC (#1867)

**What**

Allow to query the products and orders by sales channels

**How**

Updating the existing end points and repositories  (if necessary) to take a new query params that is sales_channel_id as an array of string

**Tests**

Add new integration tests

Fixes CORE-295
Fixes CORE-288
This commit is contained in:
Adrien de Peretti
2022-07-19 16:54:20 +00:00
committed by GitHub
parent 0e5f0d8cd6
commit 110c995a6a
11 changed files with 168 additions and 96 deletions
@@ -23,6 +23,7 @@ import { Type } from "class-transformer"
* - (query) region_id {string} to search for.
* - (query) currency_code {string} to search for.
* - (query) tax_rate {string} to search for.
* - (query) sales_chanel_id {string[]} to retrieve products in.
* - (query) cancelled_at {DateComparisonOperator} Date comparison for when resulting orders was cancelled, i.e. less than, greater than etc.
* - (query) created_at {DateComparisonOperator} Date comparison for when resulting orders was created, i.e. less than, greater than etc.
* - (query) updated_at {DateComparisonOperator} Date comparison for when resulting orders was updated, i.e. less than, greater than etc.
@@ -9,9 +9,11 @@ import {
ValidateNested,
} from "class-validator"
import { ProductStatus } from "../../../../models"
import { DateComparisonOperator } from "../../../../types/common"
import {
DateComparisonOperator,
extendedFindParamsMixin,
} from "../../../../types/common"
import { FilterableProductProps } from "../../../../types/product"
import { AdminGetProductsPaginationParams } from "../products"
import PriceListService from "../../../../services/price-list"
import { Request } from "express"
@@ -89,7 +91,9 @@ export default async (req: Request, res) => {
})
}
export class AdminGetPriceListsPriceListProductsParams extends AdminGetProductsPaginationParams {
export class AdminGetPriceListsPriceListProductsParams extends extendedFindParamsMixin(
{ limit: 50 }
) {
@IsString()
@IsOptional()
id?: string
@@ -1,18 +1,9 @@
import { Transform, Type } from "class-transformer"
import {
IsArray,
IsBoolean,
IsEnum,
IsNumber,
IsOptional,
IsString,
ValidateNested,
} from "class-validator"
import { Product, ProductStatus } from "../../../../models/product"
import { DateComparisonOperator } from "../../../../types/common"
import { optionalBooleanMapper } from "../../../../utils/validators/is-boolean"
import { Type } from "class-transformer"
import { IsNumber, IsOptional, IsString } from "class-validator"
import { Product } from "../../../../models"
import { PricedProduct } from "../../../../types/pricing"
import { PricingService, ProductService } from "../../../../services"
import { FilterableProductProps } from "../../../../types/product"
/**
* @oas [get] /products
@@ -32,6 +23,7 @@ import { PricingService, ProductService } from "../../../../services"
* - (query) is_giftcard {string} Search for giftcards using is_giftcard=true.
* - (query) type {string} to search for.
* - (query) order {string} to retrieve products in.
* - (query) sales_chanel_id {string[]} to retrieve products in.
* - (query) deleted_at {DateComparisonOperator} Date comparison for when resulting products was deleted, i.e. less than, greater than etc.
* - (query) created_at {DateComparisonOperator} Date comparison for when resulting products was created, i.e. less than, greater than etc.
* - (query) updated_at {DateComparisonOperator} Date comparison for when resulting products was updated, i.e. less than, greater than etc.
@@ -90,7 +82,7 @@ export default async (req, res) => {
})
}
export class AdminGetProductsPaginationParams {
export class AdminGetProductsParams extends FilterableProductProps {
@IsNumber()
@IsOptional()
@Type(() => Number)
@@ -109,69 +101,3 @@ export class AdminGetProductsPaginationParams {
@IsOptional()
fields?: string
}
export class AdminGetProductsParams extends AdminGetProductsPaginationParams {
@IsString()
@IsOptional()
id?: string
@IsString()
@IsOptional()
q?: string
@IsOptional()
@IsEnum(ProductStatus, { each: true })
status?: ProductStatus[]
@IsArray()
@IsOptional()
collection_id?: string[]
@IsArray()
@IsOptional()
tags?: string[]
@IsArray()
@IsOptional()
price_list_id?: string[]
@IsString()
@IsOptional()
title?: string
@IsString()
@IsOptional()
description?: string
@IsString()
@IsOptional()
handle?: string
@IsBoolean()
@IsOptional()
@Transform(({ value }) => optionalBooleanMapper.get(value.toLowerCase()))
is_giftcard?: boolean
@IsString()
@IsOptional()
type?: string
@IsString()
@IsOptional()
order?: string
@IsOptional()
@ValidateNested()
@Type(() => DateComparisonOperator)
created_at?: DateComparisonOperator
@IsOptional()
@ValidateNested()
@Type(() => DateComparisonOperator)
updated_at?: DateComparisonOperator
@ValidateNested()
@IsOptional()
@Type(() => DateComparisonOperator)
deleted_at?: DateComparisonOperator
}
+1 -1
View File
@@ -146,7 +146,7 @@ export class Product extends SoftDeletableEntity {
metadata: Record<string, unknown> | null
@FeatureFlagDecorators("sales_channels", [
ManyToMany(() => SalesChannel, { cascade: true }),
ManyToMany(() => SalesChannel, { cascade: ["remove", "soft-remove"] }),
JoinTable({
name: "product_sales_channel",
joinColumn: {
+1 -1
View File
@@ -1,6 +1,6 @@
import { flatten, groupBy, map, merge } from "lodash"
import { EntityRepository, FindManyOptions, Repository } from "typeorm"
import { Order } from "../models/order"
import { Order } from "../models"
@EntityRepository(Order)
export class OrderRepository extends Repository<Order> {
@@ -13,6 +13,7 @@ import {
Selector,
WithRequiredProperty,
} from "../types/common"
import { SalesChannel } from "../models";
export type ProductSelector = Omit<Selector<Product>, "tags"> & {
tags: FindOperator<string[]>
@@ -26,6 +27,7 @@ export type DefaultWithoutRelations = Omit<
export type FindWithoutRelationsOptions = DefaultWithoutRelations & {
where: DefaultWithoutRelations["where"] & {
price_list_id?: FindOperator<PriceList>
sales_channel_id?: FindOperator<SalesChannel>
}
}
@@ -50,6 +52,9 @@ export class ProductRepository extends Repository<Product> {
const price_lists = optionsWithoutRelations?.where?.price_list_id
delete optionsWithoutRelations?.where?.price_list_id
const sales_channels = optionsWithoutRelations?.where?.sales_channel_id
delete optionsWithoutRelations?.where?.sales_channel_id
const qb = this.createQueryBuilder("product")
.select(["product.id"])
.skip(optionsWithoutRelations.skip)
@@ -88,6 +93,15 @@ export class ProductRepository extends Repository<Product> {
})
}
if (sales_channels) {
qb.innerJoin(
"product.sales_channels",
"sales_channels",
"sales_channels.id IN (:...sales_channels_ids)",
{ sales_channels_ids: sales_channels.value }
)
}
if (optionsWithoutRelations.withDeleted) {
qb.withDeleted()
}
+4
View File
@@ -99,6 +99,10 @@ export class AdminListOrdersSelector {
@IsOptional()
tax_rate?: string
@IsArray()
@IsOptional()
sales_channel_id?: string[]
@IsOptional()
@ValidateNested()
@Type(() => DateComparisonOperator)
+4
View File
@@ -68,6 +68,10 @@ export class FilterableProductProps {
@IsOptional()
type?: string
@IsArray()
@IsOptional()
sales_channel_id?: string[]
@IsOptional()
@ValidateNested()
@Type(() => DateComparisonOperator)