diff --git a/.changeset/fluffy-impalas-wave.md b/.changeset/fluffy-impalas-wave.md new file mode 100644 index 0000000000..3d7a2865de --- /dev/null +++ b/.changeset/fluffy-impalas-wave.md @@ -0,0 +1,6 @@ +--- +"@medusajs/medusa": patch +"@medusajs/utils": patch +--- + +feat(medusa, medusa-utils): Add support for multiple where condition on the same column diff --git a/packages/medusa/src/utils/__tests__/build-query.spec.ts b/packages/medusa/src/utils/__tests__/build-query.spec.ts index c6dfbf75e8..b1ff6795cf 100644 --- a/packages/medusa/src/utils/__tests__/build-query.spec.ts +++ b/packages/medusa/src/utils/__tests__/build-query.spec.ts @@ -1,5 +1,18 @@ -import { FindOptionsOrder, FindOptionsSelect, In, MoreThan, Not } from "typeorm" -import { addOrderToSelect, buildLegacyFieldsListFrom, buildQuery } from "../build-query" +import { + And, + FindOptionsOrder, + FindOptionsSelect, + In, + LessThanOrEqual, + MoreThan, + MoreThanOrEqual, + Not, +} from "typeorm" +import { + addOrderToSelect, + buildLegacyFieldsListFrom, + buildQuery, +} from "../build-query" describe("buildQuery", () => { it("successfully creates query", () => { @@ -13,8 +26,12 @@ describe("buildQuery", () => { date: { gt: date }, amount: { gt: 10 }, rule: { - type: "fixed" - } + type: "fixed", + }, + updated_at: { + gte: "value", + lte: "value", + }, }, { select: [ @@ -57,8 +74,8 @@ describe("buildQuery", () => { order: { id: "ASC", "items.id": "ASC", - "items.variant.id": "ASC" - } + "items.variant.id": "ASC", + }, } ) @@ -70,8 +87,9 @@ describe("buildQuery", () => { date: MoreThan(date), amount: MoreThan(10), rule: { - type: "fixed" - } + type: "fixed", + }, + updated_at: And(MoreThanOrEqual("value"), LessThanOrEqual("value")), }, select: { order: { @@ -120,21 +138,21 @@ describe("buildQuery", () => { }, items: { variants: { - product: true + product: true, }, tax_lines: true, - adjustments: true - } + adjustments: true, + }, }, order: { id: "ASC", items: { id: "ASC", variant: { - id: "ASC" - } - } - } + id: "ASC", + }, + }, + }, }) }) }) @@ -166,22 +184,24 @@ describe("buildLegacyFieldsListFrom", () => { }) expect(q.length).toBe(14) - expect(q).toEqual(expect.arrayContaining([ - "order", - "order.items", - "order.swaps", - "order.swaps.additional_items", - "order.discounts", - "order.discounts.rule", - "order.claims", - "order.claims.additional_items", - "additional_items", - "additional_items.variant", - "return_order", - "return_order.items", - "return_order.shipping_method", - "return_order.shipping_method.tax_lines", - ])) + expect(q).toEqual( + expect.arrayContaining([ + "order", + "order.items", + "order.swaps", + "order.swaps.additional_items", + "order.discounts", + "order.discounts.rule", + "order.claims", + "order.claims.additional_items", + "additional_items", + "additional_items.variant", + "return_order", + "return_order.items", + "return_order.shipping_method", + "return_order.shipping_method.tax_lines", + ]) + ) }) it("successfully build back relation object shape to list", () => { @@ -209,35 +229,37 @@ describe("buildLegacyFieldsListFrom", () => { }, items: { variants: { - product: true + product: true, }, tax_lines: true, - adjustments: true - } + adjustments: true, + }, }) expect(q.length).toBe(19) - expect(q).toEqual(expect.arrayContaining([ - "order", - "order.items", - "order.swaps", - "order.swaps.additional_items", - "order.discounts", - "order.discounts.rule", - "order.claims", - "order.claims.additional_items", - "additional_items", - "additional_items.variant", - "return_order", - "return_order.items", - "return_order.shipping_method", - "return_order.shipping_method.tax_lines", - "items.variants", - "items.variants.product", - "items", - "items.tax_lines", - "items.adjustments", - ])) + expect(q).toEqual( + expect.arrayContaining([ + "order", + "order.items", + "order.swaps", + "order.swaps.additional_items", + "order.discounts", + "order.discounts.rule", + "order.claims", + "order.claims.additional_items", + "additional_items", + "additional_items.variant", + "return_order", + "return_order.items", + "return_order.shipping_method", + "return_order.shipping_method.tax_lines", + "items.variants", + "items.variants.product", + "items", + "items.tax_lines", + "items.adjustments", + ]) + ) }) it("successfully build back order object shape to list", () => { @@ -246,37 +268,39 @@ describe("buildLegacyFieldsListFrom", () => { items: { id: "ASC", variant: { - id: "ASC" - } - } + id: "ASC", + }, + }, }) expect(q.length).toBe(5) - expect(q).toEqual(expect.arrayContaining([ - "id", - "items", - "items.id", - "items.variant", - "items.variant.id" - ])) + expect(q).toEqual( + expect.arrayContaining([ + "id", + "items", + "items.id", + "items.variant", + "items.variant.id", + ]) + ) }) - describe('addOrderToSelect', function () { + describe("addOrderToSelect", function () { it("successfully add the order fields to the select object", () => { const select: FindOptionsSelect = { item: { variant: { - id: true - } - } + id: true, + }, + }, } const order: FindOptionsOrder = { item: { variant: { - rank: "ASC" - } - } + rank: "ASC", + }, + }, } addOrderToSelect(order, select) @@ -285,10 +309,10 @@ describe("buildLegacyFieldsListFrom", () => { item: { variant: { id: true, - rank: true - } - } + rank: true, + }, + }, }) }) - }); + }) }) diff --git a/packages/medusa/src/utils/build-query.ts b/packages/medusa/src/utils/build-query.ts index 523359b44d..2de8e5295a 100644 --- a/packages/medusa/src/utils/build-query.ts +++ b/packages/medusa/src/utils/build-query.ts @@ -1,4 +1,5 @@ import { + And, FindManyOptions, FindOperator, FindOptionsRelations, @@ -105,18 +106,19 @@ function buildWhere( if (typeof value === "object") { Object.entries(value).forEach(([objectKey, objectValue]) => { + where[key] = where[key] || [] switch (objectKey) { case "lt": - where[key] = LessThan(objectValue) + where[key].push(LessThan(objectValue)) break case "gt": - where[key] = MoreThan(objectValue) + where[key].push(MoreThan(objectValue)) break case "lte": - where[key] = LessThanOrEqual(objectValue) + where[key].push(LessThanOrEqual(objectValue)) break case "gte": - where[key] = MoreThanOrEqual(objectValue) + where[key].push(MoreThanOrEqual(objectValue)) break default: if (objectValue != undefined && typeof objectValue === "object") { @@ -128,6 +130,16 @@ function buildWhere( return }) + if (!Array.isArray(where[key])) { + continue + } + + if (where[key].length === 1) { + where[key] = where[key][0] + } else { + where[key] = And(...where[key]) + } + continue } diff --git a/packages/utils/src/common/__tests__/build-query.spec.ts b/packages/utils/src/common/__tests__/build-query.spec.ts new file mode 100644 index 0000000000..2e9384e1ff --- /dev/null +++ b/packages/utils/src/common/__tests__/build-query.spec.ts @@ -0,0 +1,318 @@ +import { + And, + FindOptionsOrder, + FindOptionsSelect, + In, + LessThanOrEqual, + MoreThan, + MoreThanOrEqual, + Not, +} from "typeorm" +import { + addOrderToSelect, + buildQuery, + objectToStringPath, +} from "../build-query" + +describe("buildQuery", () => { + it("successfully creates query", () => { + const date = new Date() + + const q = buildQuery( + { + id: "1234", + test1: ["123", "12", "1"], + test2: Not("this"), + date: { gt: date }, + amount: { gt: 10 }, + rule: { + type: "fixed", + }, + updated_at: { + gte: "value", + lte: "value", + }, + }, + { + select: [ + "order", + "order.items", + "order.swaps", + "order.swaps.additional_items", + "order.discounts", + "order.discounts.rule", + "order.claims", + "order.claims.additional_items", + "additional_items", + "additional_items.variant", + "return_order", + "return_order.items", + "return_order.shipping_method", + "return_order.shipping_method.tax_lines", + ], + relations: [ + "order", + "order.items", + "order.swaps", + "order.swaps.additional_items", + "order.discounts", + "order.discounts.rule", + "order.claims", + "order.claims.additional_items", + "additional_items", + "additional_items.variant", + "return_order", + "return_order.items", + "return_order.shipping_method", + "return_order.shipping_method.tax_lines", + "items.variants", + "items.variants.product", + "items", + "items.tax_lines", + "items.adjustments", + ], + order: { + id: "ASC", + "items.id": "ASC", + "items.variant.id": "ASC", + }, + } + ) + + expect(q).toEqual({ + where: { + id: "1234", + test1: In(["123", "12", "1"]), + test2: Not("this"), + date: MoreThan(date), + amount: MoreThan(10), + rule: { + type: "fixed", + }, + updated_at: And(MoreThanOrEqual("value"), LessThanOrEqual("value")), + }, + select: { + order: { + items: true, + swaps: { + additional_items: true, + }, + discounts: { + rule: true, + }, + claims: { + additional_items: true, + }, + }, + additional_items: { + variant: true, + }, + return_order: { + items: true, + shipping_method: { + tax_lines: true, + }, + }, + }, + relations: { + order: { + items: true, + swaps: { + additional_items: true, + }, + discounts: { + rule: true, + }, + claims: { + additional_items: true, + }, + }, + additional_items: { + variant: true, + }, + return_order: { + items: true, + shipping_method: { + tax_lines: true, + }, + }, + items: { + variants: { + product: true, + }, + tax_lines: true, + adjustments: true, + }, + }, + order: { + id: "ASC", + items: { + id: "ASC", + variant: { + id: "ASC", + }, + }, + }, + }) + }) +}) + +describe("objectToStringPath", () => { + it("successfully build back select object shape to list", () => { + const q = objectToStringPath({ + order: { + items: true, + swaps: { + additional_items: true, + }, + discounts: { + rule: true, + }, + claims: { + additional_items: true, + }, + }, + additional_items: { + variant: true, + }, + return_order: { + items: true, + shipping_method: { + tax_lines: true, + }, + }, + }) + + expect(q.length).toBe(14) + expect(q).toEqual( + expect.arrayContaining([ + "order", + "order.items", + "order.swaps", + "order.swaps.additional_items", + "order.discounts", + "order.discounts.rule", + "order.claims", + "order.claims.additional_items", + "additional_items", + "additional_items.variant", + "return_order", + "return_order.items", + "return_order.shipping_method", + "return_order.shipping_method.tax_lines", + ]) + ) + }) + + it("successfully build back relation object shape to list", () => { + const q = objectToStringPath({ + order: { + items: true, + swaps: { + additional_items: true, + }, + discounts: { + rule: true, + }, + claims: { + additional_items: true, + }, + }, + additional_items: { + variant: true, + }, + return_order: { + items: true, + shipping_method: { + tax_lines: true, + }, + }, + items: { + variants: { + product: true, + }, + tax_lines: true, + adjustments: true, + }, + }) + + expect(q.length).toBe(19) + expect(q).toEqual( + expect.arrayContaining([ + "order", + "order.items", + "order.swaps", + "order.swaps.additional_items", + "order.discounts", + "order.discounts.rule", + "order.claims", + "order.claims.additional_items", + "additional_items", + "additional_items.variant", + "return_order", + "return_order.items", + "return_order.shipping_method", + "return_order.shipping_method.tax_lines", + "items.variants", + "items.variants.product", + "items", + "items.tax_lines", + "items.adjustments", + ]) + ) + }) + + it("successfully build back order object shape to list", () => { + const q = objectToStringPath({ + id: "ASC", + items: { + id: "ASC", + variant: { + id: "ASC", + }, + }, + }) + + expect(q.length).toBe(5) + expect(q).toEqual( + expect.arrayContaining([ + "id", + "items", + "items.id", + "items.variant", + "items.variant.id", + ]) + ) + }) + + describe("addOrderToSelect", function () { + it("successfully add the order fields to the select object", () => { + const select: FindOptionsSelect = { + item: { + variant: { + id: true, + }, + }, + } + + const order: FindOptionsOrder = { + item: { + variant: { + rank: "ASC", + }, + }, + } + + addOrderToSelect(order, select) + + expect(select).toEqual({ + item: { + variant: { + id: true, + rank: true, + }, + }, + }) + }) + }) +}) diff --git a/packages/utils/src/common/build-query.ts b/packages/utils/src/common/build-query.ts index 91fd2acef0..71495e785b 100644 --- a/packages/utils/src/common/build-query.ts +++ b/packages/utils/src/common/build-query.ts @@ -1,5 +1,6 @@ import { ExtendedFindConfig, FindConfig } from "@medusajs/types" import { + And, FindManyOptions, FindOperator, FindOptionsRelations, @@ -105,29 +106,36 @@ function buildWhere( if (typeof value === "object") { Object.entries(value).forEach(([objectKey, objectValue]) => { + where[key] = where[key] || [] switch (objectKey) { case "lt": - where[key] = LessThan(objectValue) + where[key].push(LessThan(objectValue)) break case "gt": - where[key] = MoreThan(objectValue) + where[key].push(MoreThan(objectValue)) break case "lte": - where[key] = LessThanOrEqual(objectValue) + where[key].push(LessThanOrEqual(objectValue)) break case "gte": - where[key] = MoreThanOrEqual(objectValue) + where[key].push(MoreThanOrEqual(objectValue)) break default: if (objectValue != undefined && typeof objectValue === "object") { - where[key] = buildWhere(objectValue) + where[key].push(buildWhere(objectValue)) return } - where[key] = value + where[key].push(value) } return }) + if (where[key].length === 1) { + where[key] = where[key][0] + } else { + where[key] = And(...where[key]) + } + continue }