feat: Allow retrieval of soft-deleted products (#723)

This commit is contained in:
Oliver Windall Juhl
2021-11-10 22:23:56 +01:00
committed by GitHub
parent 6f4e128944
commit 1e50aee4fe
5 changed files with 111 additions and 17 deletions
@@ -144,6 +144,9 @@ describe("/admin/products", () => {
const notExpected = [
expect.objectContaining({ status: "draft" }),
expect.objectContaining({ status: "rejected" }),
expect.objectContaining({
id: "test-product_filtering_4",
}),
]
const response = await api
@@ -175,6 +178,48 @@ describe("/admin/products", () => {
}
})
it("returns a list of deleted products with free text query", async () => {
const api = useApi()
const response = await api
.get("/admin/products?deleted_at[gt]=01-26-1990&q=test", {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
expect(response.data.products).toEqual([
expect.objectContaining({
id: "test-product_filtering_4",
}),
])
})
it("returns a list of deleted products", async () => {
const api = useApi()
const response = await api
.get("/admin/products?deleted_at[gt]=01-26-1990", {
headers: {
Authorization: "Bearer test_token",
},
})
.catch((err) => {
console.log(err)
})
expect(response.status).toEqual(200)
expect(response.data.products).toEqual([
expect.objectContaining({
id: "test-product_filtering_4",
}),
])
})
it("returns a list of products in collection", async () => {
const api = useApi()
@@ -271,4 +271,16 @@ module.exports = async (connection, data = {}) => {
})
await manager.save(product3)
const product4 = manager.create(Product, {
id: "test-product_filtering_4",
handle: "test-product_filtering_4",
title: "Test product filtering 4",
profile_id: defaultProfile.id,
description: "test-product-description",
status: "proposed",
deleted_at: new Date().toISOString(),
})
await manager.save(product4)
}
@@ -1,5 +1,5 @@
import { MedusaError } from "medusa-core-utils"
import { In, FindOperator, Raw } from "typeorm"
import { FindOperator, In, Raw } from "typeorm"
/**
* Common functionality for Services
@@ -63,13 +63,17 @@ class BaseService {
return acc
}, {})
return where
}
const query = {
where: build(selector),
}
if ("deleted_at" in selector) {
query.withDeleted = true
}
if ("skip" in config) {
query.skip = config.skip
+37 -12
View File
@@ -1,8 +1,8 @@
import { flatten, groupBy, map, merge } from "lodash"
import {
OrderByCondition,
EntityRepository,
FindManyOptions,
OrderByCondition,
Repository,
} from "typeorm"
import { Product } from "../models/product"
@@ -14,6 +14,7 @@ type CustomOptions = {
order?: OrderByCondition
skip?: number
take?: number
withDeleted?: boolean
}
type FindWithRelationsOptions = CustomOptions
@@ -31,7 +32,7 @@ export class ProductRepository extends Repository<Product> {
private async queryProducts(
optionsWithoutRelations: FindWithRelationsOptions,
shouldCount: boolean = false
shouldCount = false
): Promise<[Product[], number]> {
const tags = optionsWithoutRelations.where.tags
delete optionsWithoutRelations.where.tags
@@ -48,6 +49,10 @@ export class ProductRepository extends Repository<Product> {
.andWhere(`tags.id IN (:...ids)`, { ids: tags._value })
}
if (optionsWithoutRelations.withDeleted) {
qb = qb.withDeleted()
}
let entities: Product[]
let count = null
if (shouldCount) {
@@ -79,7 +84,8 @@ export class ProductRepository extends Repository<Product> {
private async queryProductsWithIds(
entityIds: string[],
groupedRelations: { [toplevel: string]: string[] }
groupedRelations: { [toplevel: string]: string[] },
withDeleted = false
): Promise<Product[]> {
const entitiesIdsWithRelations = await Promise.all(
Object.entries(groupedRelations).map(([toplevel, rels]) => {
@@ -114,12 +120,22 @@ export class ProductRepository extends Repository<Product> {
)
}
return querybuilder
.where(
if (withDeleted) {
querybuilder = querybuilder
.where("products.id IN (:...entitiesIds)", {
entitiesIds: entityIds,
})
.withDeleted()
} else {
querybuilder = querybuilder.where(
"products.deleted_at IS NULL AND products.id IN (:...entitiesIds)",
{ entitiesIds: entityIds }
{
entitiesIds: entityIds,
}
)
.getMany()
}
return querybuilder.getMany()
})
).then(flatten)
@@ -133,7 +149,9 @@ export class ProductRepository extends Repository<Product> {
let count: number
let entities: Product[]
if (Array.isArray(idsOrOptionsWithoutRelations)) {
entities = await this.findByIds(idsOrOptionsWithoutRelations)
entities = await this.findByIds(idsOrOptionsWithoutRelations, {
withDeleted: idsOrOptionsWithoutRelations.withDeleted ?? false,
})
count = entities.length
} else {
const result = await this.queryProducts(
@@ -161,8 +179,10 @@ export class ProductRepository extends Repository<Product> {
const groupedRelations = this.getGroupedRelations(relations)
const entitiesIdsWithRelations = await this.queryProductsWithIds(
entitiesIds,
groupedRelations
groupedRelations,
idsOrOptionsWithoutRelations.withDeleted
)
const entitiesAndRelations = entitiesIdsWithRelations.concat(entities)
const entitiesToReturn =
this.mergeEntitiesWithRelations(entitiesAndRelations)
@@ -172,11 +192,14 @@ export class ProductRepository extends Repository<Product> {
public async findWithRelations(
relations: Array<keyof Product> = [],
idsOrOptionsWithoutRelations: FindWithRelationsOptions = {}
idsOrOptionsWithoutRelations: FindWithRelationsOptions = {},
withDeleted = false
): Promise<Product[]> {
let entities: Product[]
if (Array.isArray(idsOrOptionsWithoutRelations)) {
entities = await this.findByIds(idsOrOptionsWithoutRelations)
entities = await this.findByIds(idsOrOptionsWithoutRelations, {
withDeleted,
})
} else {
const result = await this.queryProducts(
idsOrOptionsWithoutRelations,
@@ -198,8 +221,10 @@ export class ProductRepository extends Repository<Product> {
const groupedRelations = this.getGroupedRelations(relations)
const entitiesIdsWithRelations = await this.queryProductsWithIds(
entitiesIds,
groupedRelations
groupedRelations,
withDeleted
)
const entitiesAndRelations = entitiesIdsWithRelations.concat(entities)
const entitiesToReturn =
this.mergeEntitiesWithRelations(entitiesAndRelations)
+11 -3
View File
@@ -106,7 +106,8 @@ class ProductService extends BaseService {
const raw = await qb.getMany()
return productRepo.findWithRelations(
relations,
raw.map((i) => i.id)
raw.map((i) => i.id),
query.withDeleted ?? false
)
}
@@ -140,7 +141,8 @@ class ProductService extends BaseService {
const products = await productRepo.findWithRelations(
relations,
raw.map((i) => i.id)
raw.map((i) => i.id),
query.withDeleted ?? false
)
return [products, count]
}
@@ -799,7 +801,7 @@ class ProductService extends BaseService {
delete where.description
delete where.title
return productRepo
let qb = productRepo
.createQueryBuilder("product")
.leftJoinAndSelect("product.variants", "variant")
.leftJoinAndSelect("product.collection", "collection")
@@ -814,6 +816,12 @@ class ProductService extends BaseService {
.orWhere(`collection.title ILIKE :q`, { q: `%${q}%` })
})
)
if (query.withDeleted) {
qb = qb.withDeleted()
}
return qb
}
}