fix: storefront product filtering (#1189)
* fix: allow multiple ids in list + expand, fields param * fix: add filtering by title * fix: adds integration test * fix: adds integration test of product variant filtering * fix: integration tests * fix: unit tests * fix: refactor query param parsing
This commit is contained in:
1 parent
4be991c156
commit
e3655b53f7
12 files changed
+324
-28
No files matched your search
@@ -111,3 +111,60 @@ Object {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`/store/variants lists by title 1`] = `
|
||||||
|
Object {
|
||||||
|
"variants": Array [
|
||||||
|
Object {
|
||||||
|
"allow_backorder": false,
|
||||||
|
"barcode": null,
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"ean": null,
|
||||||
|
"height": null,
|
||||||
|
"hs_code": null,
|
||||||
|
"id": Any<String>,
|
||||||
|
"inventory_quantity": 12,
|
||||||
|
"length": null,
|
||||||
|
"manage_inventory": true,
|
||||||
|
"material": null,
|
||||||
|
"metadata": null,
|
||||||
|
"mid_code": null,
|
||||||
|
"options": Array [
|
||||||
|
Object {
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": Any<String>,
|
||||||
|
"metadata": null,
|
||||||
|
"option_id": Any<String>,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"value": "Handcrafted",
|
||||||
|
"variant_id": Any<String>,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"origin_country": null,
|
||||||
|
"prices": Array [
|
||||||
|
Object {
|
||||||
|
"amount": 100,
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"currency_code": "usd",
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": Any<String>,
|
||||||
|
"region_id": null,
|
||||||
|
"sale_amount": null,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"variant_id": Any<String>,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"product": Any<Object>,
|
||||||
|
"product_id": Any<String>,
|
||||||
|
"sku": null,
|
||||||
|
"title": "test2",
|
||||||
|
"upc": null,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"weight": null,
|
||||||
|
"width": null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -280,3 +280,56 @@ Object {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`/store/products list params works with expand and fields 1`] = `
|
||||||
|
Object {
|
||||||
|
"count": 2,
|
||||||
|
"limit": 1,
|
||||||
|
"offset": 0,
|
||||||
|
"products": Array [
|
||||||
|
Object {
|
||||||
|
"id": Any<String>,
|
||||||
|
"title": "testprod",
|
||||||
|
"variants": Array [
|
||||||
|
Object {
|
||||||
|
"allow_backorder": false,
|
||||||
|
"barcode": null,
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"deleted_at": null,
|
||||||
|
"ean": null,
|
||||||
|
"height": null,
|
||||||
|
"hs_code": null,
|
||||||
|
"id": Any<String>,
|
||||||
|
"inventory_quantity": 10,
|
||||||
|
"length": null,
|
||||||
|
"manage_inventory": true,
|
||||||
|
"material": null,
|
||||||
|
"metadata": null,
|
||||||
|
"mid_code": null,
|
||||||
|
"origin_country": null,
|
||||||
|
"prices": Array [
|
||||||
|
Object {
|
||||||
|
"amount": 100,
|
||||||
|
"created_at": Any<String>,
|
||||||
|
"currency_code": "usd",
|
||||||
|
"deleted_at": null,
|
||||||
|
"id": Any<String>,
|
||||||
|
"region_id": null,
|
||||||
|
"sale_amount": null,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"variant_id": Any<String>,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"product_id": Any<String>,
|
||||||
|
"sku": null,
|
||||||
|
"title": "test-variant",
|
||||||
|
"upc": null,
|
||||||
|
"updated_at": Any<String>,
|
||||||
|
"weight": null,
|
||||||
|
"width": null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -2,6 +2,7 @@ const path = require("path")
|
|||||||
const setupServer = require("../../../helpers/setup-server")
|
const setupServer = require("../../../helpers/setup-server")
|
||||||
const { useApi } = require("../../../helpers/use-api")
|
const { useApi } = require("../../../helpers/use-api")
|
||||||
const { initDb, useDb } = require("../../../helpers/use-db")
|
const { initDb, useDb } = require("../../../helpers/use-db")
|
||||||
|
const { simpleProductFactory } = require("../../factories")
|
||||||
|
|
||||||
const productSeeder = require("../../helpers/product-seeder")
|
const productSeeder = require("../../helpers/product-seeder")
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
@@ -24,6 +25,24 @@ describe("/store/variants", () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
try {
|
try {
|
||||||
await productSeeder(dbConnection)
|
await productSeeder(dbConnection)
|
||||||
|
|
||||||
|
await simpleProductFactory(
|
||||||
|
dbConnection,
|
||||||
|
{
|
||||||
|
title: "prod",
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
title: "test1",
|
||||||
|
inventory_quantity: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "test2",
|
||||||
|
inventory_quantity: 12,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
100
|
||||||
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
throw err
|
throw err
|
||||||
@@ -93,6 +112,43 @@ describe("/store/variants", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("lists by title", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const response = await api.get(
|
||||||
|
"/store/variants?title[]=test1&title[]=test2&inventory_quantity[gt]=10"
|
||||||
|
)
|
||||||
|
expect(response.data).toMatchSnapshot({
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
id: expect.any(String),
|
||||||
|
title: "test2",
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
id: expect.any(String),
|
||||||
|
option_id: expect.any(String),
|
||||||
|
variant_id: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
id: expect.any(String),
|
||||||
|
variant_id: expect.any(String),
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
product: expect.any(Object),
|
||||||
|
product_id: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it("/test-variant", async () => {
|
it("/test-variant", async () => {
|
||||||
const api = useApi()
|
const api = useApi()
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const setupServer = require("../../../helpers/setup-server")
|
|||||||
const { useApi } = require("../../../helpers/use-api")
|
const { useApi } = require("../../../helpers/use-api")
|
||||||
const { initDb, useDb } = require("../../../helpers/use-db")
|
const { initDb, useDb } = require("../../../helpers/use-db")
|
||||||
|
|
||||||
|
const { simpleProductFactory } = require("../../factories")
|
||||||
const productSeeder = require("../../helpers/store-product-seeder")
|
const productSeeder = require("../../helpers/store-product-seeder")
|
||||||
const adminSeeder = require("../../helpers/admin-seeder")
|
const adminSeeder = require("../../helpers/admin-seeder")
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
@@ -222,6 +223,74 @@ describe("/store/products", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("list params", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
try {
|
||||||
|
await adminSeeder(dbConnection)
|
||||||
|
|
||||||
|
const p1 = await simpleProductFactory(
|
||||||
|
dbConnection,
|
||||||
|
{
|
||||||
|
title: "testprod",
|
||||||
|
status: "published",
|
||||||
|
variants: [{ title: "test-variant" }],
|
||||||
|
},
|
||||||
|
11
|
||||||
|
)
|
||||||
|
|
||||||
|
const p2 = await simpleProductFactory(
|
||||||
|
dbConnection,
|
||||||
|
{
|
||||||
|
title: "testprod3",
|
||||||
|
status: "published",
|
||||||
|
variants: [{ title: "test-variant1" }],
|
||||||
|
},
|
||||||
|
12
|
||||||
|
)
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const db = useDb()
|
||||||
|
await db.teardown()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("works with expand and fields", async () => {
|
||||||
|
const api = useApi()
|
||||||
|
|
||||||
|
const response = await api.get(
|
||||||
|
"/store/products?expand=variants,variants.prices&fields=id,title&limit=1"
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.data).toMatchSnapshot({
|
||||||
|
products: [
|
||||||
|
{
|
||||||
|
id: expect.any(String),
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
id: expect.any(String),
|
||||||
|
product_id: expect.any(String),
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
created_at: expect.any(String),
|
||||||
|
updated_at: expect.any(String),
|
||||||
|
id: expect.any(String),
|
||||||
|
variant_id: expect.any(String),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("/store/products/:id", () => {
|
describe("/store/products/:id", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
export type ProductFactoryData = {
|
export type ProductFactoryData = {
|
||||||
id?: string
|
id?: string
|
||||||
is_giftcard?: boolean
|
is_giftcard?: boolean
|
||||||
|
status?: string
|
||||||
title?: string
|
title?: string
|
||||||
type?: string
|
type?: string
|
||||||
options?: { id: string; title: string }[]
|
options?: { id: string; title: string }[]
|
||||||
@@ -54,6 +55,7 @@ export const simpleProductFactory = async (
|
|||||||
const toSave = manager.create(Product, {
|
const toSave = manager.create(Product, {
|
||||||
id: prodId,
|
id: prodId,
|
||||||
type_id: typeId,
|
type_id: typeId,
|
||||||
|
status: data.status,
|
||||||
title: data.title || faker.commerce.productName(),
|
title: data.title || faker.commerce.productName(),
|
||||||
is_giftcard: data.is_giftcard || false,
|
is_giftcard: data.is_giftcard || false,
|
||||||
discountable: !data.is_giftcard,
|
discountable: !data.is_giftcard,
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ describe("GET /admin/products/:id", () => {
|
|||||||
"id",
|
"id",
|
||||||
"title",
|
"title",
|
||||||
"subtitle",
|
"subtitle",
|
||||||
|
"status",
|
||||||
|
"external_id",
|
||||||
"description",
|
"description",
|
||||||
"handle",
|
"handle",
|
||||||
"is_giftcard",
|
"is_giftcard",
|
||||||
@@ -51,6 +53,7 @@ describe("GET /admin/products/:id", () => {
|
|||||||
"material",
|
"material",
|
||||||
"created_at",
|
"created_at",
|
||||||
"updated_at",
|
"updated_at",
|
||||||
|
"deleted_at",
|
||||||
"metadata",
|
"metadata",
|
||||||
],
|
],
|
||||||
relations: [
|
relations: [
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ export const defaultAdminProductFields = [
|
|||||||
"id",
|
"id",
|
||||||
"title",
|
"title",
|
||||||
"subtitle",
|
"subtitle",
|
||||||
|
"status",
|
||||||
|
"external_id",
|
||||||
"description",
|
"description",
|
||||||
"handle",
|
"handle",
|
||||||
"is_giftcard",
|
"is_giftcard",
|
||||||
@@ -91,6 +93,7 @@ export const defaultAdminProductFields = [
|
|||||||
"material",
|
"material",
|
||||||
"created_at",
|
"created_at",
|
||||||
"updated_at",
|
"updated_at",
|
||||||
|
"deleted_at",
|
||||||
"metadata",
|
"metadata",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -98,6 +101,8 @@ export const allowedAdminProductFields = [
|
|||||||
"id",
|
"id",
|
||||||
"title",
|
"title",
|
||||||
"subtitle",
|
"subtitle",
|
||||||
|
"status",
|
||||||
|
"external_id",
|
||||||
"description",
|
"description",
|
||||||
"handle",
|
"handle",
|
||||||
"is_giftcard",
|
"is_giftcard",
|
||||||
@@ -116,6 +121,7 @@ export const allowedAdminProductFields = [
|
|||||||
"material",
|
"material",
|
||||||
"created_at",
|
"created_at",
|
||||||
"updated_at",
|
"updated_at",
|
||||||
|
"deleted_at",
|
||||||
"metadata",
|
"metadata",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ import {
|
|||||||
ValidateNested,
|
ValidateNested,
|
||||||
} from "class-validator"
|
} from "class-validator"
|
||||||
import { omit, pickBy, identity } from "lodash"
|
import { omit, pickBy, identity } from "lodash"
|
||||||
import { MedusaError } from "medusa-core-utils"
|
|
||||||
import { defaultStoreProductsRelations } from "."
|
import { defaultStoreProductsRelations } from "."
|
||||||
import { ProductService } from "../../../../services"
|
import { ProductService } from "../../../../services"
|
||||||
import { DateComparisonOperator } from "../../../../types/common"
|
import { DateComparisonOperator } from "../../../../types/common"
|
||||||
import { validator } from "../../../../utils/validator"
|
import { validator } from "../../../../utils/validator"
|
||||||
|
import { IsType } from "../../../../utils/validators/is-type"
|
||||||
import { optionalBooleanMapper } from "../../../../utils/validators/is-boolean"
|
import { optionalBooleanMapper } from "../../../../utils/validators/is-boolean"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,6 +64,8 @@ export default async (req, res) => {
|
|||||||
const validated = await validator(StoreGetProductsParams, req.query)
|
const validated = await validator(StoreGetProductsParams, req.query)
|
||||||
|
|
||||||
const filterableFields: StoreGetProductsParams = omit(validated, [
|
const filterableFields: StoreGetProductsParams = omit(validated, [
|
||||||
|
"fields",
|
||||||
|
"expand",
|
||||||
"limit",
|
"limit",
|
||||||
"offset",
|
"offset",
|
||||||
])
|
])
|
||||||
@@ -71,8 +73,23 @@ export default async (req, res) => {
|
|||||||
// get only published products for store endpoint
|
// get only published products for store endpoint
|
||||||
filterableFields["status"] = ["published"]
|
filterableFields["status"] = ["published"]
|
||||||
|
|
||||||
|
let includeFields: string[] = []
|
||||||
|
if (validated.fields) {
|
||||||
|
const set = new Set(validated.fields.split(","))
|
||||||
|
set.add("id")
|
||||||
|
includeFields = [...set]
|
||||||
|
}
|
||||||
|
|
||||||
|
let expandFields: string[] = []
|
||||||
|
if (validated.expand) {
|
||||||
|
expandFields = validated.expand.split(",")
|
||||||
|
}
|
||||||
|
|
||||||
const listConfig = {
|
const listConfig = {
|
||||||
relations: defaultStoreProductsRelations,
|
select: includeFields.length ? includeFields : undefined,
|
||||||
|
relations: expandFields.length
|
||||||
|
? expandFields
|
||||||
|
: defaultStoreProductsRelations,
|
||||||
skip: validated.offset,
|
skip: validated.offset,
|
||||||
take: validated.limit,
|
take: validated.limit,
|
||||||
}
|
}
|
||||||
@@ -91,6 +108,14 @@ export default async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class StoreGetProductsPaginationParams {
|
export class StoreGetProductsPaginationParams {
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
fields?: string
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
expand?: string
|
||||||
|
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@Type(() => Number)
|
@Type(() => Number)
|
||||||
@@ -103,9 +128,9 @@ export class StoreGetProductsPaginationParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class StoreGetProductsParams extends StoreGetProductsPaginationParams {
|
export class StoreGetProductsParams extends StoreGetProductsPaginationParams {
|
||||||
@IsString()
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
id?: string
|
@IsType([String, [String]])
|
||||||
|
id?: string | string[]
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
import { Type } from "class-transformer"
|
import { Type } from "class-transformer"
|
||||||
|
import { omit } from "lodash"
|
||||||
import { IsInt, IsOptional, IsString } from "class-validator"
|
import { IsInt, IsOptional, IsString } from "class-validator"
|
||||||
import { defaultStoreVariantRelations } from "."
|
import { defaultStoreVariantRelations } from "."
|
||||||
|
import { FilterableProductVariantProps } from "../../../../types/product-variant"
|
||||||
import ProductVariantService from "../../../../services/product-variant"
|
import ProductVariantService from "../../../../services/product-variant"
|
||||||
import { validator } from "../../../../utils/validator"
|
import { validator } from "../../../../utils/validator"
|
||||||
|
import { IsType } from "../../../../utils/validators/is-type"
|
||||||
|
import { NumericalComparisonOperator } from "../../../../types/common"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [get] /variants
|
* @oas [get] /variants
|
||||||
@@ -29,17 +33,14 @@ import { validator } from "../../../../utils/validator"
|
|||||||
* $ref: "#/components/schemas/product_variant"
|
* $ref: "#/components/schemas/product_variant"
|
||||||
*/
|
*/
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const { limit, offset, expand, ids } = await validator(
|
const validated = await validator(StoreGetVariantsParams, req.query)
|
||||||
StoreGetVariantsParams,
|
const { expand, offset, limit } = validated
|
||||||
req.query
|
|
||||||
)
|
|
||||||
|
|
||||||
let expandFields: string[] = []
|
let expandFields: string[] = []
|
||||||
if (expand) {
|
if (expand) {
|
||||||
expandFields = expand.split(",")
|
expandFields = expand.split(",")
|
||||||
}
|
}
|
||||||
|
|
||||||
let selector = {}
|
|
||||||
const listConfig = {
|
const listConfig = {
|
||||||
relations: expandFields.length
|
relations: expandFields.length
|
||||||
? expandFields
|
? expandFields
|
||||||
@@ -48,14 +49,21 @@ export default async (req, res) => {
|
|||||||
take: limit,
|
take: limit,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ids) {
|
const filterableFields: FilterableProductVariantProps = omit(validated, [
|
||||||
selector = { id: ids.split(",") }
|
"ids",
|
||||||
|
"limit",
|
||||||
|
"offset",
|
||||||
|
"expand",
|
||||||
|
])
|
||||||
|
|
||||||
|
if (validated.ids) {
|
||||||
|
filterableFields.id = validated.ids.split(",")
|
||||||
}
|
}
|
||||||
|
|
||||||
const variantService: ProductVariantService = req.scope.resolve(
|
const variantService: ProductVariantService = req.scope.resolve(
|
||||||
"productVariantService"
|
"productVariantService"
|
||||||
)
|
)
|
||||||
const variants = await variantService.list(selector, listConfig)
|
const variants = await variantService.list(filterableFields, listConfig)
|
||||||
|
|
||||||
res.json({ variants })
|
res.json({ variants })
|
||||||
}
|
}
|
||||||
@@ -78,4 +86,16 @@ export class StoreGetVariantsParams {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
ids?: string
|
ids?: string
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsType([String, [String]])
|
||||||
|
id?: string | string[]
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsType([String, [String]])
|
||||||
|
title?: string | string[]
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsType([Number, NumericalComparisonOperator])
|
||||||
|
inventory_quantity?: number | NumericalComparisonOperator
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,7 @@ import { Product } from "../models/product"
|
|||||||
type DefaultWithoutRelations = Omit<FindManyOptions<Product>, "relations">
|
type DefaultWithoutRelations = Omit<FindManyOptions<Product>, "relations">
|
||||||
|
|
||||||
type CustomOptions = {
|
type CustomOptions = {
|
||||||
|
select?: DefaultWithoutRelations["select"]
|
||||||
where?: DefaultWithoutRelations["where"] & {
|
where?: DefaultWithoutRelations["where"] & {
|
||||||
tags?: FindOperator<ProductTag>
|
tags?: FindOperator<ProductTag>
|
||||||
}
|
}
|
||||||
@@ -77,9 +78,7 @@ export class ProductRepository extends Repository<Product> {
|
|||||||
return [entities, count]
|
return [entities, count]
|
||||||
}
|
}
|
||||||
|
|
||||||
private getGroupedRelations(
|
private getGroupedRelations(relations: Array<keyof Product>): {
|
||||||
relations: Array<keyof Product>
|
|
||||||
): {
|
|
||||||
[toplevel: string]: string[]
|
[toplevel: string]: string[]
|
||||||
} {
|
} {
|
||||||
const groupedRelations: { [toplevel: string]: string[] } = {}
|
const groupedRelations: { [toplevel: string]: string[] } = {}
|
||||||
@@ -98,12 +97,17 @@ export class ProductRepository extends Repository<Product> {
|
|||||||
private async queryProductsWithIds(
|
private async queryProductsWithIds(
|
||||||
entityIds: string[],
|
entityIds: string[],
|
||||||
groupedRelations: { [toplevel: string]: string[] },
|
groupedRelations: { [toplevel: string]: string[] },
|
||||||
withDeleted = false
|
withDeleted = false,
|
||||||
|
select: (keyof Product)[] = []
|
||||||
): Promise<Product[]> {
|
): Promise<Product[]> {
|
||||||
const entitiesIdsWithRelations = await Promise.all(
|
const entitiesIdsWithRelations = await Promise.all(
|
||||||
Object.entries(groupedRelations).map(([toplevel, rels]) => {
|
Object.entries(groupedRelations).map(([toplevel, rels]) => {
|
||||||
let querybuilder = this.createQueryBuilder("products")
|
let querybuilder = this.createQueryBuilder("products")
|
||||||
|
|
||||||
|
if (select && select.length) {
|
||||||
|
querybuilder.select(select.map((f) => `products.${f}`))
|
||||||
|
}
|
||||||
|
|
||||||
if (toplevel === "variants") {
|
if (toplevel === "variants") {
|
||||||
querybuilder = querybuilder
|
querybuilder = querybuilder
|
||||||
.leftJoinAndSelect(
|
.leftJoinAndSelect(
|
||||||
@@ -193,13 +197,13 @@ export class ProductRepository extends Repository<Product> {
|
|||||||
const entitiesIdsWithRelations = await this.queryProductsWithIds(
|
const entitiesIdsWithRelations = await this.queryProductsWithIds(
|
||||||
entitiesIds,
|
entitiesIds,
|
||||||
groupedRelations,
|
groupedRelations,
|
||||||
idsOrOptionsWithoutRelations.withDeleted
|
idsOrOptionsWithoutRelations.withDeleted,
|
||||||
|
idsOrOptionsWithoutRelations.select
|
||||||
)
|
)
|
||||||
|
|
||||||
const entitiesAndRelations = entitiesIdsWithRelations.concat(entities)
|
const entitiesAndRelations = entitiesIdsWithRelations.concat(entities)
|
||||||
const entitiesToReturn = this.mergeEntitiesWithRelations(
|
const entitiesToReturn =
|
||||||
entitiesAndRelations
|
this.mergeEntitiesWithRelations(entitiesAndRelations)
|
||||||
)
|
|
||||||
|
|
||||||
return [entitiesToReturn, count]
|
return [entitiesToReturn, count]
|
||||||
}
|
}
|
||||||
@@ -240,9 +244,8 @@ export class ProductRepository extends Repository<Product> {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const entitiesAndRelations = entitiesIdsWithRelations.concat(entities)
|
const entitiesAndRelations = entitiesIdsWithRelations.concat(entities)
|
||||||
const entitiesToReturn = this.mergeEntitiesWithRelations(
|
const entitiesToReturn =
|
||||||
entitiesAndRelations
|
this.mergeEntitiesWithRelations(entitiesAndRelations)
|
||||||
)
|
|
||||||
|
|
||||||
return entitiesToReturn
|
return entitiesToReturn
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -327,7 +327,9 @@ class ProductService extends BaseService {
|
|||||||
return existing.id
|
return existing.id
|
||||||
}
|
}
|
||||||
|
|
||||||
const created = productTypeRepository.create(type)
|
const created = productTypeRepository.create({
|
||||||
|
value: type.value,
|
||||||
|
})
|
||||||
const result = await productTypeRepository.save(created)
|
const result = await productTypeRepository.save(created)
|
||||||
|
|
||||||
return result.id
|
return result.id
|
||||||
|
|||||||
@@ -70,8 +70,8 @@ export class FilterableProductVariantProps {
|
|||||||
@IsType([String, [String], StringComparisonOperator])
|
@IsType([String, [String], StringComparisonOperator])
|
||||||
id?: string | string[] | StringComparisonOperator
|
id?: string | string[] | StringComparisonOperator
|
||||||
|
|
||||||
@IsString()
|
@IsType([String, [String]])
|
||||||
title?: string
|
title?: string | string[]
|
||||||
|
|
||||||
@IsType([String, [String]])
|
@IsType([String, [String]])
|
||||||
product_id?: string | string[]
|
product_id?: string | string[]
|
||||||
@@ -88,8 +88,8 @@ export class FilterableProductVariantProps {
|
|||||||
@IsType([String])
|
@IsType([String])
|
||||||
upc?: string
|
upc?: string
|
||||||
|
|
||||||
@IsNumber()
|
@IsType([Number, NumericalComparisonOperator])
|
||||||
inventory_quantity?: number
|
inventory_quantity?: number | NumericalComparisonOperator
|
||||||
|
|
||||||
@IsBoolean()
|
@IsBoolean()
|
||||||
allow_backorder?: boolean
|
allow_backorder?: boolean
|
||||||
|
|||||||
Reference in new issue
Block a user