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:
Stevche Radevski authored and GitHub committed 2024-03-08 11:40:00 +00:00
1 parent 272fb6d328
commit 056e3e9599
4 files changed
+67 -6

No files matched your search

@@ -140,7 +140,6 @@ export class AdminGetProductsParams extends extendedFindParamsMixin({
// @Transform(({ value }) => optionalBooleanMapper.get(value.toLowerCase())) // @Transform(({ value }) => optionalBooleanMapper.get(value.toLowerCase()))
// include_category_children?: boolean // include_category_children?: boolean
// TODO: The OperatorMap and DateOperator are slightly different, so the date comparisons is a breaking change.
@IsOptional() @IsOptional()
@ValidateNested() @ValidateNested()
@Type(() => OperatorMapValidator) @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 () => { it("should emit events through eventBus", async () => {
const eventBusSpy = jest.spyOn(EventBusService.prototype, "emit") const eventBusSpy = jest.spyOn(EventBusService.prototype, "emit")
const data = buildProductAndRelationsData({ const data = buildProductAndRelationsData({
+29
View File
@@ -595,12 +595,21 @@ export interface ProductOptionValueDTO {
* @prop categories - Filters on a product's categories. * @prop categories - Filters on a product's categories.
* @prop collection_id - Filters a product by its associated collections. * @prop collection_id - Filters a product by its associated collections.
*/ */
export interface FilterableProductProps export interface FilterableProductProps
extends BaseFilterable<FilterableProductProps> { extends BaseFilterable<FilterableProductProps> {
/** /**
* Search through the products' attributes, such as titles and descriptions, using this search term. * Search through the products' attributes, such as titles and descriptions, using this search term.
*/ */
q?: string 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. * The handles to filter products by.
*/ */
@@ -609,6 +618,10 @@ export interface FilterableProductProps
* The IDs to filter products by. * The IDs to filter products by.
*/ */
id?: string | string[] id?: string | string[]
/**
* Filters only or excluding gift card products
*/
is_giftcard?: boolean
/** /**
* Filters on a product's tags. * Filters on a product's tags.
*/ */
@@ -635,6 +648,10 @@ export interface FilterableProductProps
*/ */
is_active?: boolean 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. * 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. * Filters a product by the IDs of their associated collections.
*/ */
collection_id?: string | string[] | OperatorMap<string> 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>
} }
/** /**
+21 -5
View File
@@ -3,12 +3,20 @@ import { deduplicate, isDefined, isObject } from "../common"
import { SoftDeletableFilterKey } from "../dal" 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>( export function buildQuery<T = any, TDto = any>(
filters: Record<string, any> = {}, filters: Record<string, any> = {},
config: FindConfig<TDto> & { primaryKeyFields?: string | string[] } = {} config: FindConfig<TDto> & { primaryKeyFields?: string | string[] } = {}
): DAL.FindOptions<T> { ): DAL.FindOptions<T> {
const where: DAL.FilterQuery<T> = {} const where: DAL.FilterQuery<T> = {}
buildWhere(filters, where) const filterFlags: FilterFlags = {}
buildWhere(filters, where, filterFlags)
const primaryKeyFieldArray = isDefined(config.primaryKeyFields) const primaryKeyFieldArray = isDefined(config.primaryKeyFields)
? !Array.isArray(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"] findOptions.orderBy = config.order as DAL.OptionsQuery<T>["orderBy"]
} }
if (config.withDeleted) { if (config.withDeleted || filterFlags.withDeleted) {
findOptions.filters ??= {} findOptions.filters ??= {}
findOptions.filters[SoftDeletableFilterKey] = { findOptions.filters[SoftDeletableFilterKey] = {
withDeleted: true, withDeleted: true,
@@ -61,12 +69,20 @@ export function buildQuery<T = any, TDto = any>(
return { where, options: findOptions } 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)) { for (let [prop, value] of Object.entries(filters)) {
if (prop === DELETED_AT_FIELD_NAME) {
flags.withDeleted = true
}
if (["$or", "$and"].includes(prop)) { if (["$or", "$and"].includes(prop)) {
where[prop] = value.map((val) => { where[prop] = value.map((val) => {
const deepWhere = {} const deepWhere = {}
buildWhere(val, deepWhere) buildWhere(val, deepWhere, flags)
return deepWhere return deepWhere
}) })
continue continue
@@ -80,7 +96,7 @@ function buildWhere(filters: Record<string, any> = {}, where = {}) {
if (isObject(value)) { if (isObject(value)) {
where[prop] = {} where[prop] = {}
buildWhere(value, where[prop]) buildWhere(value, where[prop], flags)
continue continue
} }