feat(medusa): added endpoints for rule attribute/operator/values options (#6911)

what:

adds endpoints that returns attribute options, operator options and value options for a particular rule.
This commit is contained in:
Riqwan Thamir
2024-04-04 11:56:17 +02:00
committed by GitHub
parent 9ca38eba04
commit 483bf98a49
15 changed files with 642 additions and 16 deletions
@@ -4,6 +4,7 @@ import { transformBody, transformQuery } from "../../../api/middlewares"
import {
AdminGetPromotionsParams,
AdminGetPromotionsPromotionParams,
AdminGetPromotionsRuleValueParams,
AdminPostPromotionsPromotionReq,
AdminPostPromotionsPromotionRulesBatchAddReq,
AdminPostPromotionsPromotionRulesBatchRemoveReq,
@@ -92,4 +93,15 @@ export const adminPromotionRoutesMiddlewares: MiddlewareRoute[] = [
transformBody(AdminPostPromotionsPromotionRulesBatchRemoveReq),
],
},
{
method: ["GET"],
matcher:
"/admin/promotions/rule-value-options/:rule_type/:rule_attribute_id",
middlewares: [
transformQuery(
AdminGetPromotionsRuleValueParams,
QueryConfig.listRuleValueTransformQueryConfig
),
],
},
]
@@ -51,3 +51,9 @@ export const listTransformQueryConfig = {
...retrieveTransformQueryConfig,
isList: true,
}
export const listRuleValueTransformQueryConfig = {
defaults: [],
allowed: [],
isList: true,
}
@@ -0,0 +1,20 @@
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../types/routing"
import { ruleAttributesMap, validateRuleType } from "../../utils"
export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const { rule_type: ruleType } = req.params
validateRuleType(ruleType)
const attributes = ruleAttributesMap[ruleType] || []
res.json({
attributes,
})
}
@@ -0,0 +1,32 @@
import { RuleOperator } from "@medusajs/utils"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../types/routing"
const operators = [
{
id: RuleOperator.IN,
value: RuleOperator.IN,
label: "In",
},
{
id: RuleOperator.EQ,
value: RuleOperator.EQ,
label: "Equals",
},
{
id: RuleOperator.NE,
value: RuleOperator.NE,
label: "Not In",
},
]
export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
res.json({
operators,
})
}
@@ -0,0 +1,94 @@
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../../types/routing"
import { validateRuleAttribute, validateRuleType } from "../../../utils"
const queryConfigurations = {
region: {
entryPoint: "region",
labelAttr: "name",
valueAttr: "id",
},
currency: {
entryPoint: "currency",
labelAttr: "name",
valueAttr: "code",
},
customer_group: {
entryPoint: "customer_group",
labelAttr: "name",
valueAttr: "id",
},
sales_channel: {
entryPoint: "sales_channel",
labelAttr: "name",
valueAttr: "id",
},
country: {
entryPoint: "country",
labelAttr: "display_name",
valueAttr: "iso_2",
},
product: {
entryPoint: "product",
labelAttr: "title",
valueAttr: "id",
},
product_category: {
entryPoint: "product_category",
labelAttr: "name",
valueAttr: "id",
},
product_collection: {
entryPoint: "product_collection",
labelAttr: "title",
valueAttr: "id",
},
product_type: {
entryPoint: "product_type",
labelAttr: "value",
valueAttr: "id",
},
product_tag: {
entryPoint: "product_tag",
labelAttr: "value",
valueAttr: "id",
},
}
export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const { rule_type: ruleType, rule_attribute_id: ruleAttributeId } = req.params
const queryConfig = queryConfigurations[ruleAttributeId]
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
validateRuleType(ruleType)
validateRuleAttribute(ruleType, ruleAttributeId)
const { rows } = await remoteQuery(
remoteQueryObjectFromString({
entryPoint: queryConfig.entryPoint,
variables: {
filters: req.filterableFields,
...req.remoteQueryConfig.pagination,
},
fields: [queryConfig.labelAttr, queryConfig.valueAttr],
})
)
const values = rows.map((r) => ({
label: r[queryConfig.labelAttr],
value: r[queryConfig.valueAttr],
}))
res.json({
values,
})
}
@@ -0,0 +1,3 @@
export * from "./rule-attributes-map"
export * from "./validate-rule-attribute"
export * from "./validate-rule-type"
@@ -0,0 +1,91 @@
const ruleAttributes = [
{
id: "currency",
value: "currency_code",
label: "Currency code",
required: true,
},
{
id: "customer_group",
value: "customer_group.id",
label: "Customer Group",
required: false,
},
{
id: "region",
value: "region.id",
label: "Region",
required: false,
},
{
id: "country",
value: "shipping_address.country_code",
label: "Country",
required: false,
},
{
id: "sales_channel",
value: "sales_channel.id",
label: "Sales Channel",
required: false,
},
]
const commonAttributes = [
{
id: "product",
value: "items.product.id",
label: "Product",
required: false,
},
{
id: "product_category",
value: "items.product.categories.id",
label: "Product Category",
required: false,
},
{
id: "product_collection",
value: "items.product.collection_id",
label: "Product Collection",
required: false,
},
{
id: "product_type",
value: "items.product.type_id",
label: "Product Type",
required: false,
},
{
id: "product_tag",
value: "items.product.tags.id",
label: "Product Tag",
required: false,
},
]
const buyRuleAttributes = [
{
id: "buy_rules_min_quantity",
value: "buy_rules_min_quantity",
label: "Minimum quantity of items",
required: true,
},
...commonAttributes,
]
const targetRuleAttributes = [
{
id: "apply_to_quantity",
value: "apply_to_quantity",
label: "Quantity of items promotion will apply to",
required: true,
},
...commonAttributes,
]
export const ruleAttributesMap = {
rules: ruleAttributes,
"target-rules": targetRuleAttributes,
"buy-rules": buyRuleAttributes,
}
@@ -0,0 +1,17 @@
import { MedusaError } from "@medusajs/utils"
import { ruleAttributesMap } from "./rule-attributes-map"
export function validateRuleAttribute(
ruleType: string,
ruleAttributeId: string
) {
const ruleAttributes = ruleAttributesMap[ruleType] || []
const ruleAttribute = ruleAttributes.find((obj) => obj.id === ruleAttributeId)
if (!ruleAttribute) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Invalid rule attribute - ${ruleAttributeId}`
)
}
}
@@ -0,0 +1,14 @@
import { MedusaError, RuleType } from "@medusajs/utils"
const validRuleTypes: string[] = Object.values(RuleType)
export function validateRuleType(ruleType: string) {
const underscorizedRuleType = ruleType.split("-").join("_")
if (!validRuleTypes.includes(underscorizedRuleType)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Invalid param rule_type (${ruleType})`
)
}
}
@@ -30,6 +30,18 @@ import { AdminPostCampaignsReq } from "../campaigns/validators"
export class AdminGetPromotionsPromotionParams extends FindParams {}
export class AdminGetPromotionsRuleValueParams extends extendedFindParamsMixin({
limit: 100,
offset: 0,
}) {
/**
* Search terms to search fields.
*/
@IsString()
@IsOptional()
q?: string
}
export class AdminGetPromotionsParams extends extendedFindParamsMixin({
limit: 100,
offset: 0,
@@ -71,7 +83,6 @@ export class AdminPostPromotionsReq {
@IsOptional()
is_automatic?: boolean
@IsOptional()
@IsEnum(PromotionType)
type?: PromotionTypeValues
@@ -86,8 +97,8 @@ export class AdminPostPromotionsReq {
@IsNotEmpty()
@ValidateNested()
@Type(() => ApplicationMethodsPostReq)
application_method: ApplicationMethodsPostReq
@Type(() => AdminPostApplicationMethodsReq)
application_method: AdminPostApplicationMethodsReq
@IsOptional()
@IsArray()
@@ -113,7 +124,7 @@ export class PromotionRule {
values: string[]
}
export class ApplicationMethodsPostReq {
export class AdminPostApplicationMethodsReq {
@IsOptional()
@IsString()
description?: string
@@ -130,7 +141,6 @@ export class ApplicationMethodsPostReq {
@IsEnum(ApplicationMethodType)
type?: ApplicationMethodType
@IsOptional()
@IsEnum(ApplicationMethodTargetType)
target_type?: ApplicationMethodTargetType
@@ -161,7 +171,7 @@ export class ApplicationMethodsPostReq {
buy_rules_min_quantity?: number
}
export class ApplicationMethodsMethodPostReq {
export class AdminPostApplicationMethodsMethodReq {
@IsOptional()
@IsString()
description?: string
@@ -233,8 +243,8 @@ export class AdminPostPromotionsPromotionReq {
@IsOptional()
@ValidateNested()
@Type(() => ApplicationMethodsMethodPostReq)
application_method?: ApplicationMethodsMethodPostReq
@Type(() => AdminPostApplicationMethodsMethodReq)
application_method?: AdminPostApplicationMethodsMethodReq
@IsOptional()
@IsArray()