#Custom Review Workflow in MedusaJS v2 – Help Needed

3 messages · Page 1 of 1 (latest)

willow aurora
#

Hi everyone! 👋
I’m working on a custom product review system in MedusaJS v2 using workflows.

import {
  createWorkflow,
  transform,
  when,
  WorkflowResponse
} from "@medusajs/framework/workflows-sdk"
import { useQueryGraphStep, uploadFilesStep } from "@medusajs/medusa/core-flows"
import { createReviewStep } from "./steps/create-review"
import type { FileDTO } from "@medusajs/types"

export const createReviewWorkflow = createWorkflow("create-review", (input) => {
  // Step 1: Validate product
  useQueryGraphStep({
    entity: "product",
    fields: ["id"],
    filters: { id: input.product_id },
    options: { throwIfKeyNotFound: true },
  })

  // Step 2: Upload Images if any
  const uploadedFiles = when(input, (i) => !!i.images?.length).then(() =>
    uploadFilesStep({
      files: input.images?.map((img) => ({
        filename: img.filename,
        mimeType: img.mimeType,
        content: img.content,
        access: img.access,
      })) || [],
    })
  ) as FileDTO[]

  // Step 3: Create Review
  const review = createReviewStep({
    ...input,
    uploadedFileIds: uploadedFiles?.map((f) => f.id) || [],
  })

  return new WorkflowResponse({
    message: "Review created successfully",
    review,
  })
})

ERROR:
error: Error starting server
error: input?.images?.map is not a function
TypeError: input?.images?.map is not a function
at /home/sandip-shrestha/Desktop/Medusa/core/src/workflows/create-review.ts:47:39
at Object.then (/home/sandip-shrestha/Desktop/Medusa/core/node_modules/.pnpm/@medusajs+workflows-sdk@2.6.1_@mikro-

#

This is the Route Implementation for above code

import type {
    AuthenticatedMedusaRequest,
    MedusaResponse,
} from "@medusajs/framework/http"
import { createReviewWorkflow } from "../../../workflows/create-review"
import { z } from "zod"

export const PostStoreReviewSchema = z.object({
    title: z.string().optional(),
    content: z.string(),
    rating: z.preprocess(
        (val) => (typeof val === "string" ? parseFloat(val) : val),
        z.number().min(1).max(5)
    ),
    product_id: z.string(),
    first_name: z.string(),
    last_name: z.string(),
    parent_review_id: z.string().optional(),
})

type PostStoreReviewReq = z.infer<typeof PostStoreReviewSchema>

export const POST = async (
    req: AuthenticatedMedusaRequest<PostStoreReviewReq>,
    res: MedusaResponse
) => {
    const input = req.validatedBody
    const images = req.files as Express.Multer.File[]

    const { result } = await createReviewWorkflow(req.scope).run({
        input: {
            ...input,
            customer_id: req.auth_context?.actor_id,
            images: images.map((f) => ({
                filename: f.originalname,
                mimeType: f.mimetype,
                content: f.buffer.toString("base64"),
                access: "public",
            })),
        },
    })

    res.json({
        message: "Review created successfully",
        review: result,
    })
}

#

and this is the create-review step :

// steps/create-review.ts
import {
    createStep,
    StepResponse,
} from "@medusajs/framework/workflows-sdk"
import { PRODUCT_REVIEW_MODULE } from "../../modules/review"
import ProductReviewModuleService from "../../modules/review/service"

export type CreateReviewStepInput = {
    title?: string
    content: string
    rating: number
    product_id: string
    customer_id?: string
    first_name: string
    last_name: string
    uploadedFileIds: string[]
    parent_review_id?: string
}

export const createReviewStep = createStep(
    "create-review-step",
    async (input: CreateReviewStepInput, { container }) => {
        const reviewModuleService: ProductReviewModuleService = container.resolve(
            PRODUCT_REVIEW_MODULE
        )

        const review = await reviewModuleService.createReviews({
            ...input,
            images: input.uploadedFileIds,
        })

        return new StepResponse(review, review.id)
    },
    async (reviewId, { container }) => {
        if (!reviewId) {
            return
        }
        const reviewModuleService: ProductReviewModuleService = container.resolve(
            PRODUCT_REVIEW_MODULE
        )
        await reviewModuleService.deleteReviews(reviewId)
    }
)