fix: product ordering
This commit is contained in:
@@ -19,7 +19,7 @@ class BaseService {
|
||||
* Used to build TypeORM queries.
|
||||
*/
|
||||
buildQuery_(selector, config = {}) {
|
||||
const build = obj => {
|
||||
const build = (obj) => {
|
||||
const where = Object.entries(obj).reduce((acc, [key, value]) => {
|
||||
switch (true) {
|
||||
case value instanceof FindOperator:
|
||||
@@ -49,11 +49,11 @@ class BaseService {
|
||||
})
|
||||
|
||||
acc[key] = Raw(
|
||||
a =>
|
||||
(a) =>
|
||||
subquery
|
||||
.map((s, index) => `${a} ${s.operator} :${index}`)
|
||||
.join(" AND "),
|
||||
subquery.map(s => s.value)
|
||||
subquery.map((s) => s.value)
|
||||
)
|
||||
break
|
||||
default:
|
||||
@@ -149,7 +149,7 @@ class BaseService {
|
||||
return work(this.transactionManager_)
|
||||
} else {
|
||||
const temp = this.manager_
|
||||
const doWork = async m => {
|
||||
const doWork = async (m) => {
|
||||
this.manager_ = m
|
||||
this.transactionManager_ = m
|
||||
try {
|
||||
@@ -167,17 +167,17 @@ class BaseService {
|
||||
if (isolation) {
|
||||
let result
|
||||
try {
|
||||
result = await this.manager_.transaction(isolation, m => doWork(m))
|
||||
result = await this.manager_.transaction(isolation, (m) => doWork(m))
|
||||
return result
|
||||
} catch (error) {
|
||||
if (this.shouldRetryTransaction(error)) {
|
||||
return this.manager_.transaction(isolation, m => doWork(m))
|
||||
return this.manager_.transaction(isolation, (m) => doWork(m))
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.manager_.transaction(m => doWork(m))
|
||||
return this.manager_.transaction((m) => doWork(m))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ class BaseService {
|
||||
*/
|
||||
runDecorators_(obj, fields = [], expandFields = []) {
|
||||
return this.decorators_.reduce(async (acc, next) => {
|
||||
return acc.then(res => next(res, fields, expandFields)).catch(() => acc)
|
||||
return acc.then((res) => next(res, fields, expandFields)).catch(() => acc)
|
||||
}, Promise.resolve(obj))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,13 @@ async function loadProductsIntoSearchEngine(container) {
|
||||
const productService = container.resolve("productService")
|
||||
|
||||
const TAKE = 20
|
||||
let skip = 0
|
||||
let hasMore = true
|
||||
|
||||
let lastSeenId = ""
|
||||
|
||||
while (hasMore) {
|
||||
const products = await productService.list(
|
||||
{},
|
||||
{ id: { gt: lastSeenId } },
|
||||
{
|
||||
select: [
|
||||
"id",
|
||||
@@ -39,8 +40,7 @@ async function loadProductsIntoSearchEngine(container) {
|
||||
"options",
|
||||
],
|
||||
take: TAKE,
|
||||
skip,
|
||||
order: { created_at: "ASC" },
|
||||
order: { id: "ASC" },
|
||||
}
|
||||
)
|
||||
|
||||
@@ -50,7 +50,7 @@ async function loadProductsIntoSearchEngine(container) {
|
||||
products,
|
||||
indexTypes.products
|
||||
)
|
||||
skip += products.length
|
||||
lastSeenId = products[products.length - 1].id
|
||||
} else {
|
||||
hasMore = false
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export class OrderRepository extends Repository<Order> {
|
||||
|
||||
const entitiesAndRelationsById = groupBy(entitiesAndRelations, "id")
|
||||
|
||||
return map(entities, e => merge({}, ...entitiesAndRelationsById[e.id]))
|
||||
return map(entities, (e) => merge({}, ...entitiesAndRelationsById[e.id]))
|
||||
}
|
||||
|
||||
public async findOneWithRelations(
|
||||
|
||||
@@ -1,22 +1,35 @@
|
||||
import { flatten, groupBy, map, merge } from "lodash"
|
||||
import { EntityRepository, FindManyOptions, Repository } from "typeorm"
|
||||
import {
|
||||
OrderByCondition,
|
||||
EntityRepository,
|
||||
FindManyOptions,
|
||||
Repository,
|
||||
} from "typeorm"
|
||||
import { Product } from "../models/product"
|
||||
|
||||
type DefaultWithoutRelations = Omit<FindManyOptions<Product>, "relations">
|
||||
|
||||
type CustomOptions = {
|
||||
where?: DefaultWithoutRelations["where"] & { tags?: string[] }
|
||||
order?: OrderByCondition
|
||||
skip?: number
|
||||
take?: number
|
||||
}
|
||||
|
||||
type FindWithRelationsOptions = CustomOptions
|
||||
|
||||
@EntityRepository(Product)
|
||||
export class ProductRepository extends Repository<Product> {
|
||||
public async findWithRelations(
|
||||
relations: Array<keyof Product> = [],
|
||||
idsOrOptionsWithoutRelations: Omit<
|
||||
FindManyOptions<Product>,
|
||||
"relations"
|
||||
> = {}
|
||||
idsOrOptionsWithoutRelations: FindWithRelationsOptions = {}
|
||||
): Promise<Product[]> {
|
||||
let entities
|
||||
let entities: Product[]
|
||||
if (Array.isArray(idsOrOptionsWithoutRelations)) {
|
||||
entities = await this.findByIds(idsOrOptionsWithoutRelations)
|
||||
} else {
|
||||
// Since tags are in a one-to-many realtion they cant be included in a
|
||||
// regular query, to solve this add the join on tags seperately if
|
||||
// Since tags are in a one-to-many realtion they cant be included in a
|
||||
// regular query, to solve this add the join on tags seperately if
|
||||
// the query exists
|
||||
const tags = idsOrOptionsWithoutRelations.where.tags
|
||||
delete idsOrOptionsWithoutRelations.where.tags
|
||||
@@ -25,17 +38,15 @@ export class ProductRepository extends Repository<Product> {
|
||||
.where(idsOrOptionsWithoutRelations.where)
|
||||
.skip(idsOrOptionsWithoutRelations.skip)
|
||||
.take(idsOrOptionsWithoutRelations.take)
|
||||
|
||||
if (tags) {
|
||||
.orderBy(idsOrOptionsWithoutRelations.order)
|
||||
|
||||
if (tags) {
|
||||
qb = qb
|
||||
.leftJoinAndSelect("product.tags", "tags")
|
||||
.andWhere(
|
||||
`tags.id IN (:...ids)`, { ids: tags._value}
|
||||
)
|
||||
.andWhere(`tags.id IN (:...ids)`, { ids: tags._value })
|
||||
}
|
||||
|
||||
entities = await qb
|
||||
.getMany()
|
||||
|
||||
entities = await qb.getMany()
|
||||
}
|
||||
const entitiesIds = entities.map(({ id }) => id)
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ class ProductService extends BaseService {
|
||||
.select(["product.id"])
|
||||
.where(where)
|
||||
.andWhere(
|
||||
new Brackets(qb => {
|
||||
new Brackets((qb) => {
|
||||
qb.where(`product.description ILIKE :q`, { q: `%${q}%` })
|
||||
.orWhere(`product.title ILIKE :q`, { q: `%${q}%` })
|
||||
.orWhere(`variant.title ILIKE :q`, { q: `%${q}%` })
|
||||
@@ -140,7 +140,7 @@ class ProductService extends BaseService {
|
||||
|
||||
return productRepo.findWithRelations(
|
||||
rels,
|
||||
raw.map(i => i.id)
|
||||
raw.map((i) => i.id)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ class ProductService extends BaseService {
|
||||
* @return {Promise} resolves to the creation result.
|
||||
*/
|
||||
async create(productObject) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productRepo = manager.getCustomRepository(this.productRepository_)
|
||||
const optionRepo = manager.getCustomRepository(
|
||||
this.productOptionRepository_
|
||||
@@ -316,7 +316,7 @@ class ProductService extends BaseService {
|
||||
product = await productRepo.save(product)
|
||||
|
||||
product.options = await Promise.all(
|
||||
options.map(async o => {
|
||||
options.map(async (o) => {
|
||||
const res = optionRepo.create({ ...o, product_id: product.id })
|
||||
await optionRepo.save(res)
|
||||
return res
|
||||
@@ -366,7 +366,7 @@ class ProductService extends BaseService {
|
||||
* @return {Promise} resolves to the update result.
|
||||
*/
|
||||
async update(productId, update) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productRepo = manager.getCustomRepository(this.productRepository_)
|
||||
const productVariantRepo = manager.getCustomRepository(
|
||||
this.productVariantRepository_
|
||||
@@ -376,15 +376,8 @@ class ProductService extends BaseService {
|
||||
relations: ["variants", "tags", "images"],
|
||||
})
|
||||
|
||||
const {
|
||||
variants,
|
||||
metadata,
|
||||
options,
|
||||
images,
|
||||
tags,
|
||||
type,
|
||||
...rest
|
||||
} = update
|
||||
const { variants, metadata, options, images, tags, type, ...rest } =
|
||||
update
|
||||
|
||||
if (!product.thumbnail && !update.thumbnail && images?.length) {
|
||||
product.thumbnail = images[0]
|
||||
@@ -409,7 +402,7 @@ class ProductService extends BaseService {
|
||||
if (variants) {
|
||||
// Iterate product variants and update their properties accordingly
|
||||
for (const variant of product.variants) {
|
||||
const exists = variants.find(v => v.id && variant.id === v.id)
|
||||
const exists = variants.find((v) => v.id && variant.id === v.id)
|
||||
if (!exists) {
|
||||
await productVariantRepo.remove(variant)
|
||||
}
|
||||
@@ -420,7 +413,7 @@ class ProductService extends BaseService {
|
||||
newVariant.variant_rank = i
|
||||
|
||||
if (newVariant.id) {
|
||||
const variant = product.variants.find(v => v.id === newVariant.id)
|
||||
const variant = product.variants.find((v) => v.id === newVariant.id)
|
||||
|
||||
if (!variant) {
|
||||
throw new MedusaError(
|
||||
@@ -471,7 +464,7 @@ class ProductService extends BaseService {
|
||||
* @return {Promise} empty promise
|
||||
*/
|
||||
async delete(productId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productRepo = manager.getCustomRepository(this.productRepository_)
|
||||
|
||||
// Should not fail, if product does not exist, since delete is idempotent
|
||||
@@ -500,7 +493,7 @@ class ProductService extends BaseService {
|
||||
* @return {Promise} the result of the model update operation
|
||||
*/
|
||||
async addOption(productId, optionTitle) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productOptionRepo = manager.getCustomRepository(
|
||||
this.productOptionRepository_
|
||||
)
|
||||
@@ -509,7 +502,7 @@ class ProductService extends BaseService {
|
||||
relations: ["options", "variants"],
|
||||
})
|
||||
|
||||
if (product.options.find(o => o.title === optionTitle)) {
|
||||
if (product.options.find((o) => o.title === optionTitle)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.DUPLICATE_ERROR,
|
||||
`An option with the title: ${optionTitle} already exists`
|
||||
@@ -539,7 +532,7 @@ class ProductService extends BaseService {
|
||||
}
|
||||
|
||||
async reorderVariants(productId, variantOrder) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productRepo = manager.getCustomRepository(this.productRepository_)
|
||||
|
||||
const product = await this.retrieve(productId, {
|
||||
@@ -553,8 +546,8 @@ class ProductService extends BaseService {
|
||||
)
|
||||
}
|
||||
|
||||
product.variants = variantOrder.map(vId => {
|
||||
const variant = product.variants.find(v => v.id === vId)
|
||||
product.variants = variantOrder.map((vId) => {
|
||||
const variant = product.variants.find((v) => v.id === vId)
|
||||
if (!variant) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
@@ -583,7 +576,7 @@ class ProductService extends BaseService {
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async reorderOptions(productId, optionOrder) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productRepo = manager.getCustomRepository(this.productRepository_)
|
||||
|
||||
const product = await this.retrieve(productId, { relations: ["options"] })
|
||||
@@ -595,8 +588,8 @@ class ProductService extends BaseService {
|
||||
)
|
||||
}
|
||||
|
||||
product.options = optionOrder.map(oId => {
|
||||
const option = product.options.find(o => o.id === oId)
|
||||
product.options = optionOrder.map((oId) => {
|
||||
const option = product.options.find((o) => o.id === oId)
|
||||
if (!option) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
@@ -624,7 +617,7 @@ class ProductService extends BaseService {
|
||||
* @return {Promise} the updated product
|
||||
*/
|
||||
async updateOption(productId, optionId, data) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productOptionRepo = manager.getCustomRepository(
|
||||
this.productOptionRepository_
|
||||
)
|
||||
@@ -634,7 +627,8 @@ class ProductService extends BaseService {
|
||||
const { title, values } = data
|
||||
|
||||
const optionExists = product.options.some(
|
||||
o => o.title.toUpperCase() === title.toUpperCase() && o.id !== optionId
|
||||
(o) =>
|
||||
o.title.toUpperCase() === title.toUpperCase() && o.id !== optionId
|
||||
)
|
||||
if (optionExists) {
|
||||
throw new MedusaError(
|
||||
@@ -673,7 +667,7 @@ class ProductService extends BaseService {
|
||||
* @return {Promise} the updated product
|
||||
*/
|
||||
async deleteOption(productId, optionId) {
|
||||
return this.atomicPhase_(async manager => {
|
||||
return this.atomicPhase_(async (manager) => {
|
||||
const productOptionRepo = manager.getCustomRepository(
|
||||
this.productOptionRepository_
|
||||
)
|
||||
@@ -701,17 +695,17 @@ class ProductService extends BaseService {
|
||||
const firstVariant = product.variants[0]
|
||||
|
||||
const valueToMatch = firstVariant.options.find(
|
||||
o => o.option_id === optionId
|
||||
(o) => o.option_id === optionId
|
||||
).value
|
||||
|
||||
const equalsFirst = await Promise.all(
|
||||
product.variants.map(async v => {
|
||||
const option = v.options.find(o => o.option_id === optionId)
|
||||
product.variants.map(async (v) => {
|
||||
const option = v.options.find((o) => o.option_id === optionId)
|
||||
return option.value === valueToMatch
|
||||
})
|
||||
)
|
||||
|
||||
if (!equalsFirst.every(v => v)) {
|
||||
if (!equalsFirst.every((v) => v)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`To delete an option, first delete all variants, such that when option is deleted, no duplicate variants will exist.`
|
||||
|
||||
Reference in New Issue
Block a user