fix(medusa, utils): fix the way selects are consumed alongside the relations (#4389)

**What**
There is actually an issue with using the `fields` query params with the way the repositories are using our custom query strategy. This pr aims to fix this issue by reworking the strategy.

What we had to do was to rework the way the selects are built for each subquery in order to follow the aliasing convention and to be taken into consideration. Alongside these changes, the join used to always select everything, this needed to be changed so that if there are any selects provided for a join, the join should not select everything and let the query select the fields that are requested.

Another notable change is that all the repositories are now using the repository util in order to centralize the customization and to have a single place to update when this kind of issue arises. This means that the eager relations when using the query builder are not necessarily taken into account. For that reason, I have removed the `shipping_option` eager option in favor of explicitly asking for the relations like we started to do it in some places.

FIXES CORE-1413
This commit is contained in:
Adrien de Peretti
2023-06-29 15:26:41 +02:00
committed by GitHub
parent 9760d4a96c
commit 9dcdc0041a
38 changed files with 443 additions and 207 deletions

View File

@@ -0,0 +1,91 @@
import { buildSelects } from "../build-query"
describe("buildSelects", () => {
it("successfully build back select object shape to list", () => {
const q = buildSelects([
"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({
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,
},
},
})
})
})
describe("buildSelects", () => {
it("successfully build back select object shape to list", () => {
const q = buildSelects([
"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({
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,
},
},
})
})
})

View File

@@ -0,0 +1,42 @@
import { objectToStringPath } from "../object-to-string-path"
describe("objectToStringPath", function () {
it("should return only the properties path of the properties that are set to true", function () {
const res = objectToStringPath(
{
product: true,
variants: {
title: true,
prices: {
amount: true,
},
},
},
{
includeParentPropertyFields: false,
}
)
expect(res).toEqual(["product", "variants.title", "variants.prices.amount"])
})
it("should return a string path from an object including properties that are object and contains other properties set to true", function () {
const res = objectToStringPath({
product: true,
variants: {
title: true,
prices: {
amount: true,
},
},
})
expect(res).toEqual([
"product",
"variants",
"variants.title",
"variants.prices",
"variants.prices.amount",
])
})
})

View File

@@ -4,7 +4,8 @@ import { isObject } from "./is-object"
* Converts a structure of find options to an
* array of string paths
* @example
* input: {
* // With `includeTruePropertiesOnly` default value set to false
* const result = objectToStringPath({
* test: {
* test1: true,
* test2: true,
@@ -13,20 +14,50 @@ import { isObject } from "./is-object"
* },
* },
* test2: true
* }
* output: ['test.test1', 'test.test2', 'test.test3.test4', 'test2']
* @param input
* })
* console.log(result)
* // output: ['test', 'test.test1', 'test.test2', 'test.test3', 'test.test3.test4', 'test2']
*
* @example
* // With `includeTruePropertiesOnly` set to true
* const result = objectToStringPath({
* test: {
* test1: true,
* test2: true,
* test3: {
* test4: true
* },
* },
* test2: true
* }, {
* includeTruePropertiesOnly: true
* })
* console.log(result)
* // output: ['test.test1', 'test.test2', 'test.test3.test4', 'test2']
*
* @param {InputObject} input
* @param {boolean} includeParentPropertyFields If set to true (example 1), all properties will be included as well as the parents,
* otherwise (example 2) all properties path set to true will included, excluded the parents
*/
export function objectToStringPath(input: object = {}): string[] {
export function objectToStringPath(
input: object = {},
{ includeParentPropertyFields }: { includeParentPropertyFields: boolean } = {
includeParentPropertyFields: true,
}
): string[] {
if (!isObject(input) || !Object.keys(input).length) {
return []
}
const output: Set<string> = new Set(Object.keys(input))
const output: Set<string> = includeParentPropertyFields
? new Set(Object.keys(input))
: new Set()
for (const key of Object.keys(input)) {
if (input[key] != undefined && typeof input[key] === "object") {
const deepRes = objectToStringPath(input[key])
if (isObject(input[key])) {
const deepRes = objectToStringPath(input[key], {
includeParentPropertyFields,
})
const items = deepRes.reduce((acc, val) => {
acc.push(`${key}.${val}`)
@@ -37,7 +68,9 @@ export function objectToStringPath(input: object = {}): string[] {
continue
}
output.add(key)
if (isObject(key) || input[key] === true) {
output.add(key)
}
}
return Array.from(output)