Skip to content

Commit

Permalink
feat: Add v2 product types endpoints (#6880)
Browse files Browse the repository at this point in the history
Also adjusts the product type module APIs to follow the conventions
  • Loading branch information
sradevski authored Mar 29, 2024
1 parent 1bcb13f commit 1a48fe0
Show file tree
Hide file tree
Showing 21 changed files with 831 additions and 39 deletions.
8 changes: 8 additions & 0 deletions .changeset/fifty-badgers-crash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@medusajs/product": minor
"@medusajs/medusa": minor
"@medusajs/types": minor
"@medusajs/core-flows": patch
---

Add v2 product type endpoints and adjust the product module
30 changes: 30 additions & 0 deletions packages/core-flows/src/product/steps/create-product-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { IProductModuleService, ProductTypes } from "@medusajs/types"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"

export const createProductTypesStepId = "create-product-types"
export const createProductTypesStep = createStep(
createProductTypesStepId,
async (data: ProductTypes.CreateProductTypeDTO[], { container }) => {
const service = container.resolve<IProductModuleService>(
ModuleRegistrationName.PRODUCT
)

const created = await service.createTypes(data)
return new StepResponse(
created,
created.map((productType) => productType.id)
)
},
async (createdIds, { container }) => {
if (!createdIds?.length) {
return
}

const service = container.resolve<IProductModuleService>(
ModuleRegistrationName.PRODUCT
)

await service.deleteTypes(createdIds)
}
)
27 changes: 27 additions & 0 deletions packages/core-flows/src/product/steps/delete-product-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { IProductModuleService } from "@medusajs/types"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"

export const deleteProductTypesStepId = "delete-product-types"
export const deleteProductTypesStep = createStep(
deleteProductTypesStepId,
async (ids: string[], { container }) => {
const service = container.resolve<IProductModuleService>(
ModuleRegistrationName.PRODUCT
)

await service.softDeleteTypes(ids)
return new StepResponse(void 0, ids)
},
async (prevIds, { container }) => {
if (!prevIds?.length) {
return
}

const service = container.resolve<IProductModuleService>(
ModuleRegistrationName.PRODUCT
)

await service.restoreTypes(prevIds)
}
)
3 changes: 3 additions & 0 deletions packages/core-flows/src/product/steps/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ export * from "./delete-product-variants"
export * from "./create-collections"
export * from "./update-collections"
export * from "./delete-collections"
export * from "./create-product-types"
export * from "./update-product-types"
export * from "./delete-product-types"
42 changes: 42 additions & 0 deletions packages/core-flows/src/product/steps/update-product-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { IProductModuleService, ProductTypes } from "@medusajs/types"
import { getSelectsAndRelationsFromObjectArray } from "@medusajs/utils"
import { StepResponse, createStep } from "@medusajs/workflows-sdk"

type UpdateProductTypesStepInput = {
selector: ProductTypes.FilterableProductTypeProps
update: ProductTypes.UpdateProductTypeDTO
}

export const updateProductTypesStepId = "update-product-types"
export const updateProductTypesStep = createStep(
updateProductTypesStepId,
async (data: UpdateProductTypesStepInput, { container }) => {
const service = container.resolve<IProductModuleService>(
ModuleRegistrationName.PRODUCT
)

const { selects, relations } = getSelectsAndRelationsFromObjectArray([
data.update,
])

const prevData = await service.listTypes(data.selector, {
select: selects,
relations,
})

const productTypes = await service.updateTypes(data.selector, data.update)
return new StepResponse(productTypes, prevData)
},
async (prevData, { container }) => {
if (!prevData?.length) {
return
}

const service = container.resolve<IProductModuleService>(
ModuleRegistrationName.PRODUCT
)

await service.upsertTypes(prevData)
}
)
15 changes: 15 additions & 0 deletions packages/core-flows/src/product/workflows/create-product-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ProductTypes } from "@medusajs/types"
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
import { createProductTypesStep } from "../steps"

type WorkflowInput = { product_types: ProductTypes.CreateProductTypeDTO[] }

export const createProductTypesWorkflowId = "create-product-types"
export const createProductTypesWorkflow = createWorkflow(
createProductTypesWorkflowId,
(
input: WorkflowData<WorkflowInput>
): WorkflowData<ProductTypes.ProductTypeDTO[]> => {
return createProductTypesStep(input.product_types)
}
)
12 changes: 12 additions & 0 deletions packages/core-flows/src/product/workflows/delete-product-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
import { deleteProductTypesStep } from "../steps"

type WorkflowInput = { ids: string[] }

export const deleteProductTypesWorkflowId = "delete-product-types"
export const deleteProductTypesWorkflow = createWorkflow(
deleteProductTypesWorkflowId,
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
return deleteProductTypesStep(input.ids)
}
)
3 changes: 3 additions & 0 deletions packages/core-flows/src/product/workflows/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ export * from "./update-product-variants"
export * from "./create-collections"
export * from "./delete-collections"
export * from "./update-collections"
export * from "./create-product-types"
export * from "./delete-product-types"
export * from "./update-product-types"
20 changes: 20 additions & 0 deletions packages/core-flows/src/product/workflows/update-product-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ProductTypes } from "@medusajs/types"
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
import { updateProductTypesStep } from "../steps"

type UpdateProductTypesStepInput = {
selector: ProductTypes.FilterableProductTypeProps
update: ProductTypes.UpdateProductTypeDTO
}

type WorkflowInput = UpdateProductTypesStepInput

export const updateProductTypesWorkflowId = "update-product-types"
export const updateProductTypesWorkflow = createWorkflow(
updateProductTypesWorkflowId,
(
input: WorkflowData<WorkflowInput>
): WorkflowData<ProductTypes.ProductTypeDTO[]> => {
return updateProductTypesStep(input)
}
)
78 changes: 78 additions & 0 deletions packages/medusa/src/api-v2/admin/product-types/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {
deleteProductTypesWorkflow,
updateProductTypesWorkflow,
} from "@medusajs/core-flows"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../types/routing"

import { UpdateProductTypeDTO } from "@medusajs/types"
import { remoteQueryObjectFromString } from "@medusajs/utils"
import { refetchProductType } from "../helpers"

export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const remoteQuery = req.scope.resolve("remoteQuery")

const variables = { id: req.params.id }

const queryObject = remoteQueryObjectFromString({
entryPoint: "product_type",
variables,
fields: req.remoteQueryConfig.fields,
})

const [product_type] = await remoteQuery(queryObject)

res.status(200).json({ product_type })
}

export const POST = async (
req: AuthenticatedMedusaRequest<UpdateProductTypeDTO>,
res: MedusaResponse
) => {
const { result, errors } = await updateProductTypesWorkflow(req.scope).run({
input: {
selector: { id: req.params.id },
update: req.validatedBody,
},
throwOnError: false,
})

if (Array.isArray(errors) && errors[0]) {
throw errors[0].error
}

const productType = await refetchProductType(
result[0].id,
req.scope,
req.remoteQueryConfig.fields
)

res.status(200).json({ product_type: productType })
}

export const DELETE = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const id = req.params.id

const { errors } = await deleteProductTypesWorkflow(req.scope).run({
input: { ids: [id] },
throwOnError: false,
})

if (Array.isArray(errors) && errors[0]) {
throw errors[0].error
}

res.status(200).json({
id,
object: "product_type",
deleted: true,
})
}
20 changes: 20 additions & 0 deletions packages/medusa/src/api-v2/admin/product-types/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { MedusaContainer } from "@medusajs/types"
import { remoteQueryObjectFromString } from "@medusajs/utils"

export const refetchProductType = async (
productTypeId: string,
scope: MedusaContainer,
fields: string[]
) => {
const remoteQuery = scope.resolve("remoteQuery")
const queryObject = remoteQueryObjectFromString({
entryPoint: "product_type",
variables: {
filters: { id: productTypeId },
},
fields: fields,
})

const productTypes = await remoteQuery(queryObject)
return productTypes[0]
}
69 changes: 69 additions & 0 deletions packages/medusa/src/api-v2/admin/product-types/middlewares.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import * as QueryConfig from "./query-config"

import {
AdminGetProductTypesProductTypeParams,
AdminGetProductTypesParams,
AdminPostProductTypesProductTypeReq,
AdminPostProductTypesReq,
} from "./validators"
import { transformBody, transformQuery } from "../../../api/middlewares"

import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
import { authenticate } from "../../../utils/authenticate-middleware"

export const adminProductTypeRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/product-types/*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
},

{
method: ["GET"],
matcher: "/admin/product-types",
middlewares: [
transformQuery(
AdminGetProductTypesParams,
QueryConfig.listProductTypesTransformQueryConfig
),
],
},
{
method: ["GET"],
matcher: "/admin/product-types/:id",
middlewares: [
transformQuery(
AdminGetProductTypesProductTypeParams,
QueryConfig.retrieveProductTypeTransformQueryConfig
),
],
},
// Create/update/delete methods are new in v2
{
method: ["POST"],
matcher: "/admin/product-types",
middlewares: [
transformBody(AdminPostProductTypesReq),
transformQuery(
AdminGetProductTypesParams,
QueryConfig.retrieveProductTypeTransformQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/admin/product-types/:id",
middlewares: [
transformBody(AdminPostProductTypesProductTypeReq),
transformQuery(
AdminGetProductTypesProductTypeParams,
QueryConfig.retrieveProductTypeTransformQueryConfig
),
],
},
{
method: ["DELETE"],
matcher: "/admin/product-types/:id",
middlewares: [],
},
]
17 changes: 17 additions & 0 deletions packages/medusa/src/api-v2/admin/product-types/query-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const defaultAdminProductTypeFields = [
"id",
"value",
"created_at",
"updated_at",
]

export const retrieveProductTypeTransformQueryConfig = {
defaults: defaultAdminProductTypeFields,
isList: false,
}

export const listProductTypesTransformQueryConfig = {
...retrieveProductTypeTransformQueryConfig,
defaultLimit: 20,
isList: true,
}
Loading

0 comments on commit 1a48fe0

Please sign in to comment.