From 6602e893b88f1c8f0c8165101dd609baaf60d925 Mon Sep 17 00:00:00 2001 From: William Bouchard <46496014+willbouch@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:03:27 -0400 Subject: [PATCH] chore(medusa): fetch shipping related attributes (#13244) * chore(medusa): fet shipping related attributes * some tests * changeset * small refactor * typing * adapt attribute value * test * test again --- .changeset/wise-peas-flash.md | 5 + .../promotions/admin/promotions.spec.ts | 110 +++++++++++++++++- .../types/src/http/promotion/admin/queries.ts | 13 ++- .../promotions/[id]/[rule_type]/route.ts | 3 + .../[rule_type]/route.ts | 12 +- .../[rule_type]/[rule_attribute_id]/route.ts | 25 ++-- .../promotions/utils/rule-attributes-map.ts | 37 +++++- .../utils/rule-query-configuration.ts | 5 + .../utils/validate-rule-attribute.ts | 23 +++- .../src/api/admin/promotions/validators.ts | 3 + 10 files changed, 205 insertions(+), 31 deletions(-) create mode 100644 .changeset/wise-peas-flash.md diff --git a/.changeset/wise-peas-flash.md b/.changeset/wise-peas-flash.md new file mode 100644 index 0000000000..942e12a609 --- /dev/null +++ b/.changeset/wise-peas-flash.md @@ -0,0 +1,5 @@ +--- +"@medusajs/medusa": patch +--- + +chore(medusa): fetch shipping related attributes diff --git a/integration-tests/http/__tests__/promotions/admin/promotions.spec.ts b/integration-tests/http/__tests__/promotions/admin/promotions.spec.ts index 1160471025..49a2c2ba92 100644 --- a/integration-tests/http/__tests__/promotions/admin/promotions.spec.ts +++ b/integration-tests/http/__tests__/promotions/admin/promotions.spec.ts @@ -1,10 +1,6 @@ import { medusaIntegrationTestRunner } from "@medusajs/test-utils" import { Modules, PromotionStatus, PromotionType } from "@medusajs/utils" -import { - createAdminUser, - generatePublishableKey, - generateStoreHeaders, -} from "../../../../helpers/create-admin-user" +import { createAdminUser, generatePublishableKey, generateStoreHeaders, } from "../../../../helpers/create-admin-user" import { setupTaxStructure } from "../../../../modules/__tests__/fixtures/tax" import { medusaTshirtProduct } from "../../../__fixtures__/product" @@ -2408,6 +2404,80 @@ medusaIntegrationTestRunner({ ]) ) }) + + it("return all product target rule attributes by default", async () => { + const response = await api.get( + `/admin/promotions/rule-attribute-options/target-rules`, + adminHeaders + ) + + expect(response.status).toEqual(200) + expect(response.data.attributes).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: "product", + value: "items.product.id", + label: "Product", + required: false, + field_type: "multiselect", + operators: expect.anything(), + }), + expect.objectContaining({ + id: "product_category", + value: "items.product.categories.id", + label: "Product Category", + required: false, + field_type: "multiselect", + operators: expect.anything(), + }), + expect.objectContaining({ + id: "product_collection", + value: "items.product.collection_id", + label: "Product Collection", + required: false, + field_type: "multiselect", + operators: expect.anything(), + }), + expect.objectContaining({ + id: "product_type", + value: "items.product.type_id", + label: "Product Type", + required: false, + field_type: "multiselect", + operators: expect.anything(), + }), + expect.objectContaining({ + id: "product_tag", + value: "items.product.tags.id", + label: "Product Tag", + required: false, + field_type: "multiselect", + operators: expect.anything(), + }), + ]) + ) + }) + + it("return all target rule attributes when application method target type is shipping_methods", async () => { + const response = await api.get( + `/admin/promotions/rule-attribute-options/target-rules?application_method_target_type=shipping_methods`, + adminHeaders + ) + + expect(response.status).toEqual(200) + expect(response.data.attributes).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: "shipping_option_type", + value: "shipping_methods.shipping_option.shipping_option_type_id", + label: "Shipping Option Type", + required: false, + field_type: "multiselect", + operators: expect.anything(), + }), + ]) + ) + }) }) describe("GET /admin/promotions/rule-value-options/:ruleType/:ruleAttributeId", () => { @@ -2677,6 +2747,36 @@ medusaIntegrationTestRunner({ { label: "test tag 2", value: tag2.id }, ]) ) + + const soType1 = ( + await api.post( + "/admin/shipping-option-types", + { label: "Test 1", code: "test_1" }, + adminHeaders + ) + ).data.shipping_option_type + + const soType2 = ( + await api.post( + "/admin/shipping-option-types", + { label: "Test 2", code: "test_2" }, + adminHeaders + ) + ).data.shipping_option_type + + response = await api.get( + `/admin/promotions/rule-value-options/target-rules/shipping_option_type?application_method_target_type=shipping_methods`, + adminHeaders + ) + + expect(response.status).toEqual(200) + expect(response.data.values.length).toEqual(2) + expect(response.data.values).toEqual( + expect.arrayContaining([ + { label: "Test 1", value: soType1.id }, + { label: "Test 2", value: soType2.id }, + ]) + ) }) }) }) diff --git a/packages/core/types/src/http/promotion/admin/queries.ts b/packages/core/types/src/http/promotion/admin/queries.ts index 0805c9a169..86344b049e 100644 --- a/packages/core/types/src/http/promotion/admin/queries.ts +++ b/packages/core/types/src/http/promotion/admin/queries.ts @@ -1,5 +1,8 @@ import { BaseFilterable, OperatorMap } from "../../../dal" -import { ApplicationMethodTypeValues } from "../../../promotion" +import { + ApplicationMethodTypeValues, + PromotionTypeValues, +} from "../../../promotion" import { FindParams, SelectParams } from "../../common" export interface AdminGetPromotionParams extends SelectParams {} @@ -59,13 +62,13 @@ export interface AdminGetPromotionsParams } export interface AdminGetPromotionRuleParams { - promotion_type?: string - application_method_type?: string + promotion_type?: PromotionTypeValues + application_method_type?: ApplicationMethodTypeValues } export interface AdminGetPromotionRuleTypeParams extends SelectParams { - promotion_type?: string - application_method_type?: string + promotion_type?: PromotionTypeValues + application_method_type?: ApplicationMethodTypeValues } export interface AdminGetPromotionsRuleValueParams extends FindParams { diff --git a/packages/medusa/src/api/admin/promotions/[id]/[rule_type]/route.ts b/packages/medusa/src/api/admin/promotions/[id]/[rule_type]/route.ts index eef75f2cd8..7fb762f3a5 100644 --- a/packages/medusa/src/api/admin/promotions/[id]/[rule_type]/route.ts +++ b/packages/medusa/src/api/admin/promotions/[id]/[rule_type]/route.ts @@ -37,6 +37,9 @@ export const GET = async ( promotionType: promotion?.type || req.query.promotion_type, applicationMethodType: promotion?.application_method?.type || req.query.application_method_type, + applicationMethodTargetType: + promotion?.application_method?.target_type || + req.query.application_method_target_type, })[ruleType] const promotionRules: any[] = [] diff --git a/packages/medusa/src/api/admin/promotions/rule-attribute-options/[rule_type]/route.ts b/packages/medusa/src/api/admin/promotions/rule-attribute-options/[rule_type]/route.ts index 056203ac91..fdd8affbc2 100644 --- a/packages/medusa/src/api/admin/promotions/rule-attribute-options/[rule_type]/route.ts +++ b/packages/medusa/src/api/admin/promotions/rule-attribute-options/[rule_type]/route.ts @@ -4,6 +4,11 @@ import { MedusaResponse, } from "@medusajs/framework/http" import { getRuleAttributesMap, validateRuleType } from "../../utils" +import { + ApplicationMethodTargetTypeValues, + ApplicationMethodTypeValues, + PromotionTypeValues, +} from "@medusajs/types" export const GET = async ( req: AuthenticatedMedusaRequest, @@ -15,8 +20,11 @@ export const GET = async ( const attributes = getRuleAttributesMap({ - promotionType: req.query.promotion_type as string, - applicationMethodType: req.query.application_method_type as string, + promotionType: req.query.promotion_type as PromotionTypeValues, + applicationMethodType: req.query + .application_method_type as ApplicationMethodTypeValues, + applicationMethodTargetType: req.query + .application_method_target_type as ApplicationMethodTargetTypeValues, })[ruleType] || [] res.json({ diff --git a/packages/medusa/src/api/admin/promotions/rule-value-options/[rule_type]/[rule_attribute_id]/route.ts b/packages/medusa/src/api/admin/promotions/rule-value-options/[rule_type]/[rule_attribute_id]/route.ts index 7b34ae6062..62f2cdcadc 100644 --- a/packages/medusa/src/api/admin/promotions/rule-value-options/[rule_type]/[rule_attribute_id]/route.ts +++ b/packages/medusa/src/api/admin/promotions/rule-value-options/[rule_type]/[rule_attribute_id]/route.ts @@ -13,6 +13,10 @@ import { validateRuleType, } from "../../../utils" import { AdminGetPromotionRuleParamsType } from "../../../validators" +import { + ApplicationMethodTargetTypeValues, + RuleTypeValues, +} from "@medusajs/types" /* This endpoint returns all the potential values for rules (promotion rules, target rules and buy rules) @@ -25,12 +29,7 @@ export const GET = async ( req: AuthenticatedMedusaRequest, res: MedusaResponse ) => { - const { - rule_type: ruleType, - rule_attribute_id: ruleAttributeId, - promotion_type: promotionType, - application_method_type: applicationMethodType, - } = req.params + const { rule_type: ruleType, rule_attribute_id: ruleAttributeId } = req.params const queryConfig = ruleQueryConfigurations[ruleAttributeId] const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) const filterableFields = req.filterableFields @@ -43,12 +42,20 @@ export const GET = async ( validateRuleType(ruleType) validateRuleAttribute({ - promotionType, - ruleType, + ruleType: ruleType as RuleTypeValues, ruleAttributeId, - applicationMethodType, + promotionType: undefined, + applicationMethodType: undefined, + applicationMethodTargetType: + filterableFields.application_method_target_type as + | ApplicationMethodTargetTypeValues + | undefined, }) + if (filterableFields.application_method_target_type) { + delete filterableFields.application_method_target_type + } + const { rows, metadata } = await remoteQuery( remoteQueryObjectFromString({ entryPoint: queryConfig.entryPoint, diff --git a/packages/medusa/src/api/admin/promotions/utils/rule-attributes-map.ts b/packages/medusa/src/api/admin/promotions/utils/rule-attributes-map.ts index 06e31bb436..f533821f63 100644 --- a/packages/medusa/src/api/admin/promotions/utils/rule-attributes-map.ts +++ b/packages/medusa/src/api/admin/promotions/utils/rule-attributes-map.ts @@ -1,9 +1,15 @@ import { + ApplicationMethodTargetType, ApplicationMethodType, PromotionType, RuleOperator, } from "@medusajs/framework/utils" import { operatorsMap } from "./operators-map" +import { + ApplicationMethodTargetTypeValues, + ApplicationMethodTypeValues, + PromotionTypeValues, +} from "@medusajs/types" export enum DisguisedRule { APPLY_TO_QUANTITY = "apply_to_quantity", @@ -46,7 +52,7 @@ const ruleAttributes = [ }, ] -const commonAttributes = [ +const itemsAttributes = [ { id: "product", value: "items.product.id", @@ -89,6 +95,17 @@ const commonAttributes = [ }, ] +const shippingMethodsAttributes = [ + { + id: "shipping_option_type", + value: "shipping_methods.shipping_option.shipping_option_type_id", + label: "Shipping Option Type", + required: false, + field_type: "multiselect", + operators: Object.values(operatorsMap), + }, +] + const currencyRule = { id: DisguisedRule.CURRENCY_CODE, value: DisguisedRule.CURRENCY_CODE, @@ -127,14 +144,24 @@ const buyGetTargetRules = [ export const getRuleAttributesMap = ({ promotionType, applicationMethodType, + applicationMethodTargetType, }: { - promotionType?: string - applicationMethodType?: string + promotionType?: PromotionTypeValues + applicationMethodType?: ApplicationMethodTypeValues + applicationMethodTargetType?: ApplicationMethodTargetTypeValues }) => { const map = { rules: [...ruleAttributes], - "target-rules": [...commonAttributes], - "buy-rules": [...commonAttributes], + "target-rules": + applicationMethodTargetType === + ApplicationMethodTargetType.SHIPPING_METHODS + ? [...shippingMethodsAttributes] + : [...itemsAttributes], + "buy-rules": + applicationMethodTargetType === + ApplicationMethodTargetType.SHIPPING_METHODS + ? [...shippingMethodsAttributes] + : [...itemsAttributes], } if (applicationMethodType === ApplicationMethodType.FIXED) { diff --git a/packages/medusa/src/api/admin/promotions/utils/rule-query-configuration.ts b/packages/medusa/src/api/admin/promotions/utils/rule-query-configuration.ts index e2ffb81824..a198055aaf 100644 --- a/packages/medusa/src/api/admin/promotions/utils/rule-query-configuration.ts +++ b/packages/medusa/src/api/admin/promotions/utils/rule-query-configuration.ts @@ -49,4 +49,9 @@ export const ruleQueryConfigurations = { labelAttr: "value", valueAttr: "id", }, + shipping_option_type: { + entryPoint: "shipping_option_type", + labelAttr: "label", + valueAttr: "id", + }, } diff --git a/packages/medusa/src/api/admin/promotions/utils/validate-rule-attribute.ts b/packages/medusa/src/api/admin/promotions/utils/validate-rule-attribute.ts index 1e1aca135f..5b9d852527 100644 --- a/packages/medusa/src/api/admin/promotions/utils/validate-rule-attribute.ts +++ b/packages/medusa/src/api/admin/promotions/utils/validate-rule-attribute.ts @@ -1,19 +1,32 @@ import { MedusaError } from "@medusajs/framework/utils" import { getRuleAttributesMap } from "./rule-attributes-map" +import { + ApplicationMethodTargetTypeValues, + ApplicationMethodTypeValues, + PromotionTypeValues, + RuleTypeValues, +} from "@medusajs/types" export function validateRuleAttribute(attributes: { - promotionType: string | undefined - ruleType: string + promotionType: PromotionTypeValues | undefined + ruleType: RuleTypeValues ruleAttributeId: string - applicationMethodType?: string + applicationMethodType?: ApplicationMethodTypeValues + applicationMethodTargetType?: ApplicationMethodTargetTypeValues }) { - const { promotionType, ruleType, ruleAttributeId, applicationMethodType } = - attributes + const { + promotionType, + ruleType, + ruleAttributeId, + applicationMethodType, + applicationMethodTargetType, + } = attributes const ruleAttributes = getRuleAttributesMap({ promotionType, applicationMethodType, + applicationMethodTargetType, })[ruleType] || [] const ruleAttribute = ruleAttributes.find((obj) => obj.id === ruleAttributeId) diff --git a/packages/medusa/src/api/admin/promotions/validators.ts b/packages/medusa/src/api/admin/promotions/validators.ts index 93bdd36295..3f7f488635 100644 --- a/packages/medusa/src/api/admin/promotions/validators.ts +++ b/packages/medusa/src/api/admin/promotions/validators.ts @@ -54,6 +54,7 @@ export type AdminGetPromotionRuleParamsType = z.infer< export const AdminGetPromotionRuleParams = z.object({ promotion_type: z.string().optional(), application_method_type: z.string().optional(), + application_method_target_type: z.string().optional(), }) export type AdminGetPromotionRuleTypeParamsType = z.infer< @@ -63,6 +64,7 @@ export const AdminGetPromotionRuleTypeParams = createSelectParams().merge( z.object({ promotion_type: z.string().optional(), application_method_type: z.string().optional(), + application_method_target_type: z.string().optional(), }) ) @@ -76,6 +78,7 @@ export const AdminGetPromotionsRuleValueParams = createFindParams({ z.object({ q: z.string().optional(), value: z.union([z.string(), z.array(z.string())]).optional(), + application_method_target_type: z.string().optional(), }) )