Skip to content

Feat/shipping options updatre#3

Open
DhirenMhatre wants to merge 14 commits into
developfrom
feat/shipping-options-updatre
Open

Feat/shipping options updatre#3
DhirenMhatre wants to merge 14 commits into
developfrom
feat/shipping-options-updatre

Conversation

@DhirenMhatre
Copy link
Copy Markdown

@DhirenMhatre DhirenMhatre commented Jun 6, 2026

User description

Summary

What — What changes are introduced in this PR?

Please provide answer here

Why — Why are these changes relevant or necessary?

Please provide answer here

How — How have these changes been implemented?

Please provide answer here

Testing — How have these changes been tested, or how can the reviewer test the feature?

Please provide answer here


Examples

Provide examples or code snippets that demonstrate how this feature works, or how it can be used in practice.
This helps with documentation and ensures maintainers can quickly understand and verify the change.

// Example usage

Checklist

Please ensure the following before requesting a review:

  • I have added a changeset for this PR
    • Every non-breaking change should be marked as a patch
    • To add a changeset, run yarn changeset and follow the prompts
  • The changes are covered by relevant tests
  • I have verified the code works as intended locally
  • I have linked the related issue(s) if applicable

Additional Context

Add any additional context, related issues, or references that might help the reviewer understand this PR.


CodeAnt-AI Description

Add shipping option updates and rule management in the admin API

What Changed

  • Admin shipping options can now be updated through a new POST /admin/shipping-options/:id endpoint.
  • Shipping options now support both create and update in the same workflow, so new options can be added and existing ones can be changed without separate paths.
  • Admins can now add and remove shipping option rules in batches, with validation and not-found errors returned when the shipping option or inputs are invalid.
  • Update requests now return the latest shipping option details after changes are applied.

Impact

✅ Faster shipping option edits
✅ Fewer admin API calls for shipping changes
✅ Clearer rule update handling

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@codity-chait
Copy link
Copy Markdown

codity-chait Bot commented Jun 6, 2026

PR Summary

What Changed

  • Replaced separate create/update steps with a unified upsertShippingOptions step that handles both operations with rollback support.
  • Added POST /admin/shipping-options/:id endpoint for updating shipping options.
  • Price handling is excluded from the update workflow (accepted but not processed).

Key Changes by Area

Fulfillment Module Service: Added upsertShippingOptions() method and refactored updateShippingOptions to support ID or selector-based updates with transaction-managed rule handling.

Core Flows: New updateShippingOptionsWorkflow and upsertShippingOptionsStep with compensation logic that fetches previous data for rollback.

Admin API: New endpoint at /admin/shipping-options/:id with Zod validation schema.

Types: Added UpsertShippingOptionDTO and workflow input/output interfaces; made id optional in update DTO to support upsert pattern.

Files Changed

File Changes Summary
integration-tests/modules/tests/fulfillment/admin/shipping-option-rules.spec.ts Updated test assertions for new API signatures
integration-tests/modules/tests/fulfillment/admin/shipping-options.spec.ts Updated to use new updateShippingOptions(id, data) signature
integration-tests/modules/tests/shipping-options/admin/shipping-options.spec.ts New test file for admin shipping options CRUD
integration-tests/modules/tests/fulfillment/workflows/create-shipping-options.ts Updated to use upsert step
integration-tests/modules/tests/shipping-options/workflows/update-shipping-options.ts New integration tests for update workflow with compensation
packages/core-flows/src/fulfillment/steps/create-shipping-options.ts Replaced by upsert step
packages/core-flows/src/fulfillment/steps/index.ts Export new upsert step
packages/core-flows/src/fulfillment/steps/upsert-shipping-options.ts New step handling create/update with compensation support
packages/core-flows/src/fulfillment/workflows/create-shipping-options.ts Updated to use upsert step
packages/core-flows/src/fulfillment/workflows/index.ts Export new update workflow
packages/core-flows/src/fulfillment/workflows/update-shipping-options.ts New workflow for dedicated update operations (price handling excluded)
packages/fulfillment/integration-tests/tests/fulfillment-module-service/shipping-option.spec.ts Updated for new service methods
packages/fulfillment/src/services/fulfillment-module-service.ts Added upsertShippingOptions and updateShippingOptions_ with rule management
packages/fulfillment/src/types/service.ts Updated service type definitions
packages/medusa/src/api-v2/admin/shipping-options/[id]/route.ts New POST endpoint for updating shipping options
packages/medusa/src/api-v2/admin/shipping-options/middlewares.ts Added route middleware for update endpoint
packages/medusa/src/api-v2/admin/shipping-options/validators.ts Added AdminUpdateShippingOption Zod schema
packages/types/src/fulfillment/mutations/shipping-option.ts Added UpsertShippingOptionDTO, made id optional in update DTO
packages/types/src/fulfillment/service.ts Updated service interface definitions
packages/types/src/workflow/fulfillment/index.ts Export new workflow types
packages/types/src/workflow/fulfillment/update-shipping-options.ts New workflow input/output interfaces

Review Focus Areas

  • Compensation logic in upsertShippingOptionsStep for rollback on partial failures.
  • Rule deletion/recreation in updateShippingOptions_ - verify no orphaned rules.
  • Price exclusion in update workflow: confirm intentional gap vs create workflow.

Architecture

Design Decisions: Unified upsert pattern reduces code duplication between create and update paths. The id field presence determines operation type. Price handling was deliberately excluded from update workflow (commented at update-shipping-options.ts:35-65) to limit scope.

Risks: Intentional: Price updates require separate handling (out of scope). Unintentional: Rule deletion in updateShippingOptions_ happens before recreation - verify atomicity under transaction.

Merge Status

NOT MERGEABLE — PR Score 32/100, below threshold (50)

  • [H4] PR quality score (32) is below merge floor (50)
  • [H5] 2 CRITICAL inline review findings need resolution

@codity-chait
Copy link
Copy Markdown

codity-chait Bot commented Jun 6, 2026

Workflow Diagrams

Automatically generated sequence diagrams showing the workflows in this PR

1. Workflow Analysis

Complex complexity • Components: update-shipping-options-workflow, create-shipping-options-workflow, upsert-shipping-options-step

sequenceDiagram
    title Shipping Options Update Workflow with Compensation

    participant Client
    participant AdminAPI as Admin API
    participant UpdateWorkflow as UpdateShippingOptionsWorkflow
    participant UpsertStep as UpsertShippingOptionsStep
    participant FulfillmentService as FulfillmentModuleService
    participant PricingService as PricingModuleService
    participant Database

    Client->>AdminAPI: POST /admin/shipping-options/:id
    AdminAPI->>UpdateWorkflow: Execute workflow with update data

    UpdateWorkflow->>UpsertStep: Invoke upsert shipping options

    alt Update existing shipping option
        UpsertStep->>FulfillmentService: updateShippingOptions(id, data)
        FulfillmentService->>Database: Update shipping option record
        FulfillmentService-->>UpsertStep: Return updated option
    else Create new shipping option
        UpsertStep->>FulfillmentService: createShippingOptions(data)
        FulfillmentService->>Database: Insert shipping option record
        FulfillmentService-->>UpsertStep: Return created option
    end

    alt Rules provided in update
        UpsertStep->>FulfillmentService: Update shipping option rules
        FulfillmentService->>Database: Sync rules (create/update/delete)
    end

    alt Type provided in update
        UpsertStep->>FulfillmentService: Update shipping option type
        FulfillmentService->>Database: Update type record
    end

    UpsertStep->>PricingService: Update price sets for shipping option
    PricingService->>Database: Update price records

    UpsertStep-->>UpdateWorkflow: Return shipping option result

    alt Workflow failure detected
        UpdateWorkflow->>UpsertStep: Trigger compensation
        Note right of UpsertStep: Compensation reverts all changes
        UpsertStep->>FulfillmentService: Revert to original state
        UpsertStep->>PricingService: Revert price changes
        UpsertStep-->>UpdateWorkflow: Compensation complete
    end

    UpdateWorkflow-->>AdminAPI: Return success or error
    AdminAPI-->>Client: 200 OK with shipping option data or error response

    Note over Client,Database: Batch Rules Management separate endpoints

    Client->>AdminAPI: POST /:id/rules/batch/add
    AdminAPI->>FulfillmentService: Add rules to shipping option
    FulfillmentService->>Database: Insert rule records
    FulfillmentService-->>AdminAPI: Return updated option with rules
    AdminAPI-->>Client: 200 OK

    Client->>AdminAPI: POST /:id/rules/batch/remove
    AdminAPI->>FulfillmentService: Remove rules by IDs
    FulfillmentService->>Database: Delete rule records
    FulfillmentService-->>AdminAPI: Return updated option
    AdminAPI-->>Client: 200 OK
Loading

Note: Diagrams show detected patterns only. Complex workflows may require manual review.

Comment on lines +18 to +34
const data = transform(input, (data) => {
const shippingOptionsIndexToPrices = data.map((option, index) => {
return {
shipping_option_index: index,
prices: option.prices,
}
})

return {
shippingOptions: data,
shippingOptionsIndexToPrices,
}
})

const updatedShippingOptions = upsertShippingOptionsStep(
data.shippingOptions
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functional Critical

The workflow collects shippingOptionsIndexToPrices from input but the subsequent steps that apply price updates are commented out. This causes any prices passed to updateShippingOptionsWorkflow to be silently discarded, leaving callers with stale pricing data and no error indication.

Suggested fix
    const normalizedShippingOptionsPrices = transform(
      {
        shippingOptions: updatedShippingOptions,
        shippingOptionsIndexToPrices: data.shippingOptionsIndexToPrices,
      },
      (data) => {
        const ruleTypes = new Set<Partial<RuleTypeDTO>>()
        const shippingOptionsPrices = data.shippingOptionsIndexToPrices.map(
          ({ shipping_option_index, prices }) => {
            prices?.forEach((price) => {
              if ("region_id" in price) {
                ruleTypes.add({
                  name: "region_id",
                  rule_attribute: "region_id",
                })
              }
            })

            return {
              id: data.shippingOptions[shipping_option_index].id,
              prices,
            }
          }
        )

        return {
          shippingOptionsPrices,
          ruleTypes: Array.from(ruleTypes) as UpdateRuleTypeDTO[],
        }
      }
    )

    return upsertShippingOptionsPricesStep(normalizedShippingOptionsPrices.shippingOptionsPrices)
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/core-flows/src/fulfillment/workflows/update-shipping-options.ts
Lines: 18-34
Issue Type: functional-critical
Severity: critical

Issue Description:
The workflow collects `shippingOptionsIndexToPrices` from input but the subsequent steps that apply price updates are commented out. This causes any prices passed to `updateShippingOptionsWorkflow` to be silently discarded, leaving callers with stale pricing data and no error indication.

Current Code:
    const data = transform(input, (data) => {
      const shippingOptionsIndexToPrices = data.map((option, index) => {
        return {
          shipping_option_index: index,
          prices: option.prices,
        }
      })

      return {
        shippingOptions: data,
        shippingOptionsIndexToPrices,
      }
    })

    const updatedShippingOptions = upsertShippingOptionsStep(
      data.shippingOptions
    )

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

arrayDifference,
getSelectsAndRelationsFromObjectArray,
} from "@medusajs/utils"
import { UpsertShippingOptionDTO } from "@medusajs/types/src"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functional Critical

The UpsertShippingOptionDTO type is imported from the internal TypeScript source path @medusajs/types/src instead of the published entry point @medusajs/types. This import path does not exist in compiled/distributed package builds, causing runtime failures in non-monorepo consumers and CI builds that compile packages independently.

Suggested change
import { UpsertShippingOptionDTO } from "@medusajs/types/src"
import { UpsertShippingOptionDTO } from "@medusajs/types"
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/core-flows/src/fulfillment/steps/upsert-shipping-options.ts
Lines: 12-12
Issue Type: functional-critical
Severity: critical

Issue Description:
The `UpsertShippingOptionDTO` type is imported from the internal TypeScript source path `@medusajs/types/src` instead of the published entry point `@medusajs/types`. This import path does not exist in compiled/distributed package builds, causing runtime failures in non-monorepo consumers and CI builds that compile packages independently.

Current Code:
import { UpsertShippingOptionDTO } from "@medusajs/types/src"

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

Comment on lines +17 to +22
const shippingOptionPayload = req.validatedBody

const workflow = updateShippingOptionsWorkflow(req.scope)

const { result, errors } = await workflow.run({
input: [shippingOptionPayload],
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functional High

The POST /admin/shipping-options/:id route handler ignores the URL path parameter (req.params.id) and uses only the id from req.validatedBody, allowing clients to update arbitrary shipping options by specifying a different id in the request body. This violates REST semantics where the URL path should identify the resource being modified.

Suggested fix
  const shippingOptionPayload = {
      ...req.validatedBody,
      id: req.params.id,
    }
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/medusa/src/api-v2/admin/shipping-options/[id]/route.ts
Lines: 17-22
Issue Type: functional-high
Severity: high

Issue Description:
The POST /admin/shipping-options/:id route handler ignores the URL path parameter (req.params.id) and uses only the id from req.validatedBody, allowing clients to update arbitrary shipping options by specifying a different id in the request body. This violates REST semantics where the URL path should identify the resource being modified.

Current Code:
  const shippingOptionPayload = req.validatedBody

  const workflow = updateShippingOptionsWorkflow(req.scope)

  const { result, errors } = await workflow.run({
    input: [shippingOptionPayload],

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

Comment on lines +89 to +98
export const AdminUpdateShippingOption = z
.object({
id: z.string(),
name: z.string().optional(),
data: z.record(z.unknown()).optional(),
price_type: z.nativeEnum(ShippingOptionPriceTypeEnum).optional(),
provider_id: z.string().optional(),
type: AdminCreateShippingOptionTypeObject.optional(),
})
.strict()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functional High

The AdminUpdateShippingOption validator includes id: z.string() as a required field in the request body, but the endpoint is POST /admin/shipping-options/:id where the id is already provided in the URL path. This forces clients to redundantly supply the id in both locations, and since the route handler ignores req.params.id, any mismatch between the URL parameter and body id silently operates on the body's value, making the URL segment ineffective.

Suggested fix
export const AdminUpdateShippingOption = z
  .object({
    name: z.string().optional(),
    data: z.record(z.unknown()).optional(),
    price_type: z.nativeEnum(ShippingOptionPriceTypeEnum).optional(),
    provider_id: z.string().optional(),
    type: AdminCreateShippingOptionTypeObject.optional(),
  })
  .strict()
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/medusa/src/api-v2/admin/shipping-options/validators.ts
Lines: 89-98
Issue Type: functional-high
Severity: high

Issue Description:
The `AdminUpdateShippingOption` validator includes `id: z.string()` as a required field in the request body, but the endpoint is `POST /admin/shipping-options/:id` where the id is already provided in the URL path. This forces clients to redundantly supply the id in both locations, and since the route handler ignores `req.params.id`, any mismatch between the URL parameter and body id silently operates on the body's value, making the URL segment ineffective.

Current Code:
export const AdminUpdateShippingOption = z
  .object({
    id: z.string(),
    name: z.string().optional(),
    data: z.record(z.unknown()).optional(),
    price_type: z.nativeEnum(ShippingOptionPriceTypeEnum).optional(),
    provider_id: z.string().optional(),
    type: AdminCreateShippingOptionTypeObject.optional(),
  })
  .strict()

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

Comment on lines +46 to +57
if (toUpdate.length) {
const { selects, relations } =
getSelectsAndRelationsFromObjectArray(toUpdate)
toUpdatePreviousData = await fulfillmentService.listShippingOptions(
{
id: toUpdate.map((s) => s.id),
},
{
select: selects,
relations,
}
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functional High

The snapshot of data before updates is retrieved with only the fields present in the update payload, causing fields not included in the update to be undefined. When the compensation handler restores this incomplete snapshot via upsertShippingOptions, it corrupts fields that were never intended to be modified. The snapshot must fetch all persisted fields regardless of which fields are being updated.

Suggested fix
    if (toUpdate.length) {
      toUpdatePreviousData = await fulfillmentService.listShippingOptions(
        {
          id: toUpdate.map((s) => s.id),
        }
      )
    }
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/core-flows/src/fulfillment/steps/upsert-shipping-options.ts
Lines: 46-57
Issue Type: functional-high
Severity: high

Issue Description:
The snapshot of data before updates is retrieved with only the fields present in the update payload, causing fields not included in the update to be undefined. When the compensation handler restores this incomplete snapshot via upsertShippingOptions, it corrupts fields that were never intended to be modified. The snapshot must fetch all persisted fields regardless of which fields are being updated.

Current Code:
    if (toUpdate.length) {
      const { selects, relations } =
        getSelectsAndRelationsFromObjectArray(toUpdate)
      toUpdatePreviousData = await fulfillmentService.listShippingOptions(
        {
          id: toUpdate.map((s) => s.id),
        },
        {
          select: selects,
          relations,
        }
      )
    }

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functional Medium

The test "should create a shipping option successfully" is located in the describe("POST /admin/shipping-options/:id") block (which covers the update endpoint) but calls POST /admin/shipping-options (the create endpoint) instead of testing the update endpoint. This test should be moved to the create endpoint describe block, and a proper test for the update endpoint should be added to provide positive test coverage for POST /admin/shipping-options/:id.

Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: integration-tests/modules/__tests__/shipping-options/admin/shipping-options.spec.ts
Lines: 133-139
Issue Type: functional-medium
Severity: medium

Issue Description:
The test "should create a shipping option successfully" is located in the `describe("POST /admin/shipping-options/:id")` block (which covers the update endpoint) but calls `POST /admin/shipping-options` (the create endpoint) instead of testing the update endpoint. This test should be moved to the create endpoint describe block, and a proper test for the update endpoint should be added to provide positive test coverage for `POST /admin/shipping-options/:id`.

Current Code:
        it("should create a shipping option successfully", async () => {
          const shippingOptionPayload = {
            name: "Test shipping option",
            service_zone_id: fulfillmentSet.service_zones[0].id,
            shipping_profile_id: shippingProfile.id,
            provider_id: "manual_test-provider",
            price_type: "flat",

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

@codity-chait
Copy link
Copy Markdown

codity-chait Bot commented Jun 6, 2026

Security Scan Summary

Metric Value
Vulnerabilities Critical: 0
Overall Risk Clean
Files Scanned 20

No critical security issues detected

Scan completed in 36.7s

Security scan powered by Codity.ai

@codity-chait
Copy link
Copy Markdown

codity-chait Bot commented Jun 6, 2026

License Compliance Scan

Metric Value
Packages Scanned 754
High Risk (Strong Copyleft) 0
Medium Risk (Weak Copyleft) 3
Low Risk (Permissive) 697
Unknown License 54

Weak copyleft licenses found - verify compatibility

Some packages have unknown licenses - manual review required

Medium Risk Licenses - 3 packages

MPL-2.0 (3 packages):

  • eslint-config-turbo 1.10.12
  • next-mdx-remote 4.4.1
  • client-sessions 0.8.0
Unknown Licenses - 54 packages
  • @medusajs/promotion workspace:^
  • @medusajs/auth workspace:*
  • @medusajs/workflow-engine-inmemory workspace:*
  • @medusajs/pricing workspace:^
  • @medusajs/modules-sdk workspace:^
  • @medusajs/utils workspace:^
  • @medusajs/api-key workspace:^
  • @medusajs/inventory-next workspace:^
  • @medusajs/tax workspace:^
  • @medusajs/types workspace:^
  • @medusajs/event-bus-local workspace:*
  • @medusajs/medusa workspace:*
  • @medusajs/user workspace:^
  • @medusajs/product workspace:^
  • @medusajs/store workspace:^
  • @medusajs/cache-inmemory workspace:*
  • @medusajs/customer workspace:^
  • @medusajs/region workspace:^
  • @medusajs/currency workspace:^
  • @medusajs/inventory workspace:^

...and 34 more

Powered by Codity.ai · Docs

@codity-chait
Copy link
Copy Markdown

codity-chait Bot commented Jun 6, 2026

Code Quality Report — test-org-codity/medusa · PR #3

Scanned: 2026-06-06 18:37 UTC | Score: 54/100 | Provider: github

Executive Summary

Severity Count
Critical 0
High 1
Medium 3
Low 12
Top Findings

[CQ-LLM-001] integration-tests/modules/__tests__/shipping-options/admin/shipping-options.spec.ts:202 (Complexity · HIGH)

Issue: The test function 'it' for creating a shipping option is quite large and contains multiple assertions, which increases cyclomatic complexity.
Suggestion: Consider breaking this test into smaller, more focused tests to improve readability and maintainability.

it("should create a shipping option successfully", async () => { ... });

[CQ-LLM-002] integration-tests/modules/__tests__/shipping-options/admin/shipping-options.spec.ts:202 (Duplication · MEDIUM)

Issue: The payload for creating a shipping option is duplicated in both test cases.
Suggestion: Extract the common payload into a variable or a helper function to adhere to the DRY principle.

const shippingOptionPayload = { ... };

[CQ-LLM-003] integration-tests/modules/__tests__/shipping-options/admin/shipping-options.spec.ts:202 (Error_Handling · MEDIUM)

Issue: The error handling for the API call does not check for null or undefined values before accessing properties.
Suggestion: Add null checks for 'err' before accessing 'err.status' and 'err.data'.

expect(err.status).toEqual(400)

[CQ-LLM-005] integration-tests/modules/__tests__/shipping-options/workflows/update-shipping-options.ts:0 (Testability · MEDIUM)

Issue: The test relies on global state and does not use dependency injection, making it harder to test in isolation.
Suggestion: Refactor the code to use dependency injection for services to improve testability.

let service: IFulfillmentModuleService

[CQ-LLM-004] integration-tests/modules/__tests__/shipping-options/admin/shipping-options.spec.ts:202 (Documentation · LOW)

Issue: Missing documentation for the test cases, which makes it harder to understand the purpose of each test.
Suggestion: Add comments or JSDoc to describe the purpose and expected outcomes of each test case.

it("should create a shipping option successfully", async () => { ... });

[CQ-008] integration-tests/modules/__tests__/shipping-options/admin/shipping-options.spec.ts:261 (Maintainability · LOW)

Issue: Magic number 400 in code
Suggestion: Extract to a named constant

expect(err.status).toEqual(400)

[CQ-008] integration-tests/modules/__tests__/shipping-options/admin/shipping-options.spec.ts:299 (Maintainability · LOW)

Issue: Magic number 200 in code
Suggestion: Extract to a named constant

expect(response.status).toEqual(200)

[CQ-011] integration-tests/modules/__tests__/shipping-options/workflows/update-shipping-options.ts:12 (Duplication · LOW)

Issue: Duplicate import: 'import {'
Suggestion: Remove the duplicate import statement

import {

[CQ-011] integration-tests/modules/__tests__/shipping-options/workflows/update-shipping-options.ts:16 (Duplication · LOW)

Issue: Duplicate import: 'import {'
Suggestion: Remove the duplicate import statement

import {

[CQ-008] integration-tests/modules/__tests__/shipping-options/workflows/update-shipping-options.ts:22 (Maintainability · LOW)

Issue: Magic number 100000 in code
Suggestion: Extract to a named constant

jest.setTimeout(100000)

Per-File Breakdown

File Critical High Medium Low Total
integration-tests/modules/__tests__/shipping-options/admin/shipping-options.spec.ts 0 1 2 3 6
integration-tests/modules/__tests__/shipping-options/workflows/update-shipping-options.ts 0 0 1 6 7
packages/core-flows/src/fulfillment/steps/upsert-shipping-options.ts 0 0 0 1 1
packages/medusa/src/api-v2/admin/shipping-options/[id]/route.ts 0 0 0 2 2

Recommendations

  1. Resolve High severity issues, especially error handling gaps and performance bottlenecks.
  • Run automated tests after applying fixes to verify no regressions.

@DhirenMhatre
Copy link
Copy Markdown
Author

@CodeAnt-AI: review

@DhirenMhatre
Copy link
Copy Markdown
Author

@CodeAnt-AI: review

1 similar comment
@DhirenMhatre
Copy link
Copy Markdown
Author

@CodeAnt-AI: review

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Jun 6, 2026

CodeAnt AI is running the review.

@codeant-ai codeant-ai Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files label Jun 6, 2026
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Jun 6, 2026

Sequence Diagram

This PR adds an admin endpoint that updates shipping options by running a fulfillment workflow which upserts the option and then returns the latest details. The flow centralizes update logic in the fulfillment service while keeping the admin API response consistent.

sequenceDiagram
    participant Admin
    participant AdminAPI
    participant Workflow
    participant FulfillmentService
    participant DataStore

    Admin->>AdminAPI: POST update shipping option
    AdminAPI->>Workflow: Start update shipping options workflow
    Workflow->>FulfillmentService: Upsert shipping option data
    FulfillmentService-->>Workflow: Updated shipping option id
    AdminAPI->>DataStore: Retrieve latest shipping option
    DataStore-->>AdminAPI: Shipping option details
    AdminAPI-->>Admin: 200 OK with updated shipping option
Loading

Generated by CodeAnt AI

arrayDifference,
getSelectsAndRelationsFromObjectArray,
} from "@medusajs/utils"
import { UpsertShippingOptionDTO } from "@medusajs/types/src"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Importing UpsertShippingOptionDTO from @medusajs/types/src creates a package boundary break: @medusajs/types only publishes dist, so consumers of the published package can hit runtime module-resolution failures (Cannot find module '@medusajs/types/src'). Import this type from the public @medusajs/types entrypoint instead. [import error]

Severity Level: Major ⚠️
- ❌ External TS apps using core-flows fail type-checking.
- ⚠️ Admin shipping options workflows unusable in external projects.
Steps of Reproduction ✅
1. In `packages/core-flows/src/fulfillment/steps/upsert-shipping-options.ts:12`, note the
import `import { UpsertShippingOptionDTO } from "@medusajs/types/src"`, which references
the internal `src` folder of `@medusajs/types`.

2. Inspect `packages/types/package.json:5-6,18-20`, which declares `"main":
"dist/index.js"`, `"types": "dist/index.d.ts"`, and `"files": ["dist"]`, meaning only the
`dist` directory (not `src`) is published for `@medusajs/types`.

3. Build and publish `@medusajs/types` and `@medusajs/core-flows` using their
`prepublishOnly`/`build` scripts (`packages/types/package.json:33-37`,
`packages/core-flows/package.json:39-42`). The generated declaration for
`upsert-shipping-options.ts` will retain a type import from `"@medusajs/types/src"` in
`dist` because `UpsertShippingOptionDTO` is used purely as a type (e.g., `input as
UpsertShippingOptionDTO[]` at line 62), so the import is elided from JS but kept in
`.d.ts`.

4. In an external TypeScript project, install `@medusajs/types` and `@medusajs/core-flows`
and import any workflow that pulls in this step, e.g. `createShippingOptionsWorkflow` or
`updateShippingOptionsWorkflow` (exported via
`packages/core-flows/src/fulfillment/workflows/create-shipping-options.ts:14-16` and
`update-shipping-options.ts:9-11`, and used in
`packages/medusa/src/api-v2/admin/shipping-options/route.ts:1` and `[id]/route.ts:7`).
Running `tsc` will cause TypeScript to process the `@medusajs/core-flows` declarations,
encounter `import { UpsertShippingOptionDTO } from "@medusajs/types/src"`, and fail with a
module resolution error because the published `@medusajs/types` package does not contain a
`src` entry point.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/core-flows/src/fulfillment/steps/upsert-shipping-options.ts
**Line:** 12:12
**Comment:**
	*Import Error: Importing `UpsertShippingOptionDTO` from `@medusajs/types/src` creates a package boundary break: `@medusajs/types` only publishes `dist`, so consumers of the published package can hit runtime module-resolution failures (`Cannot find module '@medusajs/types/src'`). Import this type from the public `@medusajs/types` entrypoint instead.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +293 to +297
const response = await api.post(
`/admin/shipping-options`,
shippingOptionPayload,
adminHeaders
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Inside the POST /admin/shipping-options/:id describe block, this test still calls the create endpoint (/admin/shipping-options) and only validates create behavior. It never exercises the update route, so regressions in the new update endpoint can pass unnoticed. Call /admin/shipping-options/${shippingOptionId} with an update payload and assert updated fields instead. [api mismatch]

Severity Level: Major ⚠️
- ⚠️ Update endpoint success path lacks direct API test.
- ⚠️ Create endpoint is redundantly tested in wrong block.
- ⚠️ Regressions in update handler may go undetected.
Steps of Reproduction ✅
1. Open
`integration-tests/modules/__tests__/shipping-options/admin/shipping-options.spec.ts` and
locate the `describe("POST /admin/shipping-options/:id", () => {` block starting at line
206, which is intended to cover the admin update endpoint `/admin/shipping-options/:id`
handled by `packages/medusa/src/api-v2/admin/shipping-options/[id]/route.ts:13–44`.

2. Inside this describe block, note the first test `"should throw error when required
params are missing"` (lines 207–266), which correctly calls
`api.post(`/admin/shipping-options/${shippingOptionId}`, ...)` and asserts a 400 based on
the `AdminUpdateShippingOption` validator
(`packages/medusa/src/api-v2/admin/shipping-options/validators.ts:89–101`).

3. Observe the second test `"should create a shipping option successfully"` (lines 268–179
in the hunk; specifically 293–297 for the snippet), which builds `shippingOptionPayload`
and then calls `api.post(`/admin/shipping-options`, shippingOptionPayload, adminHeaders)`
at lines 293–297—this is the create endpoint wired to `createShippingOptionsWorkflow` in
`packages/medusa/src/api-v2/admin/shipping-options/route.ts:13–44`, not the update
endpoint.

4. If a regression is introduced in the update handler (for example, a bug in
`updateShippingOptionsWorkflow` at
`packages/core-flows/src/fulfillment/workflows/update-shipping-options.ts:11–35` that
causes valid updates to fail), the current tests under `describe("POST
/admin/shipping-options/:id")` will not catch it, because the only success-path test in
that block exercises the create route instead of `/admin/shipping-options/:id`.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** integration-tests/modules/__tests__/shipping-options/admin/shipping-options.spec.ts
**Line:** 293:297
**Comment:**
	*Api Mismatch: Inside the `POST /admin/shipping-options/:id` describe block, this test still calls the create endpoint (`/admin/shipping-options`) and only validates create behavior. It never exercises the update route, so regressions in the new update endpoint can pass unnoticed. Call `/admin/shipping-options/${shippingOptionId}` with an update payload and assert updated fields instead.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +32 to +90
const updatedShippingOptions = upsertShippingOptionsStep(
data.shippingOptions
)

/*const normalizedShippingOptionsPrices = transform(
{
shippingOptions: updatedShippingOptions,
shippingOptionsIndexToPrices: data.shippingOptionsIndexToPrices,
},
(data) => {
const ruleTypes = new Set<Partial<RuleTypeDTO>>()
const shippingOptionsPrices = data.shippingOptionsIndexToPrices.map(
({ shipping_option_index, prices }) => {
prices?.forEach((price) => {
if ("region_id" in price) {
ruleTypes.add({
name: "region_id",
rule_attribute: "region_id",
})
}
})

return {
id: data.shippingOptions[shipping_option_index].id,
prices,
}
}
)

return {
shippingOptionsPrices,
ruleTypes: Array.from(ruleTypes) as UpdateRuleTypeDTO[],
}
}
)*/

/*updatePricingRuleTypesStep(normalizedShippingOptionsPrices.ruleTypes)*/

/*const shippingOptionsPriceSetsLinkData = updateShippingOptionsPriceSetsStep(
normalizedShippingOptionsPrices.shippingOptionsPrices
)

const normalizedLinkData = transform(
{
shippingOptionsPriceSetsLinkData,
},
(data) => {
return data.shippingOptionsPriceSetsLinkData.map((item) => {
return {
id: item.id,
price_sets: [item.priceSetId],
}
})
}
)*/

/*setShippingOptionsPriceSetsStep(normalizedLinkData)*/

return updatedShippingOptions
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The workflow input type supports prices, but the workflow only upserts shipping-option entities and returns, without running any pricing rule-type or price-set update steps. As a result, callers can pass prices and get a successful response while price data remains unchanged, which is a contract mismatch and silent data-update failure. [incomplete implementation]

Severity Level: Major ⚠️
- ⚠️ Update workflow accepts prices but never applies them.
- ⚠️ Future callers may see silent price-update failures.
- ⚠️ Behavior diverges from create workflow pricing contract.
Steps of Reproduction ✅
1. The workflow contract `UpdateShippingOptionsWorkflowInput` at
`packages/types/src/workflow/fulfillment/update-shipping-options.ts:4–31` includes an
optional `prices` array and `rules` field, suggesting callers can update shipping-option
prices through the `updateShippingOptionsWorkflow`.

2. The admin API update route
`packages/medusa/src/api-v2/admin/shipping-options/[id]/route.ts:13–24` constructs a
workflow from `updateShippingOptionsWorkflow(req.scope)` and runs it with `input:
[shippingOptionPayload]` (line 22). However, `AdminUpdateShippingOption` in
`packages/medusa/src/api-v2/admin/shipping-options/validators.ts:89–99` does not include
`prices`, so any caller that actually sends `prices` must use the workflow directly (as in
`integration-tests/modules/__tests__/shipping-options/workflows/update-shipping-options.ts:66–127`).

3. In the workflow implementation at
`packages/core-flows/src/fulfillment/workflows/update-shipping-options.ts:18–30`, the
`transform` call builds `shippingOptionsIndexToPrices` from `option.prices`, but this
structure is only referenced in the commented-out pricing logic at lines 36–88; the only
active step is `const updatedShippingOptions =
upsertShippingOptionsStep(data.shippingOptions)` at lines 32–34 followed by `return
updatedShippingOptions` at line 90.

4. Inside `upsertShippingOptionsStep`
(`packages/core-flows/src/fulfillment/steps/upsert-shipping-options.ts:14–63`), the step
input type explicitly omits `"prices"` (type `StepInput = Omit<...,"prices">[]` at lines
14–18) and the step ultimately calls `fulfillmentService.upsertShippingOptions(input as
UpsertShippingOptionDTO[])` at lines 60–63, which updates only the shipping option
entities via `FulfillmentModuleService.upsertShippingOptions`
(`packages/fulfillment/src/services/fulfillment-module-service.ts:22–41, 43–86`) and does
not invoke any price-set or pricing rule-type workflows.

5. As a result, if a caller uses `updateShippingOptionsWorkflow(container).run({ input: [
{ id: <existingId>, prices: [...] } ] })` in code patterned after
`integration-tests/modules/__tests__/shipping-options/workflows/update-shipping-options.ts:110–127`,
the call will succeed and return updated shipping options, but the provided `prices` are
ignored: the pricing-related transform data (`shippingOptionsIndexToPrices`) is unused and
no pricing update steps comparable to those in `createShippingOptionsWorkflow`
(`packages/core-flows/src/fulfillment/workflows/create-shipping-options.ts:41–95`) are
executed.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/core-flows/src/fulfillment/workflows/update-shipping-options.ts
**Line:** 32:90
**Comment:**
	*Incomplete Implementation: The workflow input type supports `prices`, but the workflow only upserts shipping-option entities and returns, without running any pricing rule-type or price-set update steps. As a result, callers can pass `prices` and get a successful response while price data remains unchanged, which is a contract mismatch and silent data-update failure.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎


export interface UpdateShippingOptionDTO {
id: string
id?: string
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Adding id to UpdateShippingOptionDTO introduces an id-conflict bug with existing update logic that merges { id: resolvedId, ...data }; if data.id is present it overrides the resolved target id and can update the wrong shipping option. Keep id out of the generic update DTO (or explicitly omit it in update method signatures) and only allow id in dedicated upsert input shapes. [logic error]

Severity Level: Critical 🚨
- ❌ updateShippingOptions may mutate a different shipping option record.
- ⚠️ Data inconsistency in fulfillment module affecting shipping rules.
Steps of Reproduction ✅
1. Observe `UpdateShippingOptionDTO` in
`packages/types/src/fulfillment/mutations/shipping-option.ts:16-29` now includes an
optional `id?: string`, and `IFulfillmentModuleService.updateShippingOptions` in
`packages/types/src/fulfillment/service.ts:8-17` accepts `data: UpdateShippingOptionDTO`.

2. In the service implementation `FulfillmentModuleService.updateShippingOptions` in
`packages/fulfillment/src/services/fulfillment-module-service.ts:1049-1095`, note that for
the overload taking an `id: string`, the method constructs `normalizedInput.push({ id:
idOrSelector, ...data })` at lines 1068-1071.

3. Using the existing integration-test pattern from
`packages/fulfillment/integration-tests/__tests__/fulfillment-module-service/shipping-option.spec.ts:596-623`,
call `service.updateShippingOptions(targetId, payload)` where `targetId` is the ID you
intend to update, but set `payload: UpdateShippingOptionDTO = { id: someOtherId, name:
"updated-test", ... }` so that `payload.id` differs from `targetId`.

4. At runtime, due to the spread order `{ id: idOrSelector, ...data }`, `data.id`
overwrites `idOrSelector`, so `normalizedInput` carries `id = someOtherId`;
`updateShippingOptions_` at
`packages/fulfillment/src/services/fulfillment-module-service.ts:1097-1111` then uses
`dataArray.map((s) => s.id)` to select and update shipping options, resulting in the
method silently updating the shipping option with `someOtherId` instead of the one
referenced by the `id` argument.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/types/src/fulfillment/mutations/shipping-option.ts
**Line:** 17:17
**Comment:**
	*Logic Error: Adding `id` to `UpdateShippingOptionDTO` introduces an id-conflict bug with existing update logic that merges `{ id: resolvedId, ...data }`; if `data.id` is present it overrides the resolved target id and can update the wrong shipping option. Keep `id` out of the generic update DTO (or explicitly omit it in update method signatures) and only allow `id` in dedicated upsert input shapes.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +31 to +33
export interface UpsertShippingOptionDTO extends UpdateShippingOptionDTO {

}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: UpsertShippingOptionDTO is incorrectly derived from UpdateShippingOptionDTO, which makes create-path required fields like name, price_type, service_zone_id, shipping_profile_id, and provider_id optional at compile time. This lets callers send incomplete create payloads that pass type checks but fail at runtime when upsertShippingOptions routes them into the create flow. [api mismatch]

Severity Level: Major ⚠️
- ❌ Fulfillment upsert API accepts invalid create payloads, causing failures.
- ⚠️ Admin shipping-options workflows rely on unsafely typed upsert input.
Steps of Reproduction ✅
1. Note the create DTO in
`packages/types/src/fulfillment/mutations/shipping-option.ts:5-13` where
`CreateShippingOptionDTO` requires `name`, `price_type`, `service_zone_id`,
`shipping_profile_id`, `provider_id`, and `type`.

2. Observe `UpdateShippingOptionDTO` in the same file at lines 16-29, where all these
fields are optional and `id` is also optional, then see `UpsertShippingOptionDTO extends
UpdateShippingOptionDTO` at lines 31-33, meaning an object like `{ data: {} }` (no `id`,
no required create fields) is assignable to `UpsertShippingOptionDTO`.

3. Call the fulfillment module's upsert API
`IFulfillmentModuleService.upsertShippingOptions(data: UpsertShippingOptionDTO |
UpsertShippingOptionDTO[])` defined in `packages/types/src/fulfillment/service.ts:22-29`
with a value such as `[{ data: { foo: "bar" } }]` that has no `id` and omits `name`,
`price_type`, `service_zone_id`, `shipping_profile_id`, `provider_id`, and `type`.

4. At runtime, `FulfillmentModuleService.upsertShippingOptions_` in
`packages/fulfillment/src/services/fulfillment-module-service.ts:1232-1249` splits input
into `forUpdate` and `forCreate` based solely on `!!shippingOption.id`; the object without
`id` is treated (via the type guard) as `CreateShippingOptionDTO` and passed into
`createShippingOptions_` at lines 395-420, where it is forwarded to
`shippingOptionService_.create` without the actually required fields, leading to runtime
validation or persistence errors despite passing TypeScript checks.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/types/src/fulfillment/mutations/shipping-option.ts
**Line:** 31:33
**Comment:**
	*Api Mismatch: `UpsertShippingOptionDTO` is incorrectly derived from `UpdateShippingOptionDTO`, which makes create-path required fields like `name`, `price_type`, `service_zone_id`, `shipping_profile_id`, and `provider_id` optional at compile time. This lets callers send incomplete create payloads that pass type checks but fail at runtime when `upsertShippingOptions` routes them into the create flow.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

name?: string
service_zone_id?: string
shipping_profile_id?: string
data?: Record<string, unknown>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The new workflow input narrows data to Record<string, unknown> and drops null, while the fulfillment update DTO allows data to be null for clearing stored data. This type mismatch breaks API consistency and prevents valid "clear data" updates through the workflow type. [api mismatch]

Severity Level: Major ⚠️
- ❌ Admin update endpoint cannot clear shipping option `data`.
- ⚠️ Workflow type diverges from fulfillment service update contract.
Steps of Reproduction ✅
1. Note that the underlying fulfillment DTO `UpdateShippingOptionDTO` in
`packages/types/src/fulfillment/mutations/shipping-option.ts:16-25` allows `data?:
Record<string, unknown> | null`, enabling callers to explicitly clear stored `data` by
sending `null`.

2. The workflow input `UpdateShippingOptionsWorkflowInput` in
`packages/types/src/workflow/fulfillment/update-shipping-options.ts:4-32` narrows this to
`data?: Record<string, unknown>`, excluding `null` at the type level.

3. The admin HTTP route `POST /admin/shipping-options/:id` in
`packages/medusa/src/api-v2/admin/shipping-options/[id]/route.ts:13-24` consumes a body of
type `AdminUpdateShippingOptionType`, validated by `AdminUpdateShippingOption` in
`packages/medusa/src/api-v2/admin/shipping-options/validators.ts:89-99`, where `data` is
defined as `z.record(z.unknown()).optional()`—this schema rejects `data: null`.

4. When a client attempts to clear the `data` field by sending a request body containing
`"id": "<existing-id>"` and `"data": null` to `/admin/shipping-options/:id`, the validator
in step 3 fails, the `updateShippingOptionsWorkflow`
(`packages/core-flows/src/fulfillment/workflows/update-shipping-options.ts:9-21`) is never
executed, and there is no way through this workflow/API to perform a valid "clear data"
update that the underlying service contract otherwise supports.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/types/src/workflow/fulfillment/update-shipping-options.ts
**Line:** 9:9
**Comment:**
	*Api Mismatch: The new workflow input narrows `data` to `Record<string, unknown>` and drops `null`, while the fulfillment update DTO allows `data` to be `null` for clearing stored data. This type mismatch breaks API consistency and prevents valid "clear data" updates through the workflow type.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

const normalizedInput: UpdateShippingOptionsInput[] = []

if (isString(idOrSelector)) {
normalizedInput.push({ id: idOrSelector, ...data })
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The object spread order lets data.id override idOrSelector, so the selected id can be silently replaced and a different record gets updated. Ensure the selector id is authoritative by preventing payload id from overriding it. [incorrect variable usage]

Severity Level: Major ⚠️
- ❌ Wrong shipping option updated when ids disagree.
- ⚠️ Admin edits can silently target unintended shipping option.
- ⚠️ Troubleshooting misapplied updates becomes significantly harder.
Steps of Reproduction ✅
1. In
`packages/fulfillment/integration-tests/__tests__/fulfillment-module-service/shipping-option.spec.ts:596-639`,
copy the pattern that creates a shipping option and the `updateData:
UpdateShippingOptionDTO` object used for `service.updateShippingOptions`.

2. Extend the test to create two shipping options `optionA` and `optionB` using the
existing creation helper, so you have two valid ids (see same spec file around lines 7-15
for how options are created).

3. Call `service.updateShippingOptions(optionA.id, { ...updateData, id: optionB.id })`
(following the call pattern at `shipping-option.spec.ts:620-622`), so the method receives
an `id` argument for `optionA` and a conflicting `data.id` for `optionB`.

4. Inside `FulfillmentModuleService.updateShippingOptions` at
`packages/fulfillment/src/services/fulfillment-module-service.ts:50-96`, the string
overload branch executes and builds `normalizedInput` using `normalizedInput.push({ id:
idOrSelector, ...data })` (line 1071), so `data.id` (optionB.id) overwrites `idOrSelector`
(optionA.id); `updateShippingOptions_` then uses the overridden id and updates `optionB`
instead of `optionA`.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/fulfillment/src/services/fulfillment-module-service.ts
**Line:** 1071:1071
**Comment:**
	*Incorrect Variable Usage: The object spread order lets `data.id` override `idOrSelector`, so the selected id can be silently replaced and a different record gets updated. Ensure the selector id is authoritative by preventing payload `id` from overriding it.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +17 to +24
const shippingOptionPayload = req.validatedBody

const workflow = updateShippingOptionsWorkflow(req.scope)

const { result, errors } = await workflow.run({
input: [shippingOptionPayload],
throwOnError: false,
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: This endpoint ignores req.params.id and forwards the body payload as-is to the update workflow, so a client can call /admin/shipping-options/:id and update a different shipping option by sending another id in the body. Build the workflow input from the route param and either reject mismatches or overwrite the body id with req.params.id. [api mismatch]

Severity Level: Critical 🚨
-`/admin/shipping-options/:id` can update a different option.
- ⚠️ Path identifier is ignored, breaking RESTful expectations.
- ⚠️ Admin tooling may display confusing, inconsistent updates.
Steps of Reproduction ✅
1. Create at least two shipping options via `POST /admin/shipping-options` handled by
`packages/medusa/src/api-v2/admin/shipping-options/route.ts:13-43`, and note their ids
`so1` and `so2`.

2. From an authenticated admin client, send `POST /admin/shipping-options/so1` with a JSON
body validated by `AdminUpdateShippingOption` (see `validators.ts:89-98`), setting `"id":
"so2"` and some updated fields (for example `"name": "Updated"`).

3. The middleware pipeline in
`packages/medusa/src/api-v2/admin/shipping-options/middlewares.ts:32-41` runs
`validateAndTransformBody(AdminUpdateShippingOption)`, populating `req.validatedBody` with
the body `id` (`so2`) but never comparing it to `req.params.id` (`so1`).

4. The handler in `packages/medusa/src/api-v2/admin/shipping-options/[id]/route.ts:13-24`
assigns `shippingOptionPayload = req.validatedBody` and calls
`updateShippingOptionsWorkflow(req.scope).run({ input: [shippingOptionPayload] })`, so the
workflow input id is `so2` and the route param `:id` is ignored.

5. Inside the workflow, `updateShippingOptionsWorkflow`
(`packages/core-flows/src/fulfillment/workflows/update-shipping-options.ts:11-34`)
delegates to `upsertShippingOptionsStep` (`steps/upsert-shipping-options.ts:20-63`), which
uses the payload `id` field only; the remote query in `[id]/route.ts:30-41` then fetches
and returns the shipping option with id `so2`, demonstrating that the request to
`/admin/shipping-options/so1` actually updated and returned `so2`.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/medusa/src/api-v2/admin/shipping-options/[id]/route.ts
**Line:** 17:24
**Comment:**
	*Api Mismatch: This endpoint ignores `req.params.id` and forwards the body payload as-is to the update workflow, so a client can call `/admin/shipping-options/:id` and update a different shipping option by sending another `id` in the body. Build the workflow input from the route param and either reject mismatches or overwrite the body `id` with `req.params.id`.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +89 to +98
export const AdminUpdateShippingOption = z
.object({
id: z.string(),
name: z.string().optional(),
data: z.record(z.unknown()).optional(),
price_type: z.nativeEnum(ShippingOptionPriceTypeEnum).optional(),
provider_id: z.string().optional(),
type: AdminCreateShippingOptionTypeObject.optional(),
})
.strict()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Requiring id in the update body for a POST /admin/shipping-options/:id route creates a contract mismatch and causes valid path-based update requests to fail validation unless they duplicate the id in the body. Make the body schema path-oriented (no required body id) and rely on the route param as the source of truth. [api mismatch]

Severity Level: Critical 🚨
-`/admin/shipping-options/:id` rejects standard update bodies.
- ⚠️ Clients must duplicate ids in body and path.
- ⚠️ API contract deviates from other Medusa admin endpoints.
Steps of Reproduction ✅
1. From an authenticated admin client, call `POST /admin/shipping-options/:id` with a JSON
body containing update fields such as `{ \"name\": \"Updated option\" }` but without an
`id` property.

2. The middleware configuration in
`packages/medusa/src/api-v2/admin/shipping-options/middlewares.ts:32-41` applies
`validateAndTransformBody(AdminUpdateShippingOption)` for this route, using the schema
defined in `validators.ts:89-98`.

3. `AdminUpdateShippingOption` requires `id: z.string()` in the body and is `.strict()`,
so the validator rejects the request as invalid because the body lacks `id`, even though
the correct identifier is already supplied in `:id` in the path.

4. As a result, the request never reaches the handler in `[id]/route.ts:13-43`, and
legitimate path-based update requests that only rely on the URL identifier fail
validation, forcing clients to redundantly duplicate the id in the body.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/medusa/src/api-v2/admin/shipping-options/validators.ts
**Line:** 89:98
**Comment:**
	*Api Mismatch: Requiring `id` in the update body for a `POST /admin/shipping-options/:id` route creates a contract mismatch and causes valid path-based update requests to fail validation unless they duplicate the id in the body. Make the body schema path-oriented (no required body id) and rely on the route param as the source of truth.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Jun 6, 2026

CodeAnt AI finished running the review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants