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

This commit is contained in:
Oliver Windall Juhl authored and GitHub committed 2021-11-10 22:23:56 +01:00
1 parent 6f4e128944
commit 1e50aee4fe
5 files changed
+110 -16

No files matched your search

@@ -144,6 +144,9 @@ describe("/admin/products", () => {
const notExpected = [ const notExpected = [
expect.objectContaining({ status: "draft" }), expect.objectContaining({ status: "draft" }),
expect.objectContaining({ status: "rejected" }), expect.objectContaining({ status: "rejected" }),
expect.objectContaining({
id: "test-product_filtering_4",
}),
] ]
const response = await api 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 () => { it("returns a list of products in collection", async () => {
const api = useApi() const api = useApi()
@@ -271,4 +271,16 @@ module.exports = async (connection, data = {}) => {
}) })
await manager.save(product3) 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 { MedusaError } from "medusa-core-utils"
import { In, FindOperator, Raw } from "typeorm" import { FindOperator, In, Raw } from "typeorm"
/** /**
* Common functionality for Services * Common functionality for Services
@@ -71,6 +71,10 @@ class BaseService {
where: build(selector), where: build(selector),
} }
if ("deleted_at" in selector) {
query.withDeleted = true
}
if ("skip" in config) { if ("skip" in config) {
query.skip = config.skip query.skip = config.skip
} }
+37 -12
View File
@@ -1,8 +1,8 @@
import { flatten, groupBy, map, merge } from "lodash" import { flatten, groupBy, map, merge } from "lodash"
import { import {
OrderByCondition,
EntityRepository, EntityRepository,
FindManyOptions, FindManyOptions,
OrderByCondition,
Repository, Repository,
} from "typeorm" } from "typeorm"
import { Product } from "../models/product" import { Product } from "../models/product"
@@ -14,6 +14,7 @@ type CustomOptions = {
order?: OrderByCondition order?: OrderByCondition
skip?: number skip?: number
take?: number take?: number
withDeleted?: boolean
} }
type FindWithRelationsOptions = CustomOptions type FindWithRelationsOptions = CustomOptions
@@ -31,7 +32,7 @@ export class ProductRepository extends Repository<Product> {
private async queryProducts( private async queryProducts(
optionsWithoutRelations: FindWithRelationsOptions, optionsWithoutRelations: FindWithRelationsOptions,
shouldCount: boolean = false shouldCount = false
): Promise<[Product[], number]> { ): Promise<[Product[], number]> {
const tags = optionsWithoutRelations.where.tags const tags = optionsWithoutRelations.where.tags
delete 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 }) .andWhere(`tags.id IN (:...ids)`, { ids: tags._value })
} }
if (optionsWithoutRelations.withDeleted) {
qb = qb.withDeleted()
}
let entities: Product[] let entities: Product[]
let count = null let count = null
if (shouldCount) { if (shouldCount) {
@@ -79,7 +84,8 @@ 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
): 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]) => {
@@ -114,12 +120,22 @@ export class ProductRepository extends Repository<Product> {
) )
} }
return querybuilder if (withDeleted) {
.where( querybuilder = querybuilder
.where("products.id IN (:...entitiesIds)", {
entitiesIds: entityIds,
})
.withDeleted()
} else {
querybuilder = querybuilder.where(
"products.deleted_at IS NULL AND products.id IN (:...entitiesIds)", "products.deleted_at IS NULL AND products.id IN (:...entitiesIds)",
{ entitiesIds: entityIds } {
entitiesIds: entityIds,
}
) )
.getMany() }
return querybuilder.getMany()
}) })
).then(flatten) ).then(flatten)
@@ -133,7 +149,9 @@ export class ProductRepository extends Repository<Product> {
let count: number let count: number
let entities: Product[] let entities: Product[]
if (Array.isArray(idsOrOptionsWithoutRelations)) { if (Array.isArray(idsOrOptionsWithoutRelations)) {
entities = await this.findByIds(idsOrOptionsWithoutRelations) entities = await this.findByIds(idsOrOptionsWithoutRelations, {
withDeleted: idsOrOptionsWithoutRelations.withDeleted ?? false,
})
count = entities.length count = entities.length
} else { } else {
const result = await this.queryProducts( const result = await this.queryProducts(
@@ -161,8 +179,10 @@ export class ProductRepository extends Repository<Product> {
const groupedRelations = this.getGroupedRelations(relations) const groupedRelations = this.getGroupedRelations(relations)
const entitiesIdsWithRelations = await this.queryProductsWithIds( const entitiesIdsWithRelations = await this.queryProductsWithIds(
entitiesIds, entitiesIds,
groupedRelations groupedRelations,
idsOrOptionsWithoutRelations.withDeleted
) )
const entitiesAndRelations = entitiesIdsWithRelations.concat(entities) const entitiesAndRelations = entitiesIdsWithRelations.concat(entities)
const entitiesToReturn = const entitiesToReturn =
this.mergeEntitiesWithRelations(entitiesAndRelations) this.mergeEntitiesWithRelations(entitiesAndRelations)
@@ -172,11 +192,14 @@ export class ProductRepository extends Repository<Product> {
public async findWithRelations( public async findWithRelations(
relations: Array<keyof Product> = [], relations: Array<keyof Product> = [],
idsOrOptionsWithoutRelations: FindWithRelationsOptions = {} idsOrOptionsWithoutRelations: FindWithRelationsOptions = {},
withDeleted = false
): Promise<Product[]> { ): Promise<Product[]> {
let entities: Product[] let entities: Product[]
if (Array.isArray(idsOrOptionsWithoutRelations)) { if (Array.isArray(idsOrOptionsWithoutRelations)) {
entities = await this.findByIds(idsOrOptionsWithoutRelations) entities = await this.findByIds(idsOrOptionsWithoutRelations, {
withDeleted,
})
} else { } else {
const result = await this.queryProducts( const result = await this.queryProducts(
idsOrOptionsWithoutRelations, idsOrOptionsWithoutRelations,
@@ -198,8 +221,10 @@ export class ProductRepository extends Repository<Product> {
const groupedRelations = this.getGroupedRelations(relations) const groupedRelations = this.getGroupedRelations(relations)
const entitiesIdsWithRelations = await this.queryProductsWithIds( const entitiesIdsWithRelations = await this.queryProductsWithIds(
entitiesIds, entitiesIds,
groupedRelations groupedRelations,
withDeleted
) )
const entitiesAndRelations = entitiesIdsWithRelations.concat(entities) const entitiesAndRelations = entitiesIdsWithRelations.concat(entities)
const entitiesToReturn = const entitiesToReturn =
this.mergeEntitiesWithRelations(entitiesAndRelations) this.mergeEntitiesWithRelations(entitiesAndRelations)
+11 -3
View File
@@ -106,7 +106,8 @@ class ProductService extends BaseService {
const raw = await qb.getMany() const raw = await qb.getMany()
return productRepo.findWithRelations( return productRepo.findWithRelations(
relations, 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( const products = await productRepo.findWithRelations(
relations, relations,
raw.map((i) => i.id) raw.map((i) => i.id),
query.withDeleted ?? false
) )
return [products, count] return [products, count]
} }
@@ -799,7 +801,7 @@ class ProductService extends BaseService {
delete where.description delete where.description
delete where.title delete where.title
return productRepo let qb = productRepo
.createQueryBuilder("product") .createQueryBuilder("product")
.leftJoinAndSelect("product.variants", "variant") .leftJoinAndSelect("product.variants", "variant")
.leftJoinAndSelect("product.collection", "collection") .leftJoinAndSelect("product.collection", "collection")
@@ -814,6 +816,12 @@ class ProductService extends BaseService {
.orWhere(`collection.title ILIKE :q`, { q: `%${q}%` }) .orWhere(`collection.title ILIKE :q`, { q: `%${q}%` })
}) })
) )
if (query.withDeleted) {
qb = qb.withDeleted()
}
return qb
} }
} }