Skip to content

Conversation

@naman03malhotra
Copy link
Contributor

@naman03malhotra naman03malhotra commented Jul 24, 2025

Overview

This PR implements comprehensive Product Data Mapping functionality for the WooCommerce CLI Migrator's Shopify platform support. It transforms raw Shopify GraphQL product data into standardized format for WooCommerce importing.

Changes

New Files

  • src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php - Complete implementation
  • Updated tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php

Key Features Implemented

Core Product Mapping

  • Basic Fields: name, slug, description, status, dates, visibility
  • Enhanced Fields: publication status, product classification, SEO preservation
  • Simple Products: pricing (sale detection), inventory, weight conversion, SKU
  • Variable Products: attributes, variations with complete variant data
  • Media: images with featured detection, alt text, gallery ordering
  • Taxonomies: categories (collections), tags, brands (vendor)
  • Metafields: custom data + SEO fields preservation

Advanced Features

  • Sale Price Detection: compareAtPrice > price logic
  • Weight Unit Conversion: GRAMS/KILOGRAMS/POUNDS/OUNCES → store unit
  • Publication Tracking: publishedAt, availableForSale status
  • Product Classification: gift cards, subscriptions, product types
  • SEO Preservation: Yoast-compatible metafields
  • Selective Processing: Constructor field filtering

Testing

Since we'd need a controller to test it, there are no manual testing steps for the mapper yet.

Unit Tests

# Run tests locally
pnpm run test:php:env tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php

Integration Status

The ShopifyMapper is ready for integration but not yet connected to CLI commands:

  • Infrastructure Ready: PlatformRegistry::get_mapper() can instantiate the mapper
  • Platform Registered: ShopifyPlatform registers mapper class correctly
  • CLI Integration Pending: ProductsCommand doesn't use mapper yet (planned for the next PRs)

Current CLI behavior (shows raw Shopify data only):

wp wc migrate products --fetch --limit=1 --platform=shopify
# Output: Raw product titles, IDs, status - no mapping applied

@github-actions github-actions bot added the plugin: woocommerce Issues related to the WooCommerce Core plugin. label Jul 24, 2025
@naman03malhotra naman03malhotra changed the title Init Implement Product Data Mapping for WooCommerce CLI Migrator Jul 24, 2025
@naman03malhotra naman03malhotra requested a review from Copilot July 24, 2025 11:24
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements comprehensive Product Data Mapping functionality for the WooCommerce CLI Migrator's Shopify platform support. It transforms raw Shopify GraphQL product data into standardized format for WooCommerce importing, replacing the previous stub implementation with full field mapping, pricing logic, variant handling, and taxonomy support.

  • Complete transformation from Shopify GraphQL product nodes to WooCommerce-compatible arrays
  • Support for both simple and variable products with comprehensive variant mapping
  • Enhanced field processing including SEO, inventory, weight conversion, and image handling

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
ShopifyMapper.php Core implementation replacing stub with comprehensive product data mapping logic
ShopifyMapperTest.php Extensive test suite covering all mapping scenarios and edge cases
ShopifyPlatform.php Minor whitespace addition
CredentialManager.php Minor whitespace addition
SetupCommand.php Minor whitespace addition
ResetCommand.php Minor whitespace addition
Comments suppressed due to low confidence (1)

plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php:491

  • The selective field processing test only verifies that excluded fields are not present in the result, but doesn't test that the fields_to_process array actually controls which processing logic is executed. Consider adding assertions that verify the mapper skips expensive operations when fields are not requested.
	public function test_selective_field_processing() {

@github-actions
Copy link
Contributor

github-actions bot commented Jul 24, 2025

Testing Guidelines

Hi @roccotripaldi ,

Apart from reviewing the code changes, please make sure to review the testing instructions (Guide) and verify that relevant tests (E2E, Unit, Integration, etc.) have been added or updated as needed.

Reminder: PR reviewers are required to document testing performed. This includes:

  • 🖼️ Screenshots or screen recordings.
  • 📝 List of functionality tested / steps followed.
  • 🌐 Site details (environment attributes such as hosting type, plugins, theme, store size, store age, and relevant settings).
  • 🔍 Any analysis performed, such as assessing potential impacts on environment attributes and other plugins, conducting performance profiling, or using LLM/AI-based analysis.

⚠️ Within the testing details you provide, please ensure that no sensitive information (such as API keys, passwords, user data, etc.) is included in this public issue.

@naman03malhotra naman03malhotra marked this pull request as ready for review July 24, 2025 11:43
@github-actions
Copy link
Contributor

github-actions bot commented Jul 24, 2025

Test using WordPress Playground

The changes in this pull request can be previewed and tested using a WordPress Playground instance.
WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Test this pull request with WordPress Playground.

Note that this URL is valid for 30 days from when this comment was last updated. You can update it by closing/reopening the PR or pushing a new commit.

Base automatically changed from migrator/shopify-product-fetch to trunk August 4, 2025 09:59
@naman03malhotra naman03malhotra force-pushed the migrator/shopify-woo-mapper branch from 1fe8d0e to e67712b Compare August 4, 2025 10:07
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 4, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Implemented ShopifyMapper to convert Shopify GraphQL product objects into WooCommerce-compatible arrays and added extensive tests and a changelog entry. The mapper includes field selection, unit conversion, metadata, images, variable product/variation handling, SEO, and inventory mapping.

Changes

Cohort / File(s) Change Summary
ShopifyMapper Implementation
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
Replaced stub with full implementation mapping Shopify GraphQL product objects to a WooCommerce importable array. Added constructor supporting selective fields and many private helpers for variable-product detection, status mapping, enhanced status, classification, SEO, categories/tags extraction, weight conversion, description sanitization, images, metafields, simple/variable product data, and default field list. Handles Shopify-specific shapes (media nodes, variant pricing, camelCase keys, stock/oversell logic).
ShopifyMapper Test Expansion
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
Expanded tests from a stub to a comprehensive suite using realistic Shopify fixtures. Added tests for basic fields, status mappings, catalog visibility, enhanced status, classification, pricing, inventory, weight conversion, variable detection, attributes, variations, categories, tags, brand, images, SEO, metafields, selective field processing, and malformed-data handling. Added helpers to create simple and variable Shopify product fixtures.
Changelog Entry
plugins/woocommerce/changelog/migrator-shopify-woo-mapper
Added a changelog entry documenting the new Shopify→WooCommerce product mapping feature (minor addition).

Sequence Diagram(s)

sequenceDiagram
    participant ShopifyAPI as Shopify GraphQL API
    participant ShopifyMapper as ShopifyMapper
    participant WooImporter as WooCommerce Importer

    ShopifyAPI->>ShopifyMapper: Provide Shopify product object
    ShopifyMapper->>ShopifyMapper: Map basic fields, SEO, metafields, images
    ShopifyMapper->>ShopifyMapper: If variable -> map attributes and variations
    ShopifyMapper->>ShopifyMapper: Convert units, sanitize descriptions, filter fields
    ShopifyMapper->>WooImporter: Return WooCommerce-compatible product array
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch migrator/shopify-woo-mapper

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

♻️ Duplicate comments (5)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (5)

116-117: Use numeric comparison for prices

String comparison of prices can lead to incorrect results (e.g., '9.99' > '10.00' evaluates to true).

-				if ( $variant_node->compareAtPrice && $variant_node->compareAtPrice > $variant_node->price ) {
+				if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) {

132-132: Add null check for inventoryItem

The code verifies that 'inventoryItem' property exists but doesn't check if it's null before accessing its 'tracked' property.

-				$manage_stock              = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem->tracked;
+				$manage_stock              = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem && $variant_node->inventoryItem->tracked;

227-227: Use numeric comparison for prices (duplicate logic)

Same string comparison issue as line 116. Consider extracting this into a helper method to avoid duplication.

-					if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) {
+					if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) {

Wait, I see the cast is already there. Let me re-examine...


243-243: Add null check for inventoryItem

Same null pointer issue as line 132. The code doesn't verify if inventoryItem is null.

-					$manage_stock                     = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem->tracked;
+					$manage_stock                     = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem && $variant_node->inventoryItem->tracked;

460-468: Define arrays as class constants for better performance

The unit mapping and conversion factors arrays are recreated on every method call, which impacts performance during bulk imports.

Add these as class constants:

+	/**
+	 * Unit mapping from Shopify to internal format.
+	 */
+	private const UNIT_MAP = array(
+		'GRAMS'     => 'g',
+		'KILOGRAMS' => 'kg',
+		'POUNDS'    => 'lb',
+		'OUNCES'    => 'oz',
+	);
+
+	/**
+	 * Weight conversion factors.
+	 */
+	private const CONVERSION_FACTORS = array(
+		'kg' => array(
+			'kg' => 1,
+			'g'  => 1000,
+			'lb' => 2.20462,
+			'oz' => 35.274,
+		),
+		'g'  => array(
+			'kg' => 0.001,
+			'g'  => 1,
+			'lb' => 0.00220462,
+			'oz' => 0.035274,
+		),
+		'lb' => array(
+			'kg' => 0.453592,
+			'g'  => 453.592,
+			'lb' => 1,
+			'oz' => 16,
+		),
+		'oz' => array(
+			'kg' => 0.0283495,
+			'g'  => 28.3495,
+			'lb' => 0.0625,
+			'oz' => 1,
+		),
+	);

 	private function get_converted_weight( $weight, $weight_unit ): ?float {
 		if ( null === $weight || null === $weight_unit || (float) $weight <= 0 ) {
 			return null;
 		}

-		$unit_map = array(
-			'GRAMS'     => 'g',
-			'KILOGRAMS' => 'kg',
-			'POUNDS'    => 'lb',
-			'OUNCES'    => 'oz',
-		);
-
-		$shopify_unit_key = $unit_map[ $weight_unit ] ?? null;
+		$shopify_unit_key = self::UNIT_MAP[ $weight_unit ] ?? null;

And update the conversion logic:

-		$conversion_factors = array(
-			'kg' => array(
-				'kg' => 1,
-				'g'  => 1000,
-				'lb' => 2.20462,
-				'oz' => 35.274,
-			),
-			'g'  => array(
-				'kg' => 0.001,
-				'g'  => 1,
-				'lb' => 0.00220462,
-				'oz' => 0.035274,
-			),
-			'lb' => array(
-				'kg' => 0.453592,
-				'g'  => 453.592,
-				'lb' => 1,
-				'oz' => 16,
-			),
-			'oz' => array(
-				'kg' => 0.0283495,
-				'g'  => 28.3495,
-				'lb' => 0.0625,
-				'oz' => 1,
-			),
-		);
-
-		if ( ! isset( $conversion_factors[ $shopify_unit_key ][ $store_weight_unit ] ) ) {
+		if ( ! isset( self::CONVERSION_FACTORS[ $shopify_unit_key ][ $store_weight_unit ] ) ) {
 			return (float) $weight;
 		}

-		return (float) $weight * $conversion_factors[ $shopify_unit_key ][ $store_weight_unit ];
+		return (float) $weight * self::CONVERSION_FACTORS[ $shopify_unit_key ][ $store_weight_unit ];

Also applies to: 490-521

🧹 Nitpick comments (1)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (1)

537-563: Consider extracting test data builders

The helper methods create_simple_shopify_product and create_variable_shopify_product create complex test objects. Consider using a builder pattern or factory to make test data creation more flexible and maintainable.

Example:

class ShopifyProductBuilder {
    private $product;
    
    public function __construct() {
        $this->product = (object) [
            'id' => 'gid://shopify/Product/123456789',
            'status' => 'ACTIVE',
            // ... defaults
        ];
    }
    
    public function withTitle(string $title): self {
        $this->product->title = $title;
        return $this;
    }
    
    public function withVariants(array $variants): self {
        $this->product->variants = (object) ['edges' => $variants];
        return $this;
    }
    
    public function build(): object {
        return $this->product;
    }
}

Also applies to: 570-639

📜 Review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2085079 and e67712b.

📒 Files selected for processing (2)
  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (1 hunks)
  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{php,js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/code-quality.mdc)

**/*.{php,js,jsx,ts,tsx}: Guard against unexpected inputs
Sanitize and validate any potentially dangerous inputs
Ensure code is backwards compatible
Write code that is readable and intuitive
Ensure code has unit or E2E tests where applicable

Files:

  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
**/*.{php,js,ts,jsx,tsx}

⚙️ CodeRabbit Configuration File

**/*.{php,js,ts,jsx,tsx}: Don't trust that extension developers will follow the best practices, make sure the code:

  • Guards against unexpected inputs.
  • Sanitizes and validates any potentially dangerous inputs.
  • Is backwards compatible.
  • Is readable and intuitive.
  • Has unit or E2E tests where applicable.

Files:

  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
🧠 Learnings (3)
📓 Common learnings
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.
Learnt from: gigitux
PR: woocommerce/woocommerce#59659
File: plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts:136-147
Timestamp: 2025-07-22T09:30:43.528Z
Learning: In WooCommerce blocks TypeScript code, avoid type assertions (`as`) when accessing properties from unified stores. Instead, create proper union types that combine all the different store component types using `Partial<>` wrappers, and export the unified type to help third-party extenders. For example: `type UnifiedStore = BaseStore & Partial<StoreA> & Partial<StoreB>`.
📚 Learning: in the woocommerce fulfillments system, only the ups shipping provider is currently fully implemente...
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce blocks, when porting existing code patterns that have known issues (like parseint tru...
Learnt from: samueljseay
PR: woocommerce/woocommerce#58716
File: plugins/woocommerce/client/blocks/assets/js/blocks/mini-cart/iapi-frontend.ts:83-101
Timestamp: 2025-06-17T07:07:53.443Z
Learning: In WooCommerce blocks, when porting existing code patterns that have known issues (like parseInt truncating decimal money values), maintain consistency with existing implementation rather than making isolated fixes. The preference is for systematic refactoring approaches (like broader Dinero adoption) over piecemeal changes.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php

Comment on lines +530 to +532
private function sanitize_product_description( string $html ): string {
return trim( $html );
}
Copy link
Contributor

@coderabbitai coderabbitai bot Aug 4, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Method name is misleading - no actual HTML sanitization performed

The method name sanitize_product_description suggests it sanitizes HTML for security, but it only trims whitespace. This could lead to XSS vulnerabilities if untrusted HTML is passed through.

Either rename the method to trim_product_description or implement proper HTML sanitization:

 	private function sanitize_product_description( string $html ): string {
-		return trim( $html );
+		// Use WordPress built-in sanitization
+		return wp_kses_post( trim( $html ) );
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private function sanitize_product_description( string $html ): string {
return trim( $html );
}
private function sanitize_product_description( string $html ): string {
// Use WordPress built-in sanitization
return wp_kses_post( trim( $html ) );
}
🤖 Prompt for AI Agents
In
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
at lines 530 to 532, the method sanitize_product_description only trims
whitespace but its name suggests it sanitizes HTML for security. To fix this,
either rename the method to trim_product_description to reflect its actual
behavior or implement proper HTML sanitization to prevent XSS vulnerabilities
when handling untrusted HTML input.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This this is an admin level actions this is not needed for now.

Copy link
Contributor

Choose a reason for hiding this comment

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

@naman03malhotra, understood that HTML sanitization isn't needed for this admin-level migration context.

As a minor improvement for code clarity, you could consider renaming the method to trim_product_description() to better reflect what it actually does, but that's entirely optional.

🐰✨


🧠 Learnings used
Learnt from: senadir
PR: woocommerce/woocommerce#59426
File: plugins/woocommerce/client/legacy/js/frontend/a8c-address-autocomplete-service.js:355-355
Timestamp: 2025-07-08T09:14:47.894Z
Learning: In the WooCommerce address autocomplete system, input sanitization happens upstream in address-autocomplete.js before data reaches a8c-address-autocomplete-service.js. The trim() operation in the service file is for optimization purposes (to skip API requests when only spaces are added), not for security sanitization.

Learnt from: CR
PR: woocommerce/woocommerce#0
File: .cursor/rules/code-quality.mdc:0-0
Timestamp: 2025-07-18T14:54:36.232Z
Learning: Applies to **/*.{php,js,jsx,ts,tsx} : Sanitize and validate any potentially dangerous inputs

Learnt from: mreishus
PR: woocommerce/woocommerce#58817
File: plugins/woocommerce/includes/wc-product-functions.php:140-140
Timestamp: 2025-06-13T23:52:46.221Z
Learning: In WooCommerce's wc_delete_product_transients function, the context check includes both $is_admin_page (for regular admin screens) and $is_privileged_ajax (for AJAX requests from users with edit_products capability), ensuring that legitimate admin AJAX operations like product imports/exports can still delete transients.

Learnt from: nerrad
PR: woocommerce/woocommerce#60176
File: plugins/woocommerce/client/legacy/css/admin.scss:9088-9105
Timestamp: 2025-08-04T00:21:51.440Z
Learning: WooCommerce is currently in the midst of an admin redesign project, so temporary/short-term CSS solutions for admin pages are preferred over long-term refactoring when addressing immediate needs.

Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.

@naman03malhotra naman03malhotra self-assigned this Aug 4, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (8)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (7)

420-420: Use numeric comparison for prices

When comparing prices as strings, '9.99' > '10.00' evaluates to true, which is incorrect. Cast both values to float before comparison.

-				if ( $variant_node->compareAtPrice && $variant_node->compareAtPrice > $variant_node->price ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.
+				if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.

436-436: Add null check for inventoryItem property

The code checks if 'inventoryItem' property exists but doesn't verify if it's not null before accessing its 'tracked' property.

-				$manage_stock                  = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem->tracked; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.
+				$manage_stock                  = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem && $variant_node->inventoryItem->tracked; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.

260-268: Define unit mapping as class constant

The weight unit mapping array should be defined as a class constant rather than recreating it on every method call.

+	/**
+	 * Weight unit mapping from Shopify to WooCommerce.
+	 */
+	private const UNIT_MAP = array(
+		'GRAMS'     => 'g',
+		'KILOGRAMS' => 'kg',
+		'POUNDS'    => 'lb',
+		'OUNCES'    => 'oz',
+	);
+
 	private function get_converted_weight( $weight, $weight_unit ): ?float {
 		if ( null === $weight || null === $weight_unit || (float) $weight <= 0 ) {
 			return null;
 		}
 
-		$unit_map = array(
-			'GRAMS'     => 'g',
-			'KILOGRAMS' => 'kg',
-			'POUNDS'    => 'lb',
-			'OUNCES'    => 'oz',
-		);
-
-		$shopify_unit_key = $unit_map[ $weight_unit ] ?? null;
+		$shopify_unit_key = self::UNIT_MAP[ $weight_unit ] ?? null;

290-321: Define conversion factors as class constant

The weight conversion factors array should be defined as a class constant for better performance.

+	/**
+	 * Weight conversion factors between units.
+	 */
+	private const CONVERSION_FACTORS = array(
+		'kg' => array(
+			'kg' => 1,
+			'g'  => 1000,
+			'lb' => 2.20462,
+			'oz' => 35.274,
+		),
+		'g'  => array(
+			'kg' => 0.001,
+			'g'  => 1,
+			'lb' => 0.00220462,
+			'oz' => 0.035274,
+		),
+		'lb' => array(
+			'kg' => 0.453592,
+			'g'  => 453.592,
+			'lb' => 1,
+			'oz' => 16,
+		),
+		'oz' => array(
+			'kg' => 0.0283495,
+			'g'  => 28.3495,
+			'lb' => 0.0625,
+			'oz' => 1,
+		),
+	);
+
 		// Fallback manual conversion.
-		$conversion_factors = array(
-			'kg' => array(
-				'kg' => 1,
-				'g'  => 1000,
-				'lb' => 2.20462,
-				'oz' => 35.274,
-			),
-			'g'  => array(
-				'kg' => 0.001,
-				'g'  => 1,
-				'lb' => 0.00220462,
-				'oz' => 0.035274,
-			),
-			'lb' => array(
-				'kg' => 0.453592,
-				'g'  => 453.592,
-				'lb' => 1,
-				'oz' => 16,
-			),
-			'oz' => array(
-				'kg' => 0.0283495,
-				'g'  => 28.3495,
-				'lb' => 0.0625,
-				'oz' => 1,
-			),
-		);
-
-		if ( ! isset( $conversion_factors[ $shopify_unit_key ][ $store_weight_unit ] ) ) {
+		if ( ! isset( self::CONVERSION_FACTORS[ $shopify_unit_key ][ $store_weight_unit ] ) ) {
 			return (float) $weight;
 		}
 
-		return (float) $weight * $conversion_factors[ $shopify_unit_key ][ $store_weight_unit ];
+		return (float) $weight * self::CONVERSION_FACTORS[ $shopify_unit_key ][ $store_weight_unit ];

330-332: Method name is misleading - implement proper HTML sanitization

The method name sanitize_product_description suggests it sanitizes HTML for security, but it only trims whitespace. This could lead to XSS vulnerabilities.

 	private function sanitize_product_description( string $html ): string {
-		return trim( $html );
+		// Use WordPress built-in sanitization
+		return wp_kses_post( trim( $html ) );
 	}

509-509: Use numeric comparison for variant prices

Same issue as line 420 - string comparison of prices can lead to incorrect results.

-					if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.
+					if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.

525-525: Add null check for variant inventoryItem

Same null pointer issue as line 436.

-					$manage_stock                     = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem->tracked; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.
+					$manage_stock                     = property_exists( $variant_node, 'inventoryItem' ) && is_object( $variant_node->inventoryItem ) && $variant_node->inventoryItem->tracked; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (1)

130-133: Fix property names to match Shopify GraphQL schema

Multiple properties use snake_case but should use camelCase to match Shopify's GraphQL API.

-		$shopify_product->product_type          = 'Apparel';
+		$shopify_product->productType          = 'Apparel';
 		$shopify_product->category              = (object) array( 'name' => 'Shirts' );
-		$shopify_product->is_gift_card          = false;
-		$shopify_product->requires_selling_plan = true;
+		$shopify_product->isGiftCard          = false;
+		$shopify_product->requiresSellingPlan = true;
📜 Review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e67712b and 20c2154.

📒 Files selected for processing (2)
  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (1 hunks)
  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{php,js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/code-quality.mdc)

**/*.{php,js,jsx,ts,tsx}: Guard against unexpected inputs
Sanitize and validate any potentially dangerous inputs
Ensure code is backwards compatible
Write code that is readable and intuitive
Ensure code has unit or E2E tests where applicable

Files:

  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
**/*.{php,js,ts,jsx,tsx}

⚙️ CodeRabbit Configuration File

**/*.{php,js,ts,jsx,tsx}: Don't trust that extension developers will follow the best practices, make sure the code:

  • Guards against unexpected inputs.
  • Sanitizes and validates any potentially dangerous inputs.
  • Is backwards compatible.
  • Is readable and intuitive.
  • Has unit or E2E tests where applicable.

Files:

  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
🧠 Learnings (7)
📓 Common learnings
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.
Learnt from: gigitux
PR: woocommerce/woocommerce#59659
File: plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts:136-147
Timestamp: 2025-07-22T09:30:43.528Z
Learning: In WooCommerce blocks TypeScript code, avoid type assertions (`as`) when accessing properties from unified stores. Instead, create proper union types that combine all the different store component types using `Partial<>` wrappers, and export the unified type to help third-party extenders. For example: `type UnifiedStore = BaseStore & Partial<StoreA> & Partial<StoreB>`.
📚 Learning: the displaystyleswitcher component in plugins/woocommerce/client/blocks/assets/js/blocks/product-fil...
Learnt from: dinhtungdu
PR: woocommerce/woocommerce#59900
File: plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/inspector.tsx:0-0
Timestamp: 2025-07-24T05:37:00.907Z
Learning: The DisplayStyleSwitcher component in plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/components/display-style-switcher/index.tsx has been updated so that its onChange prop accepts only a string type (not string | number | undefined), eliminating the need for type assertions when using this component.

Applied to files:

  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
📚 Learning: in the woocommerce fulfillments system, only the ups shipping provider is currently fully implemente...
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce blocks, when porting existing code patterns that have known issues (like parseint tru...
Learnt from: samueljseay
PR: woocommerce/woocommerce#58716
File: plugins/woocommerce/client/blocks/assets/js/blocks/mini-cart/iapi-frontend.ts:83-101
Timestamp: 2025-06-17T07:07:53.443Z
Learning: In WooCommerce blocks, when porting existing code patterns that have known issues (like parseInt truncating decimal money values), maintain consistency with existing implementation rather than making isolated fixes. The preference is for systematic refactoring approaches (like broader Dinero adoption) over piecemeal changes.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in the woocommerce address autocomplete system, input sanitization happens upstream in address-autoc...
Learnt from: senadir
PR: woocommerce/woocommerce#59426
File: plugins/woocommerce/client/legacy/js/frontend/a8c-address-autocomplete-service.js:355-355
Timestamp: 2025-07-08T09:14:47.894Z
Learning: In the WooCommerce address autocomplete system, input sanitization happens upstream in address-autocomplete.js before data reaches a8c-address-autocomplete-service.js. The trim() operation in the service file is for optimization purposes (to skip API requests when only spaces are added), not for security sanitization.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: applies to **/*.{php,js,jsx,ts,tsx} : sanitize and validate any potentially dangerous inputs...
Learnt from: CR
PR: woocommerce/woocommerce#0
File: .cursor/rules/code-quality.mdc:0-0
Timestamp: 2025-07-18T14:54:36.232Z
Learning: Applies to **/*.{php,js,jsx,ts,tsx} : Sanitize and validate any potentially dangerous inputs

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce projects, formatting suggestions should respect the project's prettier configuration ...
Learnt from: lysyjan
PR: woocommerce/woocommerce#59632
File: packages/js/email-editor/src/layouts/flex-email.tsx:116-122
Timestamp: 2025-07-14T10:41:46.200Z
Learning: In WooCommerce projects, formatting suggestions should respect the project's Prettier configuration and linting rules. Changes that would break the lint job should be avoided, even if they appear to improve readability.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
🧬 Code Graph Analysis (1)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (1)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (3)
  • init (50-52)
  • map_product_data (60-83)
  • ShopifyMapper (26-659)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: Metrics - @woocommerce/plugin-woocommerce [performance]
  • GitHub Check: Core e2e tests 4/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 1/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 2/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 6/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 5/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 3/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core API tests - @woocommerce/plugin-woocommerce [api]
  • GitHub Check: PHP: 7.4 WP: latest - 1 [WP 6.7.2] 2/2 - @woocommerce/plugin-woocommerce [unit:php]
  • GitHub Check: PHP: 8.4 WP: latest [WP latest] 1/2 - @woocommerce/plugin-woocommerce [unit:php]
  • GitHub Check: PHP: 8.4 WP: latest [WP latest] 2/2 - @woocommerce/plugin-woocommerce [unit:php]
  • GitHub Check: PHP: 7.4 WP: latest - 1 [WP 6.7.2] 1/2 - @woocommerce/plugin-woocommerce [unit:php]
  • GitHub Check: Lint - @woocommerce/plugin-woocommerce
  • GitHub Check: build
🔇 Additional comments (3)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (2)

60-83: Well-structured mapping implementation!

The refactored map_product_data method now has excellent separation of concerns with dedicated helper methods for each mapping aspect. This makes the code more maintainable and testable.


584-607: Clean image mapping implementation!

The image mapping logic properly validates objects and handles featured image detection correctly.

plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (1)

18-640: Excellent test coverage!

The test suite provides comprehensive coverage of all mapping scenarios including:

  • Basic field mapping
  • Status and visibility mappings
  • Product classification
  • Simple and variable product handling
  • Categories, tags, and brand mapping
  • Image and SEO field handling
  • Metafields processing
  • Selective field processing
  • Error handling with malformed data

The tests are well-structured with clear assertions and good use of helper methods.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (4)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (4)

341-343: Method name is misleading - no actual HTML sanitization performed

The method name sanitize_product_description suggests it sanitizes HTML for security, but it only trims whitespace. This could lead to XSS vulnerabilities if untrusted HTML is passed through.

Either rename the method to trim_product_description or implement proper HTML sanitization using wp_kses_post().


431-437: Use numeric comparison for price values

The comparison should use explicit numeric casting rather than relying on PHP's type juggling. When comparing prices, explicit float casting ensures accurate comparison.

-			if ( $variant_node->compareAtPrice && $variant_node->compareAtPrice > $variant_node->price ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.
+			if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.

447-453: Potential null pointer exception when accessing inventoryItem->tracked

The code checks if 'inventoryItem' property exists but doesn't verify if it's not null before accessing its 'tracked' property.

-				$manage_stock                  = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem->tracked; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.
+				$manage_stock                  = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem && $variant_node->inventoryItem->tracked; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.

536-542: Potential null pointer exception when accessing inventoryItem->tracked

Same issue as in the simple product section - the code doesn't verify if inventoryItem is not null before accessing its tracked property.

-					$manage_stock                     = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem->tracked; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.
+					$manage_stock                     = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem && $variant_node->inventoryItem->tracked; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL uses camelCase.
🧹 Nitpick comments (1)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (1)

423-484: Consider extracting duplicate variant mapping logic

The logic for mapping variant data (price, SKU, stock, weight) is duplicated between mapSimpleProductData and mapVariableProductData. Consider extracting this into a private helper method like mapVariantData($variant_node) to reduce duplication and ensure consistent behavior.

Example refactoring:

private function mapVariantData( $variant_node ): array {
    $variant_data = array();
    
    // Price logic
    if ( $this->should_process( 'price' ) ) {
        if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) {
            $variant_data['regular_price'] = $variant_node->compareAtPrice;
            $variant_data['sale_price'] = $variant_node->price;
        } else {
            $variant_data['regular_price'] = $variant_node->price;
            $variant_data['sale_price'] = null;
        }
    }
    
    // SKU, stock, weight logic...
    
    return $variant_data;
}

Also applies to: 493-587

📜 Review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 20c2154 and f5bab42.

📒 Files selected for processing (2)
  • plugins/woocommerce/changelog/migrator-shopify-woo-mapper (1 hunks)
  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • plugins/woocommerce/changelog/migrator-shopify-woo-mapper
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{php,js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/code-quality.mdc)

**/*.{php,js,jsx,ts,tsx}: Guard against unexpected inputs
Sanitize and validate any potentially dangerous inputs
Ensure code is backwards compatible
Write code that is readable and intuitive
Ensure code has unit or E2E tests where applicable

Files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
**/*.{php,js,ts,jsx,tsx}

⚙️ CodeRabbit Configuration File

**/*.{php,js,ts,jsx,tsx}: Don't trust that extension developers will follow the best practices, make sure the code:

  • Guards against unexpected inputs.
  • Sanitizes and validates any potentially dangerous inputs.
  • Is backwards compatible.
  • Is readable and intuitive.
  • Has unit or E2E tests where applicable.

Files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
🧠 Learnings (6)
📓 Common learnings
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.
Learnt from: gigitux
PR: woocommerce/woocommerce#59659
File: plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts:136-147
Timestamp: 2025-07-22T09:30:43.528Z
Learning: In WooCommerce blocks TypeScript code, avoid type assertions (`as`) when accessing properties from unified stores. Instead, create proper union types that combine all the different store component types using `Partial<>` wrappers, and export the unified type to help third-party extenders. For example: `type UnifiedStore = BaseStore & Partial<StoreA> & Partial<StoreB>`.
📚 Learning: in the woocommerce fulfillments system, only the ups shipping provider is currently fully implemente...
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce blocks, when porting existing code patterns that have known issues (like parseint tru...
Learnt from: samueljseay
PR: woocommerce/woocommerce#58716
File: plugins/woocommerce/client/blocks/assets/js/blocks/mini-cart/iapi-frontend.ts:83-101
Timestamp: 2025-06-17T07:07:53.443Z
Learning: In WooCommerce blocks, when porting existing code patterns that have known issues (like parseInt truncating decimal money values), maintain consistency with existing implementation rather than making isolated fixes. The preference is for systematic refactoring approaches (like broader Dinero adoption) over piecemeal changes.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in the woocommerce address autocomplete system, input sanitization happens upstream in address-autoc...
Learnt from: senadir
PR: woocommerce/woocommerce#59426
File: plugins/woocommerce/client/legacy/js/frontend/a8c-address-autocomplete-service.js:355-355
Timestamp: 2025-07-08T09:14:47.894Z
Learning: In the WooCommerce address autocomplete system, input sanitization happens upstream in address-autocomplete.js before data reaches a8c-address-autocomplete-service.js. The trim() operation in the service file is for optimization purposes (to skip API requests when only spaces are added), not for security sanitization.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: applies to **/*.{php,js,jsx,ts,tsx} : sanitize and validate any potentially dangerous inputs...
Learnt from: CR
PR: woocommerce/woocommerce#0
File: .cursor/rules/code-quality.mdc:0-0
Timestamp: 2025-07-18T14:54:36.232Z
Learning: Applies to **/*.{php,js,jsx,ts,tsx} : Sanitize and validate any potentially dangerous inputs

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce projects, formatting suggestions should respect the project's prettier configuration ...
Learnt from: lysyjan
PR: woocommerce/woocommerce#59632
File: packages/js/email-editor/src/layouts/flex-email.tsx:116-122
Timestamp: 2025-07-14T10:41:46.200Z
Learning: In WooCommerce projects, formatting suggestions should respect the project's Prettier configuration and linting rules. Changes that would break the lint job should be avoided, even if they appear to improve readability.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
🔇 Additional comments (1)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (1)

17-98: Well-structured implementation with comprehensive mapping coverage

The ShopifyMapper class implementation is comprehensive and well-organized. The refactoring into smaller, focused methods improves maintainability and testability. Good practices observed:

  • Proper use of class constants for weight conversions
  • Defensive programming with null checks and property existence validation
  • Backward compatibility handling for both camelCase and snake_case properties
  • Clear separation of concerns with dedicated mapping methods

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (7)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (5)

421-421: Fix string comparison for prices

String comparison of prices can produce incorrect results (e.g., '9.99' > '10.00' evaluates to true).


510-510: Fix string comparison for prices in variations

String comparison of prices can produce incorrect results.


437-437: Add null check for inventoryItem before accessing tracked property

The code checks if inventoryItem exists but doesn't verify it's not null.


526-526: Add null check for inventoryItem in variations

The code checks if inventoryItem exists but doesn't verify it's not null.


331-333: Method name is misleading - implement proper HTML sanitization

The method name suggests HTML sanitization but only trims whitespace, which could lead to XSS vulnerabilities.

plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (2)

129-132: Fix property names to match Shopify GraphQL schema

Test data should use camelCase to match actual Shopify API responses.


508-528: Add test for HTML sanitization

Should verify that HTML content is properly sanitized to prevent XSS attacks.

🧹 Nitpick comments (2)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (1)

172-213: Refactor duplicated property checking logic

The pattern of checking both camelCase and snake_case properties is repeated. Extract this into a helper method to reduce duplication.

Add a helper method:

private function get_property_value( object $obj, string $camel_case_name, string $snake_case_name ) {
    if ( property_exists( $obj, $camel_case_name ) && $obj->$camel_case_name !== null ) {
        return $obj->$camel_case_name;
    }
    if ( property_exists( $obj, $snake_case_name ) && $obj->$snake_case_name !== null ) {
        return $obj->$snake_case_name;
    }
    return null;
}

Then simplify the classification method:

-    $product_type = null;
-    if ( property_exists( $shopify_product, 'productType' ) && $shopify_product->productType ) {
-        $product_type = $shopify_product->productType;
-    } elseif ( property_exists( $shopify_product, 'product_type' ) && $shopify_product->product_type ) {
-        $product_type = $shopify_product->product_type;
-    }
+    $product_type = $this->get_property_value( $shopify_product, 'productType', 'product_type' );
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (1)

18-637: Consider adding security-focused test cases

While the test coverage is comprehensive, consider adding tests for:

  1. SQL injection attempts in product names/descriptions
  2. Script injection in metafields
  3. Path traversal attempts in image URLs

Example test to add:

public function test_security_validation() {
    $shopify_product = $this->create_simple_shopify_product();
    $shopify_product->title = "'; DROP TABLE products; --";
    $shopify_product->descriptionHtml = '<script>alert("XSS")</script><p>Safe</p>';
    
    $result = $this->mapper->map_product_data( $shopify_product );
    
    // Verify dangerous input is properly handled
    $this->assertStringNotContainsString( 'DROP TABLE', $result['name'] );
    $this->assertStringNotContainsString( '<script>', $result['description'] );
}
📜 Review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f5bab42 and d2bac77.

📒 Files selected for processing (2)
  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (1 hunks)
  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{php,js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/code-quality.mdc)

**/*.{php,js,jsx,ts,tsx}: Guard against unexpected inputs
Sanitize and validate any potentially dangerous inputs
Ensure code is backwards compatible
Write code that is readable and intuitive
Ensure code has unit or E2E tests where applicable

Files:

  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
**/*.{php,js,ts,jsx,tsx}

⚙️ CodeRabbit Configuration File

**/*.{php,js,ts,jsx,tsx}: Don't trust that extension developers will follow the best practices, make sure the code:

  • Guards against unexpected inputs.
  • Sanitizes and validates any potentially dangerous inputs.
  • Is backwards compatible.
  • Is readable and intuitive.
  • Has unit or E2E tests where applicable.

Files:

  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
🧠 Learnings (7)
📓 Common learnings
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.
Learnt from: gigitux
PR: woocommerce/woocommerce#59659
File: plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts:136-147
Timestamp: 2025-07-22T09:30:43.528Z
Learning: In WooCommerce blocks TypeScript code, avoid type assertions (`as`) when accessing properties from unified stores. Instead, create proper union types that combine all the different store component types using `Partial<>` wrappers, and export the unified type to help third-party extenders. For example: `type UnifiedStore = BaseStore & Partial<StoreA> & Partial<StoreB>`.
📚 Learning: the displaystyleswitcher component in plugins/woocommerce/client/blocks/assets/js/blocks/product-fil...
Learnt from: dinhtungdu
PR: woocommerce/woocommerce#59900
File: plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/inspector.tsx:0-0
Timestamp: 2025-07-24T05:37:00.907Z
Learning: The DisplayStyleSwitcher component in plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/components/display-style-switcher/index.tsx has been updated so that its onChange prop accepts only a string type (not string | number | undefined), eliminating the need for type assertions when using this component.

Applied to files:

  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
📚 Learning: applies to **/*.{php,js,jsx,ts,tsx} : sanitize and validate any potentially dangerous inputs...
Learnt from: CR
PR: woocommerce/woocommerce#0
File: .cursor/rules/code-quality.mdc:0-0
Timestamp: 2025-07-18T14:54:36.232Z
Learning: Applies to **/*.{php,js,jsx,ts,tsx} : Sanitize and validate any potentially dangerous inputs

Applied to files:

  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in the woocommerce fulfillments system, only the ups shipping provider is currently fully implemente...
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce blocks, when porting existing code patterns that have known issues (like parseint tru...
Learnt from: samueljseay
PR: woocommerce/woocommerce#58716
File: plugins/woocommerce/client/blocks/assets/js/blocks/mini-cart/iapi-frontend.ts:83-101
Timestamp: 2025-06-17T07:07:53.443Z
Learning: In WooCommerce blocks, when porting existing code patterns that have known issues (like parseInt truncating decimal money values), maintain consistency with existing implementation rather than making isolated fixes. The preference is for systematic refactoring approaches (like broader Dinero adoption) over piecemeal changes.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in the woocommerce address autocomplete system, input sanitization happens upstream in address-autoc...
Learnt from: senadir
PR: woocommerce/woocommerce#59426
File: plugins/woocommerce/client/legacy/js/frontend/a8c-address-autocomplete-service.js:355-355
Timestamp: 2025-07-08T09:14:47.894Z
Learning: In the WooCommerce address autocomplete system, input sanitization happens upstream in address-autocomplete.js before data reaches a8c-address-autocomplete-service.js. The trim() operation in the service file is for optimization purposes (to skip API requests when only spaces are added), not for security sanitization.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce projects, formatting suggestions should respect the project's prettier configuration ...
Learnt from: lysyjan
PR: woocommerce/woocommerce#59632
File: packages/js/email-editor/src/layouts/flex-email.tsx:116-122
Timestamp: 2025-07-14T10:41:46.200Z
Learning: In WooCommerce projects, formatting suggestions should respect the project's Prettier configuration and linting rules. Changes that would break the lint job should be avoided, even if they appear to improve readability.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
🔇 Additional comments (3)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (2)

290-323: Well-implemented weight conversion logic

The weight conversion method properly handles null values, uses WooCommerce's built-in conversion when available, and has a solid fallback implementation using class constants.


585-608: Well-structured image mapping with proper validation

The image mapping properly validates media nodes, handles featured images correctly, and safely handles optional alt text.

plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (1)

535-561: Well-structured test helper with realistic data

The helper method creates properly formatted Shopify product objects with correct camelCase properties and realistic data structure.

Copy link
Contributor

@roccotripaldi roccotripaldi left a comment

Choose a reason for hiding this comment

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

Great work! This is a robust mapping class with great unit test coverage.

I ran the unit tests locally, and they all passed.

Feel free to ignore my suggestions about code comments. I really just want to highlight that well written code doesn't need a lot of commentary.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (5)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (5)

95-117: Add input validation for the Shopify product object

The method accepts an object parameter but doesn't validate its structure before accessing properties. This could cause runtime errors when accessing required properties.

Add validation at the start of the method:

 public function map_product_data( object $shopify_product ): array {
+    // Validate required fields
+    if ( ! property_exists( $shopify_product, 'id' ) || 
+         ! property_exists( $shopify_product, 'title' ) || 
+         ! property_exists( $shopify_product, 'handle' ) ) {
+        throw new \InvalidArgumentException( 'Invalid Shopify product object: missing required fields' );
+    }
+
     $is_variable = $this->is_variable_product( $shopify_product );

418-424: Fix price comparison logic

The price comparison should use numeric comparison rather than string comparison. String comparison can produce incorrect results (e.g., '9.99' > '10.00' evaluates to true).

-				if ( $variant_node->compareAtPrice && $variant_node->compareAtPrice > $variant_node->price ) {
+				if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) {

433-433: Add null check for inventoryItem access

Potential null pointer exception when accessing $variant_node->inventoryItem->tracked. The code checks if the property exists but doesn't verify if it's not null.

-				$manage_stock                  = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem->tracked;
+				$manage_stock                  = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem && $variant_node->inventoryItem->tracked;

505-511: Fix price comparison logic in variations

Same string comparison issue as in simple product mapping. Use numeric comparison for accurate price comparison.

-					if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) {
+					if ( $variant_node->compareAtPrice && (float) $variant_node->compareAtPrice > (float) $variant_node->price ) {

520-520: Add null check for inventoryItem access in variations

Same null pointer issue as in simple product mapping. Add proper null checking.

-					$manage_stock                     = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem->tracked;
+					$manage_stock                     = property_exists( $variant_node, 'inventoryItem' ) && $variant_node->inventoryItem && $variant_node->inventoryItem->tracked;
🧹 Nitpick comments (1)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (1)

330-345: Consider renaming method for clarity

While the admin-level context makes HTML sanitization unnecessary as discussed in previous reviews, consider renaming sanitize_product_description to trim_product_description to better reflect its actual behavior.

The should_process method is well-implemented for selective field filtering.

📜 Review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d2bac77 and fb5e976.

📒 Files selected for processing (1)
  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{php,js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/code-quality.mdc)

**/*.{php,js,jsx,ts,tsx}: Guard against unexpected inputs
Sanitize and validate any potentially dangerous inputs
Ensure code is backwards compatible
Write code that is readable and intuitive
Ensure code has unit or E2E tests where applicable

Files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
**/*.{php,js,ts,jsx,tsx}

⚙️ CodeRabbit Configuration File

**/*.{php,js,ts,jsx,tsx}: Don't trust that extension developers will follow the best practices, make sure the code:

  • Guards against unexpected inputs.
  • Sanitizes and validates any potentially dangerous inputs.
  • Is backwards compatible.
  • Is readable and intuitive.
  • Has unit or E2E tests where applicable.

Files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
🧠 Learnings (12)
📓 Common learnings
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.
Learnt from: gigitux
PR: woocommerce/woocommerce#59659
File: plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts:136-147
Timestamp: 2025-07-22T09:30:43.528Z
Learning: In WooCommerce blocks TypeScript code, avoid type assertions (`as`) when accessing properties from unified stores. Instead, create proper union types that combine all the different store component types using `Partial<>` wrappers, and export the unified type to help third-party extenders. For example: `type UnifiedStore = BaseStore & Partial<StoreA> & Partial<StoreB>`.
📚 Learning: in the woocommerce fulfillments system, only the ups shipping provider is currently fully implemente...
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce blocks, when porting existing code patterns that have known issues (like parseint tru...
Learnt from: samueljseay
PR: woocommerce/woocommerce#58716
File: plugins/woocommerce/client/blocks/assets/js/blocks/mini-cart/iapi-frontend.ts:83-101
Timestamp: 2025-06-17T07:07:53.443Z
Learning: In WooCommerce blocks, when porting existing code patterns that have known issues (like parseInt truncating decimal money values), maintain consistency with existing implementation rather than making isolated fixes. The preference is for systematic refactoring approaches (like broader Dinero adoption) over piecemeal changes.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in the woocommerce address autocomplete system, input sanitization happens upstream in address-autoc...
Learnt from: senadir
PR: woocommerce/woocommerce#59426
File: plugins/woocommerce/client/legacy/js/frontend/a8c-address-autocomplete-service.js:355-355
Timestamp: 2025-07-08T09:14:47.894Z
Learning: In the WooCommerce address autocomplete system, input sanitization happens upstream in address-autocomplete.js before data reaches a8c-address-autocomplete-service.js. The trim() operation in the service file is for optimization purposes (to skip API requests when only spaces are added), not for security sanitization.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: applies to **/*.{php,js,jsx,ts,tsx} : sanitize and validate any potentially dangerous inputs...
Learnt from: CR
PR: woocommerce/woocommerce#0
File: .cursor/rules/code-quality.mdc:0-0
Timestamp: 2025-07-18T14:54:36.232Z
Learning: Applies to **/*.{php,js,jsx,ts,tsx} : Sanitize and validate any potentially dangerous inputs

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce projects, formatting suggestions should respect the project's prettier configuration ...
Learnt from: lysyjan
PR: woocommerce/woocommerce#59632
File: packages/js/email-editor/src/layouts/flex-email.tsx:116-122
Timestamp: 2025-07-14T10:41:46.200Z
Learning: In WooCommerce projects, formatting suggestions should respect the project's Prettier configuration and linting rules. Changes that would break the lint job should be avoided, even if they appear to improve readability.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: woocommerce is currently in the midst of an admin redesign project, so temporary/short-term css solu...
Learnt from: nerrad
PR: woocommerce/woocommerce#60176
File: plugins/woocommerce/client/legacy/css/admin.scss:9088-9105
Timestamp: 2025-08-04T00:21:51.440Z
Learning: WooCommerce is currently in the midst of an admin redesign project, so temporary/short-term CSS solutions for admin pages are preferred over long-term refactoring when addressing immediate needs.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce's wc_delete_product_transients function, the context check includes both $is_admin_pa...
Learnt from: mreishus
PR: woocommerce/woocommerce#58817
File: plugins/woocommerce/includes/wc-product-functions.php:140-140
Timestamp: 2025-06-13T23:52:46.221Z
Learning: In WooCommerce's wc_delete_product_transients function, the context check includes both $is_admin_page (for regular admin screens) and $is_privileged_ajax (for AJAX requests from users with edit_products capability), ensuring that legitimate admin AJAX operations like product imports/exports can still delete transients.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: woocommerce legacy javascript files in plugins/woocommerce/client/legacy/js/ must use older javascri...
Learnt from: opr
PR: woocommerce/woocommerce#0
File: :0-0
Timestamp: 2025-06-20T17:38:16.565Z
Learning: WooCommerce legacy JavaScript files in plugins/woocommerce/client/legacy/js/ must use older JavaScript syntax and cannot use modern features like optional chaining (?.) due to browser compatibility requirements. Explicit null checking with && operators should be used instead.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in plugins/woocommerce/src/internal/productfilters/taxonomyhierarchydata.php, the get_descendants() ...
Learnt from: dinhtungdu
PR: woocommerce/woocommerce#60142
File: plugins/woocommerce/src/Internal/ProductFilters/TaxonomyHierarchyData.php:71-93
Timestamp: 2025-08-01T05:52:57.768Z
Learning: In plugins/woocommerce/src/Internal/ProductFilters/TaxonomyHierarchyData.php, the get_descendants() and get_ancestors() methods don't need extensive input validation because: 1) the taxonomy parameter is always validated upstream in the controlled WooCommerce core context, and 2) the null coalescing operator (??) already efficiently handles invalid term_id keys in the returned map.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in woocommerce codebase, when table names are hardcoded (like `$wpdb->prefix . 'wc_order_coupon_look...
Learnt from: NeosinneR
PR: woocommerce/woocommerce#60129
File: plugins/woocommerce/includes/wc-coupon-functions.php:145-154
Timestamp: 2025-07-31T11:18:02.478Z
Learning: In WooCommerce codebase, when table names are hardcoded (like `$wpdb->prefix . 'wc_order_coupon_lookup'`), the preference is to maintain consistency with existing patterns throughout the codebase rather than making isolated security improvements when the risk is minimal (since $wpdb->prefix is controlled by WordPress and table names are hardcoded constants).

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
📚 Learning: in the woocommerce productfilters queryclauses class, the $chosen_taxonomies parameter in add_taxono...
Learnt from: dinhtungdu
PR: woocommerce/woocommerce#59499
File: plugins/woocommerce/src/Internal/ProductFilters/QueryClauses.php:327-332
Timestamp: 2025-07-10T04:22:27.648Z
Learning: In the WooCommerce ProductFilters QueryClauses class, the $chosen_taxonomies parameter in add_taxonomy_clauses() already contains only validated public product taxonomies. The validation occurs upstream in the Params class during parameter registration, so additional taxonomy existence validation in the processing methods is redundant.

Applied to files:

  • plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: Metrics - @woocommerce/plugin-woocommerce [performance]
  • GitHub Check: Core e2e tests 1/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core API tests - @woocommerce/plugin-woocommerce [api]
  • GitHub Check: Core e2e tests 3/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 4/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 5/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 6/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 2/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: PHP: 7.4 WP: latest - 1 [WP 6.7.3] 1/2 - @woocommerce/plugin-woocommerce [unit:php]
  • GitHub Check: PHP: 8.4 WP: latest [WP latest] 2/2 - @woocommerce/plugin-woocommerce [unit:php]
  • GitHub Check: PHP: 7.4 WP: latest - 1 [WP 6.7.3] 2/2 - @woocommerce/plugin-woocommerce [unit:php]
  • GitHub Check: PHP: 8.4 WP: latest [WP latest] 1/2 - @woocommerce/plugin-woocommerce [unit:php]
  • GitHub Check: Lint - @woocommerce/plugin-woocommerce
  • GitHub Check: build
🔇 Additional comments (7)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (7)

33-71: Excellent implementation of weight conversion constants!

The weight unit mapping and conversion factors are well-structured and address the previous review comment about extracting these as class constants. The conversion factors are mathematically accurate and comprehensive.


125-163: Well-implemented helper methods with proper validation

The product detection and status mapping methods are well-structured with appropriate property_exists checks and null handling. The logic for determining variable products and status conversion is correct.


171-233: Excellent compatibility handling for GraphQL variations

The dual property checking for both camelCase and snake_case variants shows excellent attention to GraphQL API variations. The SEO mapping includes proper nested object validation.


241-280: Robust category and tag mapping with proper edge case handling

The methods include appropriate checks for empty/missing data and properly handle Shopify's collection-to-category and tag mapping. The use of trim() and empty checks prevents processing invalid data.


289-322: Excellent weight conversion with proper fallbacks

The method handles edge cases well (null inputs, zero weights) and intelligently uses WooCommerce's wc_get_weight() function when available, falling back to manual conversion. The handling of different weight unit formats (lbs vs lb) is a nice touch.


354-403: Well-structured basic field mapping

The method effectively delegates to helper methods and includes proper validation with property_exists checks. The mapping logic covers all essential product fields with appropriate fallbacks.


579-653: Well-implemented image mapping and utility methods

The image mapping method properly handles featured media detection and includes comprehensive validation. The metafield mapping and default fields methods are well-structured and provide good coverage.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (1)

157-224: Strong pricing and inventory test coverage

Good validation of pricing logic including sale detection via compareAtPrice, inventory management, and stock policies. The weight conversion test validates basic functionality but could be enhanced.

Consider adding more comprehensive weight conversion tests:

/**
 * Test weight conversion from different units.
 */
public function test_comprehensive_weight_conversion() {
    // Test conversion from different Shopify units
    $units_to_test = array(
        'GRAMS' => 1000.0,
        'KILOGRAMS' => 1.0, 
        'POUNDS' => 2.20462,
        'OUNCES' => 35.274,
    );
    
    foreach ( $units_to_test as $unit => $value ) {
        $shopify_product = $this->create_simple_shopify_product();
        $shopify_product->variants->edges[0]->node->inventoryItem = (object) array(
            'measurement' => (object) array(
                'weight' => (object) array(
                    'value' => $value,
                    'unit'  => $unit,
                ),
            ),
        );
        
        $result = $this->mapper->map_product_data( $shopify_product );
        $this->assertIsFloat( $result['weight'] );
        $this->assertGreaterThan( 0, $result['weight'] );
    }
}
📜 Review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fb5e976 and 63f20e4.

📒 Files selected for processing (1)
  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{php,js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/code-quality.mdc)

**/*.{php,js,jsx,ts,tsx}: Guard against unexpected inputs
Sanitize and validate any potentially dangerous inputs
Ensure code is backwards compatible
Write code that is readable and intuitive
Ensure code has unit or E2E tests where applicable

Files:

  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
**/*.{php,js,ts,jsx,tsx}

⚙️ CodeRabbit Configuration File

**/*.{php,js,ts,jsx,tsx}: Don't trust that extension developers will follow the best practices, make sure the code:

  • Guards against unexpected inputs.
  • Sanitizes and validates any potentially dangerous inputs.
  • Is backwards compatible.
  • Is readable and intuitive.
  • Has unit or E2E tests where applicable.

Files:

  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
🧠 Learnings (3)
📓 Common learnings
Learnt from: tpaksu
PR: woocommerce/woocommerce#59172
File: plugins/woocommerce/src/Internal/Fulfillments/Providers/MPLShippingProvider.php:14-21
Timestamp: 2025-06-26T10:47:27.212Z
Learning: In the WooCommerce fulfillments system, only the UPS shipping provider is currently fully implemented. All other shipping provider classes in plugins/woocommerce/src/Internal/Fulfillments/Providers/ are placeholders for future implementations, so empty shipping country arrays and stub methods in these classes are intentional and should not be flagged as issues.
Learnt from: gigitux
PR: woocommerce/woocommerce#59659
File: plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/frontend.ts:136-147
Timestamp: 2025-07-22T09:30:43.528Z
Learning: In WooCommerce blocks TypeScript code, avoid type assertions (`as`) when accessing properties from unified stores. Instead, create proper union types that combine all the different store component types using `Partial<>` wrappers, and export the unified type to help third-party extenders. For example: `type UnifiedStore = BaseStore & Partial<StoreA> & Partial<StoreB>`.
📚 Learning: the displaystyleswitcher component in plugins/woocommerce/client/blocks/assets/js/blocks/product-fil...
Learnt from: dinhtungdu
PR: woocommerce/woocommerce#59900
File: plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/inner-blocks/attribute-filter/inspector.tsx:0-0
Timestamp: 2025-07-24T05:37:00.907Z
Learning: The DisplayStyleSwitcher component in plugins/woocommerce/client/blocks/assets/js/blocks/product-filters/components/display-style-switcher/index.tsx has been updated so that its onChange prop accepts only a string type (not string | number | undefined), eliminating the need for type assertions when using this component.

Applied to files:

  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
📚 Learning: applies to **/*.{php,js,jsx,ts,tsx} : sanitize and validate any potentially dangerous inputs...
Learnt from: CR
PR: woocommerce/woocommerce#0
File: .cursor/rules/code-quality.mdc:0-0
Timestamp: 2025-07-18T14:54:36.232Z
Learning: Applies to **/*.{php,js,jsx,ts,tsx} : Sanitize and validate any potentially dangerous inputs

Applied to files:

  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
🔇 Additional comments (7)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (7)

18-40: LGTM! Proper test class structure

The test class follows WooCommerce testing conventions with proper inheritance from WC_Unit_Test_Case, type declarations, and setup methodology.


45-152: Comprehensive basic product mapping tests

Excellent coverage of core product field transformations including status mappings, catalog visibility, and product classification. The tests properly validate the conversion from Shopify GraphQL format to WooCommerce import format.


229-299: Excellent variable product mapping coverage

Comprehensive validation of the complex transformation from Shopify's product options/variants structure to WooCommerce's attributes/variations format. Proper testing of attribute properties like position, visibility, and variation flags.


304-391: Solid taxonomy mapping validation

Good coverage of transforming Shopify collections to categories, tags arrays, and vendor to brand mapping. Proper validation of name and slug generation for all taxonomy types.


396-485: Comprehensive media and SEO testing

Excellent validation of image mapping with featured detection, alt text preservation, and gallery ordering. SEO tests properly validate metafield generation for compatibility with Yoast SEO.


535-637: Well-structured helper methods with realistic test data

The helper methods create comprehensive and realistic Shopify product objects that properly simulate GraphQL API responses. Good use of nested structures and camelCase property naming matching Shopify conventions.

Consider adding a helper method for creating edge case test data:

/**
 * Create a Shopify product with edge case data for validation testing.
 */
private function create_edge_case_shopify_product(): object {
    return (object) array(
        'id' => '', // Empty ID
        'title' => null, // Null title
        'handle' => 'invalid--handle', // Invalid handle format
        'status' => 'UNKNOWN_STATUS', // Invalid status
        'variants' => (object) array('edges' => array()), // No variants
    );
}

1-639: Excellent comprehensive test suite with minor security enhancement opportunity

This is a well-crafted test suite that thoroughly validates the ShopifyMapper functionality. The coverage spans all major features including basic mapping, variable products, taxonomy handling, media processing, and error conditions.

Strengths:

  • Comprehensive coverage of all mapper functionality
  • Realistic test data matching Shopify GraphQL structure
  • Proper WooCommerce testing conventions
  • Good edge case and error handling coverage

The test suite effectively validates the complex transformation from Shopify's GraphQL format to WooCommerce's import format, ensuring data integrity throughout the migration process.

Comment on lines +490 to +528
public function test_selective_field_processing() {
$mapper_limited = new ShopifyMapper( array( 'fields' => array( 'title', 'slug', 'price' ) ) );

$shopify_product = $this->create_simple_shopify_product();
$result = $mapper_limited->map_product_data( $shopify_product );

// Should still have basic fields.
$this->assertEquals( 'Amazing T-Shirt', $result['name'] );
$this->assertEquals( 'amazing-t-shirt', $result['slug'] );
$this->assertEquals( '25.00', $result['regular_price'] );

// Should not have SKU key because 'sku' not in fields.
$this->assertArrayNotHasKey( 'sku', $result );
}

/**
* Test error handling with malformed data.
*/
public function test_error_handling_with_malformed_data() {
// Test with minimal data.
$minimal_product = (object) array(
'id' => 'gid://shopify/Product/123',
'title' => 'Test Product',
'handle' => 'test-product',
'status' => 'ACTIVE',
'createdAt' => '2023-01-01T00:00:00Z',
);

$result = $this->mapper->map_product_data( $minimal_product );

$this->assertIsArray( $result );
$this->assertEquals( 'Test Product', $result['name'] );
$this->assertEquals( 'test-product', $result['slug'] );
$this->assertEquals( 'publish', $result['status'] );
$this->assertFalse( $result['is_variable'] );
$this->assertEquals( array(), $result['categories'] );
$this->assertEquals( array(), $result['tags'] );
$this->assertEquals( array(), $result['images'] );
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Good error handling, but enhance input validation testing

The selective processing and basic error handling tests are solid. However, considering the coding guidelines about guarding against unexpected inputs and sanitization, additional validation tests would strengthen security.

Add comprehensive input validation tests:

/**
 * Test input validation and sanitization.
 */
public function test_input_validation_and_sanitization() {
    // Test with malicious script injection
    $malicious_product = (object) array(
        'id' => 'gid://shopify/Product/123',
        'title' => '<script>alert("xss")</script>Safe Title',
        'descriptionHtml' => '<script>alert("xss")</script><p>Safe content</p>',
        'handle' => 'test-product',
        'status' => 'ACTIVE',
        'createdAt' => '2023-01-01T00:00:00Z',
    );
    
    $result = $this->mapper->map_product_data( $malicious_product );
    
    // Should sanitize script tags
    $this->assertStringNotContainsString( '<script>', $result['name'] );
    $this->assertStringNotContainsString( '<script>', $result['description'] );
    
    // Test with null/undefined values
    $null_product = (object) array(
        'id' => null,
        'title' => null,
        'handle' => null,
        'status' => 'INVALID_STATUS',
    );
    
    $result = $this->mapper->map_product_data( $null_product );
    $this->assertIsArray( $result );
    $this->assertNotNull( $result['name'] ); // Should have fallback
}
🤖 Prompt for AI Agents
In
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
around lines 490 to 528, add a new test method named
test_input_validation_and_sanitization to cover input validation and
sanitization scenarios. This test should create a product object with malicious
script tags in title and descriptionHtml fields and verify that the mapped
result strips out script tags. Also, test with null or invalid values for key
fields and assert that the mapper returns a valid array with fallback values
where appropriate. This will strengthen security by ensuring the mapper properly
sanitizes and handles unexpected or malformed inputs.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (2)

508-528: Enhance malformed data coverage: nulls/invalid enums/incorrect types

Extend the malformed data test to include:

  • Null/empty title/handle with fallbacks.
  • Invalid status (assert safe default).
  • Non-array tags/collections.
  • Missing variants (ensure pricing fields handled gracefully).

Example:

$bad = (object) array(
    'id' => null,
    'title' => null,
    'handle' => null,
    'status' => 'INVALID',
    'tags' => null,
    'collections' => null,
);
$r = $this->mapper->map_product_data( $bad );
$this->assertIsArray( $r );
$this->assertArrayHasKey( 'name', $r );
$this->assertContains( $r['status'], array( 'draft', 'publish' ) );
$this->assertEquals( array(), $r['tags'] );
$this->assertEquals( array(), $r['categories'] );

508-528: Add sanitization tests for HTML/script content in title/description

Per security guidelines, add tests ensuring script tags are stripped from descriptionHtml (and possibly title) while preserving safe content. This was raised earlier as well.

Example:

public function test_html_sanitization() {
    $p = $this->create_simple_shopify_product();
    $p->title           = '<script>alert(1)</script>Safe Title';
    $p->descriptionHtml = '<script>alert("XSS")</script><p>Safe</p>';

    $r = $this->mapper->map_product_data( $p );

    $this->assertStringNotContainsString( '<script', $r['name'] );
    $this->assertStringNotContainsString( '<script', $r['description'] );
    $this->assertStringContainsString( 'Safe', $r['description'] );
}
🧹 Nitpick comments (10)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (10)

45-50: Basic smoke test is appropriate

Verifies the method returns an array. Consider also asserting a couple of required keys (e.g., name, status) to make this smoke test a bit more meaningful.


128-133: Add a camelCase variant for classification fields

You’re testing snake_case compatibility here (product_type, is_gift_card, requires_selling_plan). Add a camelCase variant test (productType, isGiftCard, requiresSellingPlan) to lock in dual-compat behavior.

Example addition:

public function test_product_classification_mapping_camel_case() {
    $p = $this->create_simple_shopify_product();
    $p->productType          = 'Apparel';
    $p->category             = (object) array( 'name' => 'Shirts' );
    $p->isGiftCard           = false;
    $p->requiresSellingPlan  = true;

    $r = $this->mapper->map_product_data( $p );

    $this->assertEquals( array( 'name' => 'Apparel', 'slug' => 'apparel' ), $r['product_type'] );
    $this->assertEquals( array( 'name' => 'Shirts', 'slug' => 'shirts' ), $r['standard_category'] );
    $this->assertFalse( $r['is_gift_card'] );
    $this->assertTrue( $r['requires_subscription'] );
}

157-172: Sale price logic covered; consider a non-sale guard

You verify compareAtPrice > price results in sale. Add a guard case (compareAtPrice <= price) to assert sale_price remains null and regular_price equals price.

Example:

$same_price = $this->create_simple_shopify_product();
$same_price->variants->edges[0]->node->compareAtPrice = '25.00';
$r = $this->mapper->map_product_data( $same_price );
$this->assertEquals( '25.00', $r['regular_price'] );
$this->assertNull( $r['sale_price'] );

205-224: Weight conversion test is minimal; add unit variety and invalids

Consider adding cases for KILOGRAMS, POUNDS, OUNCES and an invalid unit. This helps ensure robust conversion and graceful fallback.

Example snippet:

// KG
$kg = $this->create_simple_shopify_product();
$kg->variants->edges[0]->node->inventoryItem = (object) array(
    'tracked' => true,
    'measurement' => (object) array( 'weight' => (object) array( 'value' => 1.5, 'unit' => 'KILOGRAMS' ) ),
);
$r = $this->mapper->map_product_data( $kg );
$this->assertIsFloat( $r['weight'] );

// Invalid unit
$bad = $this->create_simple_shopify_product();
$bad->variants->edges[0]->node->inventoryItem = (object) array(
    'tracked' => true,
    'measurement' => (object) array( 'weight' => (object) array( 'value' => 10, 'unit' => 'STONES' ) ),
);
$r = $this->mapper->map_product_data( $bad );
$this->assertTrue( ! isset( $r['weight'] ) || null === $r['weight'] );

266-299: Variation mapping is good; extend with stock/sale/weight assertions

Add checks for per-variation manage_stock/stock_status, and sale logic (variant compareAtPrice > price). If weight can be variant-specific, assert it as well.

Example:

$vp = $this->create_variable_shopify_product();
$vp->variants->edges[0]->node->compareAtPrice = '22.00';
$vp->variants->edges[0]->node->inventoryQuantity = 0;
$vp->variants->edges[0]->node->inventoryItem = (object) array(
    'tracked' => true,
    'measurement' => (object) array( 'weight' => (object) array( 'value' => 500, 'unit' => 'GRAMS' ) ),
);
$r = $this->mapper->map_product_data( $vp );
$v1 = $r['variations'][0];
$this->assertEquals( '22.00', $v1['regular_price'] );
$this->assertEquals( '20.00', $v1['sale_price'] );
$this->assertEquals( 'outofstock', $v1['stock_status'] );
$this->assertIsFloat( $v1['weight'] );

345-373: Tags mapping verified; add deduplication/normalization case

Consider adding a case with duplicate/mixed-case tags (e.g., 'Summer', 'summer') to ensure deduplication and consistent slug normalization.


396-439: Images mapping is strong; add fallbacks and reordering scenarios

Add tests for:

  • No featuredMedia: first media should be featured.
  • Missing altText: fall back to product title or empty string.
  • Non-image media nodes are skipped gracefully.

Example:

$p = $this->create_simple_shopify_product();
unset( $p->featuredMedia );
$p->media = (object) array(
    'edges' => array(
        (object) array( 'node' => (object) array(
            'id' => 'gid://shopify/MediaImage/1',
            'image' => (object) array( 'url' => 'https://example.com/1.jpg', 'altText' => null ),
        ) ),
        (object) array( 'node' => (object) array(
            'id' => 'gid://shopify/MediaImage/2',
            'image' => (object) array( 'url' => 'https://example.com/2.jpg', 'altText' => 'Second' ),
        ) ),
    ),
);
$r = $this->mapper->map_product_data( $p );
$this->assertTrue( $r['images'][0]['is_featured'] );
$this->assertNotEmpty( $r['images'][0]['alt'] ); // or equals product title if that’s the policy

460-485: Metafields mapping looks correct; test non-string and JSON values

Add cases where metafield values are numbers, booleans, or JSON strings to verify normalization/serialization rules.

Example:

$p = $this->create_simple_shopify_product();
$p->metafields = (object) array( 'edges' => array(
    (object) array( 'node' => (object) array( 'namespace' => 'specs', 'key' => 'rating', 'value' => 4.5 ) ),
    (object) array( 'node' => (object) array( 'namespace' => 'data', 'key' => 'json', 'value' => '{"a":1}' ) ),
) );
$r = $this->mapper->map_product_data( $p );
$this->assertArrayHasKey( 'specs_rating', $r['metafields'] );
$this->assertArrayHasKey( 'data_json', $r['metafields'] );

490-503: Strengthen selective fields behavior assertions

You assert sku is omitted. Also assert non-requested groups are omitted (e.g., images, tags, categories, variations for simple products).

Suggested additions:

 $this->assertEquals( '25.00', $result['regular_price'] );
 
 // Should not have SKU key because 'sku' not in fields.
 $this->assertArrayNotHasKey( 'sku', $result );
+// Should not include other non-requested keys.
+$this->assertArrayNotHasKey( 'images', $result );
+$this->assertArrayNotHasKey( 'categories', $result );
+$this->assertArrayNotHasKey( 'tags', $result );
+$this->assertArrayNotHasKey( 'metafields', $result );
+$this->assertArrayNotHasKey( 'variations', $result );

569-637: Helpers are clear and comprehensive

Fixtures are well structured for simple and variable products. Consider adding an optional parameter to toggle common toggles (e.g., add weight/inventory/seo/media) to reduce duplication across tests.

📜 Review details

Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63f20e4 and 12d56a0.

📒 Files selected for processing (1)
  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{php,js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/code-quality.mdc)

**/*.{php,js,jsx,ts,tsx}: Guard against unexpected inputs
Sanitize and validate any potentially dangerous inputs
Ensure code is backwards compatible
Write code that is readable and intuitive
Ensure code has unit or E2E tests where applicable

Files:

  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
**/*.{php,js,ts,jsx,tsx}

⚙️ CodeRabbit Configuration File

**/*.{php,js,ts,jsx,tsx}: Don't trust that extension developers will follow the best practices, make sure the code:

  • Guards against unexpected inputs.
  • Sanitizes and validates any potentially dangerous inputs.
  • Is backwards compatible.
  • Is readable and intuitive.
  • Has unit or E2E tests where applicable.

Files:

  • plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php
🧬 Code Graph Analysis (1)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (2)
plugins/woocommerce/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapper.php (2)
  • map_product_data (95-117)
  • ShopifyMapper (26-654)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Mocks/MockPlatformMapper.php (1)
  • map_product_data (24-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: Metrics - @woocommerce/plugin-woocommerce [performance]
  • GitHub Check: Core e2e tests 5/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core API tests - @woocommerce/plugin-woocommerce [api]
  • GitHub Check: PHP: 7.4 WP: latest - 1 [WP 6.7.3] 1/2 - @woocommerce/plugin-woocommerce [unit:php]
  • GitHub Check: Core e2e tests 6/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 2/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: PHP: 8.4 WP: latest [WP latest] 1/2 - @woocommerce/plugin-woocommerce [unit:php]
  • GitHub Check: Core e2e tests 4/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: PHP: 7.4 WP: latest - 1 [WP 6.7.3] 2/2 - @woocommerce/plugin-woocommerce [unit:php]
  • GitHub Check: Core e2e tests 1/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: Core e2e tests 3/6 - @woocommerce/plugin-woocommerce [e2e]
  • GitHub Check: PHP: 8.4 WP: latest [WP latest] 2/2 - @woocommerce/plugin-woocommerce [unit:php]
  • GitHub Check: Lint - @woocommerce/plugin-woocommerce
  • GitHub Check: build
🔇 Additional comments (9)
plugins/woocommerce/tests/php/src/Internal/CLI/Migrator/Platforms/Shopify/ShopifyMapperTest.php (9)

55-67: Good coverage of core fields for simple products

Assertions for name, slug, description, status, ids, dates, and is_variable look solid.


95-109: Catalog visibility mapping looks correct

Visibility and original_url behavior based on onlineStoreUrl presence is well captured.


114-122: Enhanced status mapping assertions are good

date_published_gmt and available_for_sale are exercised and aligned with expected keys.


229-237: Variable product detection and presence of attributes/variations covered

Good positive detection and counts.


242-261: Attributes mapping looks correct

Validates names, options, positions, visibility, and variation flags for multiple attributes.


304-340: Collections-to-categories mapping verified

Ordering and slug/name mapping look good.


378-391: Brand mapping from vendor verified

Slug generation and name are correct.


444-455: SEO fields mapping aligned with Yoast-compatible keys

Good coverage for title/description mapping into metafields.


177-200: Remove backorders/oversell assertion – not exposed by the mapper

The ShopifyMapper only uses the internal $allow_oversell flag to derive stock_status and does not populate a backorders or oversell key in its output array. There’s nothing to assert beyond stock_status for CONTINUE policy.

• ShopifyMapper.php (lines 436–437) show $allow_oversell is used only in computing stock_status
• No simple_data['backorders'], ['oversell'] or similar field is ever set
• Current tests correctly verify behavior via stock_status; no additional assertion is needed

Likely an incorrect or invalid review comment.

@naman03malhotra naman03malhotra merged commit a9b1b77 into trunk Aug 11, 2025
26 checks passed
@naman03malhotra naman03malhotra deleted the migrator/shopify-woo-mapper branch August 11, 2025 10:12
@github-actions github-actions bot added this to the 10.2.0 milestone Aug 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

plugin: woocommerce Issues related to the WooCommerce Core plugin.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants