feat: Set withDeleted as true if filtering on deleted_at in modules (#6626)
Similar convention already existed in v1, so we are just applying this for the abstract module factory
This commit is contained in:
@@ -140,7 +140,6 @@ export class AdminGetProductsParams extends extendedFindParamsMixin({
|
||||
// @Transform(({ value }) => optionalBooleanMapper.get(value.toLowerCase()))
|
||||
// include_category_children?: boolean
|
||||
|
||||
// TODO: The OperatorMap and DateOperator are slightly different, so the date comparisons is a breaking change.
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => OperatorMapValidator)
|
||||
|
||||
@@ -765,6 +765,23 @@ describe("ProductModuleService products", function () {
|
||||
}
|
||||
})
|
||||
|
||||
it("should retrieve soft-deleted products if filtered on deleted_at", async () => {
|
||||
const data = buildProductAndRelationsData({
|
||||
images,
|
||||
thumbnail: images[0],
|
||||
})
|
||||
|
||||
const products = await module.create([data])
|
||||
|
||||
await module.softDelete([products[0].id])
|
||||
|
||||
const softDeleted = await module.list({
|
||||
deleted_at: { $gt: "01-01-2022" },
|
||||
})
|
||||
|
||||
expect(softDeleted).toHaveLength(1)
|
||||
})
|
||||
|
||||
it("should emit events through eventBus", async () => {
|
||||
const eventBusSpy = jest.spyOn(EventBusService.prototype, "emit")
|
||||
const data = buildProductAndRelationsData({
|
||||
|
||||
@@ -595,12 +595,21 @@ export interface ProductOptionValueDTO {
|
||||
* @prop categories - Filters on a product's categories.
|
||||
* @prop collection_id - Filters a product by its associated collections.
|
||||
*/
|
||||
|
||||
export interface FilterableProductProps
|
||||
extends BaseFilterable<FilterableProductProps> {
|
||||
/**
|
||||
* Search through the products' attributes, such as titles and descriptions, using this search term.
|
||||
*/
|
||||
q?: string
|
||||
/**
|
||||
* The status to filter products by
|
||||
*/
|
||||
status?: ProductStatus | ProductStatus[]
|
||||
/**
|
||||
* The titles to filter products by.
|
||||
*/
|
||||
title?: string | string[]
|
||||
/**
|
||||
* The handles to filter products by.
|
||||
*/
|
||||
@@ -609,6 +618,10 @@ export interface FilterableProductProps
|
||||
* The IDs to filter products by.
|
||||
*/
|
||||
id?: string | string[]
|
||||
/**
|
||||
* Filters only or excluding gift card products
|
||||
*/
|
||||
is_giftcard?: boolean
|
||||
/**
|
||||
* Filters on a product's tags.
|
||||
*/
|
||||
@@ -635,6 +648,10 @@ export interface FilterableProductProps
|
||||
*/
|
||||
is_active?: boolean
|
||||
}
|
||||
/**
|
||||
* Filter a product by the ID of the associated type
|
||||
*/
|
||||
type_id?: string | string[]
|
||||
/**
|
||||
* Filter a product by the IDs of their associated categories.
|
||||
*/
|
||||
@@ -643,6 +660,18 @@ export interface FilterableProductProps
|
||||
* Filters a product by the IDs of their associated collections.
|
||||
*/
|
||||
collection_id?: string | string[] | OperatorMap<string>
|
||||
/**
|
||||
* Filters a product based on when it was created
|
||||
*/
|
||||
created_at?: OperatorMap<string>
|
||||
/**
|
||||
* Filters a product based on when it was updated
|
||||
*/
|
||||
updated_at?: OperatorMap<string>
|
||||
/**
|
||||
* Filters soft-deleted products based on the date they were deleted at.
|
||||
*/
|
||||
deleted_at?: OperatorMap<string>
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,12 +3,20 @@ import { deduplicate, isDefined, isObject } from "../common"
|
||||
|
||||
import { SoftDeletableFilterKey } from "../dal"
|
||||
|
||||
// Following convention here is fine, we can make it configurable if needed.
|
||||
const DELETED_AT_FIELD_NAME = "deleted_at"
|
||||
|
||||
type FilterFlags = {
|
||||
withDeleted?: boolean
|
||||
}
|
||||
|
||||
export function buildQuery<T = any, TDto = any>(
|
||||
filters: Record<string, any> = {},
|
||||
config: FindConfig<TDto> & { primaryKeyFields?: string | string[] } = {}
|
||||
): DAL.FindOptions<T> {
|
||||
const where: DAL.FilterQuery<T> = {}
|
||||
buildWhere(filters, where)
|
||||
const filterFlags: FilterFlags = {}
|
||||
buildWhere(filters, where, filterFlags)
|
||||
|
||||
const primaryKeyFieldArray = isDefined(config.primaryKeyFields)
|
||||
? !Array.isArray(config.primaryKeyFields)
|
||||
@@ -43,7 +51,7 @@ export function buildQuery<T = any, TDto = any>(
|
||||
findOptions.orderBy = config.order as DAL.OptionsQuery<T>["orderBy"]
|
||||
}
|
||||
|
||||
if (config.withDeleted) {
|
||||
if (config.withDeleted || filterFlags.withDeleted) {
|
||||
findOptions.filters ??= {}
|
||||
findOptions.filters[SoftDeletableFilterKey] = {
|
||||
withDeleted: true,
|
||||
@@ -61,12 +69,20 @@ export function buildQuery<T = any, TDto = any>(
|
||||
return { where, options: findOptions }
|
||||
}
|
||||
|
||||
function buildWhere(filters: Record<string, any> = {}, where = {}) {
|
||||
function buildWhere(
|
||||
filters: Record<string, any> = {},
|
||||
where = {},
|
||||
flags: FilterFlags = {}
|
||||
) {
|
||||
for (let [prop, value] of Object.entries(filters)) {
|
||||
if (prop === DELETED_AT_FIELD_NAME) {
|
||||
flags.withDeleted = true
|
||||
}
|
||||
|
||||
if (["$or", "$and"].includes(prop)) {
|
||||
where[prop] = value.map((val) => {
|
||||
const deepWhere = {}
|
||||
buildWhere(val, deepWhere)
|
||||
buildWhere(val, deepWhere, flags)
|
||||
return deepWhere
|
||||
})
|
||||
continue
|
||||
@@ -80,7 +96,7 @@ function buildWhere(filters: Record<string, any> = {}, where = {}) {
|
||||
|
||||
if (isObject(value)) {
|
||||
where[prop] = {}
|
||||
buildWhere(value, where[prop])
|
||||
buildWhere(value, where[prop], flags)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user