From a7cace90e072a66565bc530909fd6533ed7cdad7 Mon Sep 17 00:00:00 2001 From: Riqwan Thamir Date: Thu, 8 Aug 2024 16:40:02 +0200 Subject: [PATCH] feat(medusa): filter shipping options by location (#8511) what: - filters shipping options by location ID RESOLVES CC-328 --- .../admin/shipping-option.spec.ts | 57 +++++++++++++++--- .../api/admin/shipping-options/middlewares.ts | 23 +++++--- .../api/admin/shipping-options/validators.ts | 1 + .../src/api/utils/maybe-apply-link-filter.ts | 59 ++++++++++++++++--- 4 files changed, 118 insertions(+), 22 deletions(-) diff --git a/integration-tests/http/__tests__/shipping-option/admin/shipping-option.spec.ts b/integration-tests/http/__tests__/shipping-option/admin/shipping-option.spec.ts index 070b4a94f4..6857435e20 100644 --- a/integration-tests/http/__tests__/shipping-option/admin/shipping-option.spec.ts +++ b/integration-tests/http/__tests__/shipping-option/admin/shipping-option.spec.ts @@ -43,9 +43,7 @@ medusaIntegrationTestRunner({ location = ( await api.post( `/admin/stock-locations`, - { - name: "Test location", - }, + { name: "Test location" }, adminHeaders ) ).data.stock_location @@ -53,10 +51,15 @@ medusaIntegrationTestRunner({ location = ( await api.post( `/admin/stock-locations/${location.id}/fulfillment-sets?fields=*fulfillment_sets`, - { - name: "Test", - type: "test-type", - }, + { name: "Test", type: "test-type" }, + adminHeaders + ) + ).data.stock_location + + location2 = ( + await api.post( + `/admin/stock-locations`, + { name: "Test location 2" }, adminHeaders ) ).data.stock_location @@ -91,6 +94,46 @@ medusaIntegrationTestRunner({ ).data.region }) + describe("GET /admin/shipping-options", () => { + it("should filters options by stock_location_id", async () => { + const shippingOptionPayload = { + name: "Test shipping option", + service_zone_id: fulfillmentSet.service_zones[0].id, + shipping_profile_id: shippingProfile.id, + provider_id: "manual_test-provider", + price_type: "flat", + type: { + label: "Test type", + description: "Test description", + code: "test-code", + }, + prices: [{ currency_code: "usd", amount: 1000 }], + } + + const { + data: { shipping_option: shippingOption }, + } = await api.post( + `/admin/shipping-options`, + shippingOptionPayload, + adminHeaders + ) + + const shippingOptions = await api.get( + `/admin/shipping-options?stock_location_id=${location.id}`, + adminHeaders + ) + + expect(shippingOptions.data.shipping_options).toHaveLength(1) + + const shippingOptions2 = await api.get( + `/admin/shipping-options?stock_location_id=${location2.id}`, + adminHeaders + ) + + expect(shippingOptions2.data.shipping_options).toHaveLength(0) + }) + }) + describe("POST /admin/shipping-options", () => { it("should throw error when required params are missing", async () => { const shippingOptionPayload = { diff --git a/packages/medusa/src/api/admin/shipping-options/middlewares.ts b/packages/medusa/src/api/admin/shipping-options/middlewares.ts index 3c4f003c0b..dafab3888a 100644 --- a/packages/medusa/src/api/admin/shipping-options/middlewares.ts +++ b/packages/medusa/src/api/admin/shipping-options/middlewares.ts @@ -1,4 +1,13 @@ import { MiddlewareRoute } from "@medusajs/framework" +import { maybeApplyLinkFilter } from "../../utils/maybe-apply-link-filter" +import { validateAndTransformBody } from "../../utils/validate-body" +import { validateAndTransformQuery } from "../../utils/validate-query" +import { createBatchBody } from "../../utils/validators" +import { + listTransformQueryConfig, + retrieveRuleTransformQueryConfig, + retrieveTransformQueryConfig, +} from "./query-config" import { AdminCreateShippingOption, AdminCreateShippingOptionRule, @@ -8,14 +17,6 @@ import { AdminUpdateShippingOption, AdminUpdateShippingOptionRule, } from "./validators" -import { - listTransformQueryConfig, - retrieveRuleTransformQueryConfig, - retrieveTransformQueryConfig, -} from "./query-config" -import { validateAndTransformBody } from "../../utils/validate-body" -import { validateAndTransformQuery } from "../../utils/validate-query" -import { createBatchBody } from "../../utils/validators" export const adminShippingOptionRoutesMiddlewares: MiddlewareRoute[] = [ { @@ -26,6 +27,12 @@ export const adminShippingOptionRoutesMiddlewares: MiddlewareRoute[] = [ AdminGetShippingOptionsParams, listTransformQueryConfig ), + maybeApplyLinkFilter({ + entryPoint: "location_fulfillment_set", + resourceId: "fulfillment_set_id", + filterableField: "stock_location_id", + filterByField: "service_zone.fulfillment_set_id", + }), ], }, { diff --git a/packages/medusa/src/api/admin/shipping-options/validators.ts b/packages/medusa/src/api/admin/shipping-options/validators.ts index eea2bf2d37..fd2803ab65 100644 --- a/packages/medusa/src/api/admin/shipping-options/validators.ts +++ b/packages/medusa/src/api/admin/shipping-options/validators.ts @@ -27,6 +27,7 @@ export const AdminGetShippingOptionsParams = createFindParams({ service_zone_id: z.union([z.string(), z.array(z.string())]).optional(), shipping_profile_id: z.union([z.string(), z.array(z.string())]).optional(), provider_id: z.union([z.string(), z.array(z.string())]).optional(), + stock_location_id: z.union([z.string(), z.array(z.string())]).optional(), shipping_option_type_id: z .union([z.string(), z.array(z.string())]) .optional(), diff --git a/packages/medusa/src/api/utils/maybe-apply-link-filter.ts b/packages/medusa/src/api/utils/maybe-apply-link-filter.ts index 50d1b7ab17..9498789f83 100644 --- a/packages/medusa/src/api/utils/maybe-apply-link-filter.ts +++ b/packages/medusa/src/api/utils/maybe-apply-link-filter.ts @@ -10,6 +10,7 @@ export function maybeApplyLinkFilter({ entryPoint, resourceId, filterableField, + filterByField = "id", }) { return async (req: MedusaRequest, _, next: NextFunction) => { const filterableFields = req.filterableFields @@ -37,21 +38,65 @@ export function maybeApplyLinkFilter({ }) const resources = await remoteQuery(queryObject) + let existingFilters = filterableFields[filterByField] as + | string[] + | string + | undefined - let existingIdFilters = filterableFields.id as string[] | string | undefined - if (existingIdFilters) { - if (typeof existingIdFilters === "string") { - existingIdFilters = [existingIdFilters] + if (existingFilters) { + if (typeof existingFilters === "string") { + existingFilters = [existingFilters] } - filterableFields.id = arrayIntersection( - existingIdFilters, + filterableFields[filterByField] = arrayIntersection( + existingFilters, resources.map((p) => p[resourceId]) ) } else { - filterableFields.id = resources.map((p) => p[resourceId]) + filterableFields[filterByField] = resources.map((p) => p[resourceId]) } + req.filterableFields = transformFilterableFields(filterableFields) + return next() } } +/* + Transforms an object key string into nested objects + before = { + "test.something.another": [] + } + + after = { + test: { + something: { + another: [] + } + } + } +*/ +function transformFilterableFields(filterableFields: Record) { + const result = {}; + for (const key of Object.keys(filterableFields)) { + const value = filterableFields[key]; + const keys = key.split("."); + let current = result; + + // Iterate over the keys, creating nested objects as needed + for (let i = 0; i < keys.length; i++) { + const part = keys[i]; + current[part] ??= {}; + + if (i === keys.length - 1) { + // If its the last key, assign the value + current[part] = value; + break; + } + + current = current[part]; + } + } + + return result; +} +