feat(core-flows,medusa): added api + workflows for rule types CRUD (#6684)

This commit is contained in:
Riqwan Thamir
2024-03-13 16:43:30 +01:00
committed by GitHub
parent 5f6d128613
commit b78f863d80
16 changed files with 412 additions and 4 deletions

View File

@@ -0,0 +1,6 @@
---
"@medusajs/core-flows": patch
"@medusajs/medusa": patch
---
feat(core-flows,medusa): added api + workflows for rule types CRUD

View File

@@ -107,6 +107,122 @@ medusaIntegrationTestRunner({
})
})
})
describe("POST /admin/pricing/rule-types", () => {
it("should throw an error if required params are not passed", async () => {
const { response } = await api
.post(
`/admin/pricing/rule-types`,
{
rule_attribute: "rule_attr_test1",
default_priority: 7,
},
adminHeaders
)
.catch((e) => e)
expect(response.status).toEqual(400)
expect(response.data.message).toEqual(
"name must be a string, name should not be empty"
)
})
it("should create a rule type successfully", async () => {
const response = await api.post(
`/admin/pricing/rule-types`,
{
name: "test",
rule_attribute: "rule_attr_test",
default_priority: 6,
},
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data.rule_type).toEqual(
expect.objectContaining({
id: expect.any(String),
name: "test",
rule_attribute: "rule_attr_test",
default_priority: 6,
})
)
})
})
describe("POST /admin/pricing/rule-types/:id", () => {
it("should throw an error if id does not exist", async () => {
const { response } = await api
.post(`/admin/pricing/rule-types/does-not-exist`, {}, adminHeaders)
.catch((e) => e)
expect(response.status).toEqual(404)
expect(response.data.message).toEqual(
`RuleType with id "does-not-exist" not found`
)
})
it("should update a rule type successfully", async () => {
const [ruleType] = ruleTypes
const response = await api.post(
`/admin/pricing/rule-types/${ruleType.id}`,
{
name: "test update",
rule_attribute: "test_update",
default_priority: 7,
},
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data.rule_type).toEqual(
expect.objectContaining({
id: expect.any(String),
name: "test update",
rule_attribute: "test_update",
default_priority: 7,
})
)
})
})
describe("DELETE /admin/pricing/rule-types/:id", () => {
it("should delete rule type successfully", async () => {
const [ruleType] = ruleTypes
const response = await api.delete(
`/admin/pricing/rule-types/${ruleType.id}`,
adminHeaders
)
console.log("response.data -- ", response.data)
expect(response.status).toEqual(200)
expect(response.data).toEqual({
id: ruleType.id,
object: "rule_type",
deleted: true,
})
const deletedRuleTypes = await pricingModule.listRuleTypes({
id: [ruleType.id],
})
expect(deletedRuleTypes.length).toEqual(0)
})
it("should return 200 when id does not exist", async () => {
const response = await api.delete(
`/admin/pricing/rule-types/does-not-exist`,
adminHeaders
)
expect(response.status).toEqual(200)
expect(response.data).toEqual({
id: "does-not-exist",
object: "rule_type",
deleted: true,
})
})
})
})
},
})

View File

@@ -6,6 +6,7 @@ export * from "./definitions"
export * as Handlers from "./handlers"
export * from "./invite"
export * from "./payment"
export * from "./pricing"
export * from "./product"
export * from "./promotion"
export * from "./region"

View File

@@ -0,0 +1,2 @@
export * from "./steps"
export * from "./workflows"

View File

@@ -0,0 +1,31 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { CreateRuleTypeDTO, IPricingModuleService } from "@medusajs/types"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
export const createPricingRuleTypesStepId = "create-pricing-rule-types"
export const createPricingRuleTypesStep = createStep(
createPricingRuleTypesStepId,
async (data: CreateRuleTypeDTO[], { container }) => {
const pricingModule = container.resolve<IPricingModuleService>(
ModuleRegistrationName.PRICING
)
const ruleTypes = await pricingModule.createRuleTypes(data)
return new StepResponse(
ruleTypes,
ruleTypes.map((ruleType) => ruleType.id)
)
},
async (ruleTypeIds, { container }) => {
if (!ruleTypeIds?.length) {
return
}
const pricingModule = container.resolve<IPricingModuleService>(
ModuleRegistrationName.PRICING
)
await pricingModule.delete(ruleTypeIds)
}
)

View File

@@ -0,0 +1,31 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { IPricingModuleService } from "@medusajs/types"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
export const deletePricingRuleTypesStepId = "delete-pricing-rule-types"
export const deletePricingRuleTypesStep = createStep(
deletePricingRuleTypesStepId,
async (ids: string[], { container }) => {
const pricingModule = container.resolve<IPricingModuleService>(
ModuleRegistrationName.PRICING
)
// TODO: implement soft deleting rule types
// await pricingModule.softDeleteRuleTypes(ids)
await pricingModule.deleteRuleTypes(ids)
return new StepResponse(void 0, ids)
},
async (ids, { container }) => {
if (!ids?.length) {
return
}
const pricingModule = container.resolve<IPricingModuleService>(
ModuleRegistrationName.PRICING
)
// TODO: implement restoring soft deleted rule types
// await pricingModule.restoreRuleTypes(ids)
}
)

View File

@@ -0,0 +1,3 @@
export * from "./create-pricing-rule-types"
export * from "./delete-pricing-rule-types"
export * from "./update-pricing-rule-types"

View File

@@ -0,0 +1,48 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { IPricingModuleService, UpdateRuleTypeDTO } from "@medusajs/types"
import {
convertItemResponseToUpdateRequest,
getSelectsAndRelationsFromObjectArray,
} from "@medusajs/utils"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
export const updatePricingRuleTypesStepId = "update-pricing-rule-types"
export const updatePricingRuleTypesStep = createStep(
updatePricingRuleTypesStepId,
async (data: UpdateRuleTypeDTO[], { container }) => {
const pricingModule = container.resolve<IPricingModuleService>(
ModuleRegistrationName.PRICING
)
const { selects, relations } = getSelectsAndRelationsFromObjectArray(data)
const dataBeforeUpdate = await pricingModule.listRuleTypes(
{ id: data.map((d) => d.id) },
{ relations, select: selects }
)
const updatedRuleTypes = await pricingModule.updateRuleTypes(data)
return new StepResponse(updatedRuleTypes, {
dataBeforeUpdate,
selects,
relations,
})
},
async (revertInput, { container }) => {
if (!revertInput) {
return
}
const { dataBeforeUpdate = [], selects, relations } = revertInput
const pricingModule = container.resolve<IPricingModuleService>(
ModuleRegistrationName.PRICING
)
await pricingModule.update(
dataBeforeUpdate.map((data) =>
convertItemResponseToUpdateRequest(data, selects, relations)
)
)
}
)

View File

@@ -0,0 +1,13 @@
import { CreateRuleTypeDTO, RuleTypeDTO } from "@medusajs/types"
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
import { createPricingRuleTypesStep } from "../steps"
type WorkflowInput = { data: CreateRuleTypeDTO[] }
export const createPricingRuleTypesWorkflowId = "create-pricing-rule-types"
export const createPricingRuleTypesWorkflow = createWorkflow(
createPricingRuleTypesWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<RuleTypeDTO[]> => {
return createPricingRuleTypesStep(input.data)
}
)

View File

@@ -0,0 +1,12 @@
import { createWorkflow, WorkflowData } from "@medusajs/workflows-sdk"
import { deletePricingRuleTypesStep } from "../steps"
type WorkflowInput = { ids: string[] }
export const deletePricingRuleTypesWorkflowId = "delete-pricing-rule-types"
export const deletePricingRuleTypesWorkflow = createWorkflow(
deletePricingRuleTypesWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
deletePricingRuleTypesStep(input.ids)
}
)

View File

@@ -0,0 +1,3 @@
export * from "./create-pricing-rule-types"
export * from "./delete-pricing-rule-types"
export * from "./update-pricing-rule-types"

View File

@@ -0,0 +1,13 @@
import { RuleTypeDTO, UpdateRuleTypeDTO } from "@medusajs/types"
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
import { updatePricingRuleTypesStep } from "../steps"
type WorkflowInput = { data: UpdateRuleTypeDTO[] }
export const updatePricingRuleTypesWorkflowId = "update-pricing-rule-types"
export const updatePricingRuleTypesWorkflow = createWorkflow(
updatePricingRuleTypesWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<RuleTypeDTO[]> => {
return updatePricingRuleTypesStep(input.data)
}
)

View File

@@ -1,10 +1,13 @@
import { transformQuery } from "../../../api/middlewares"
import { transformBody, transformQuery } from "../../../api/middlewares"
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
import { authenticate } from "../../../utils/authenticate-middleware"
import * as QueryConfig from "./query-config"
import {
AdminDeletePricingRuleTypesRuleTypeReq,
AdminGetPricingRuleTypesParams,
AdminGetPricingRuleTypesRuleTypeParams,
AdminPostPricingRuleTypesReq,
AdminPostPricingRuleTypesRuleTypeReq,
} from "./validators"
export const adminPricingRoutesMiddlewares: MiddlewareRoute[] = [
@@ -22,6 +25,11 @@ export const adminPricingRoutesMiddlewares: MiddlewareRoute[] = [
),
],
},
{
method: ["POST"],
matcher: "/admin/pricing/rule-types",
middlewares: [transformBody(AdminPostPricingRuleTypesReq)],
},
{
method: ["GET"],
matcher: "/admin/pricing/rule-types/:id",
@@ -32,4 +40,14 @@ export const adminPricingRoutesMiddlewares: MiddlewareRoute[] = [
),
],
},
{
method: ["POST"],
matcher: "/admin/pricing/rule-types/:id",
middlewares: [transformBody(AdminPostPricingRuleTypesRuleTypeReq)],
},
{
method: ["DELETE"],
matcher: "/admin/pricing/rule-types/:id",
middlewares: [transformBody(AdminDeletePricingRuleTypesRuleTypeReq)],
},
]

View File

@@ -1,10 +1,20 @@
import {
deletePricingRuleTypesWorkflow,
updatePricingRuleTypesWorkflow,
} from "@medusajs/core-flows"
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { IPricingModuleService } from "@medusajs/types"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../types/routing"
import { AdminGetPricingRuleTypesRuleTypeParams } from "../../validators"
import { cleanResponseData } from "../../../../../utils/clean-response-data"
import { defaultAdminPricingRuleTypeFields } from "../../query-config"
import {
AdminDeletePricingRuleTypesRuleTypeReq,
AdminGetPricingRuleTypesRuleTypeParams,
AdminPostPricingRuleTypesRuleTypeReq,
} from "../../validators"
export const GET = async (
req: AuthenticatedMedusaRequest<AdminGetPricingRuleTypesRuleTypeParams>,
@@ -21,3 +31,47 @@ export const GET = async (
res.status(200).json({ rule_type: ruleType })
}
export const POST = async (
req: AuthenticatedMedusaRequest<AdminPostPricingRuleTypesRuleTypeReq>,
res: MedusaResponse
) => {
const workflow = updatePricingRuleTypesWorkflow(req.scope)
const { result, errors } = await workflow.run({
input: {
data: [{ ...req.validatedBody, id: req.params.id }],
},
throwOnError: false,
})
if (Array.isArray(errors) && errors[0]) {
throw errors[0].error
}
res.status(200).json({
rule_type: cleanResponseData(result[0], defaultAdminPricingRuleTypeFields),
})
}
export const DELETE = async (
req: AuthenticatedMedusaRequest<AdminDeletePricingRuleTypesRuleTypeReq>,
res: MedusaResponse
) => {
const id = req.params.id
const workflow = deletePricingRuleTypesWorkflow(req.scope)
const { errors } = await workflow.run({
input: { ids: [id] },
throwOnError: false,
})
if (Array.isArray(errors) && errors[0]) {
throw errors[0].error
}
res.status(200).json({
id,
object: "rule_type",
deleted: true,
})
}

View File

@@ -1,10 +1,16 @@
import { createPricingRuleTypesWorkflow } from "@medusajs/core-flows"
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { IPricingModuleService } from "@medusajs/types"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../types/routing"
import { AdminGetPricingRuleTypesParams } from "../validators"
import { cleanResponseData } from "../../../../utils/clean-response-data"
import { defaultAdminPricingRuleTypeFields } from "../query-config"
import {
AdminGetPricingRuleTypesParams,
AdminPostPricingRuleTypesReq,
} from "../validators"
export const GET = async (
req: AuthenticatedMedusaRequest<AdminGetPricingRuleTypesParams>,
@@ -28,3 +34,24 @@ export const GET = async (
limit,
})
}
export const POST = async (
req: AuthenticatedMedusaRequest<AdminPostPricingRuleTypesReq>,
res: MedusaResponse
) => {
const workflow = createPricingRuleTypesWorkflow(req.scope)
const ruleTypesData = [req.validatedBody]
const { result, errors } = await workflow.run({
input: { data: ruleTypesData },
throwOnError: false,
})
if (Array.isArray(errors) && errors[0]) {
throw errors[0].error
}
res.status(200).json({
rule_type: cleanResponseData(result[0], defaultAdminPricingRuleTypeFields),
})
}

View File

@@ -1,4 +1,4 @@
import { IsOptional, IsString } from "class-validator"
import { IsNotEmpty, IsNumber, IsOptional, IsString } from "class-validator"
import { FindParams, extendedFindParamsMixin } from "../../../types/common"
export class AdminGetPricingRuleTypesRuleTypeParams extends FindParams {}
@@ -10,3 +10,33 @@ export class AdminGetPricingRuleTypesParams extends extendedFindParamsMixin({
@IsOptional()
rule_attribute?: string[]
}
export class AdminPostPricingRuleTypesReq {
@IsNotEmpty()
@IsString()
name: string
@IsNotEmpty()
@IsString()
rule_attribute: string
@IsNotEmpty()
@IsNumber()
default_priority: number
}
export class AdminPostPricingRuleTypesRuleTypeReq {
@IsOptional()
@IsString()
name?: string
@IsOptional()
@IsString()
rule_attribute?: string
@IsOptional()
@IsNumber()
default_priority?: number
}
export class AdminDeletePricingRuleTypesRuleTypeReq {}