Skip to content

Conversation

@michelle0927
Copy link
Collaborator

@michelle0927 michelle0927 commented Aug 26, 2025

Resolves #18052

Summary by CodeRabbit

  • New Features

    • Added Akeneo actions: Get Product, Search Products, Get Draft, Submit Product For Approval, and Delete Product.
    • Added options to include attribute options, asset share links, quality scores, and completeness in responses.
    • Added locale and attribute selectors; product search supports filters, pagination, and measurement conversion.
  • Refactor

    • Product Identifier is now required in the Akeneo app.
    • Create Product Media File action now accepts an optional Product ID.
  • Bug Fixes

    • Validation enforces scope when converting measurements in product search.
  • Chores

    • Bumped Akeneo component version to 0.2.0 (action manifest minor updates).

@vercel
Copy link

vercel bot commented Aug 26, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
pipedream-docs Ignored Ignored Aug 26, 2025 9:49pm
pipedream-docs-redirect-do-not-edit Ignored Ignored Aug 26, 2025 9:49pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 26, 2025

Walkthrough

Adds multiple Akeneo actions (get, search, delete, get draft, submit for approval), extends the Akeneo app with new props and API methods (getProduct, getLocales, getProductDraft, deleteProduct, submitDraft), updates an existing media-file action prop and bumps component package version.

Changes

Cohort / File(s) Summary
Akeneo app: props & methods
components/akeneo/akeneo.app.mjs
Adds propDefinitions (locale, attribute, withAttributeOptions, withAssetShareLinks, withQualityScores, withCompletenesses), removes optional: true from app productId prop, and implements methods: getProduct, getLocales, getProductDraft, deleteProduct, submitDraft.
Get product actions
components/akeneo/actions/get-product/get-product.mjs, components/akeneo/actions/get-draft/get-draft.mjs
New actions that call akeneo.getProduct and akeneo.getProductDraft, export run summaries and return API responses.
Product mutation actions
components/akeneo/actions/delete-product/delete-product.mjs, components/akeneo/actions/submit-product-for-approval/submit-product-for-approval.mjs
New actions that call akeneo.deleteProduct and akeneo.submitDraft, export success summaries and return responses.
Search/list action
components/akeneo/actions/search-products/search-products.mjs
New search action with many query props, validates convertMeasurements requires scope, maps flags to API params, exports item count summary.
Media-file action tweak
components/akeneo/actions/create-a-new-product-media-file/create-a-new-product-media-file.mjs
Bumped action version 0.1.2 → 0.1.3 and marks productId prop as optional: true at the action level (validation still requires productId or productModelCode).
Package metadata
components/akeneo/package.json
Bumped package version 0.1.2 → 0.2.0.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant User
  participant Action as Get Product Action
  participant App as Akeneo App
  participant API as Akeneo API

  User->>Action: Run(productId, flags)
  Action->>App: getProduct({ productId, params })
  App->>API: GET /products/{productId}?with_* params
  API-->>App: 200 Product
  App-->>Action: Product
  Action-->>User: Summary + Product
Loading
sequenceDiagram
  autonumber
  participant User
  participant Action as Search Products Action
  participant App as Akeneo App
  participant API as Akeneo API
  Note right of Action: Validate convertMeasurements requires scope

  User->>Action: Run(filters, flags)
  Action->>Action: Validate params
  Action->>App: getProducts(params)
  App->>API: GET /products (or POST search) with params
  API-->>App: 200 Collection
  App-->>Action: Response
  Action-->>User: Summary "Found N products" + Response
Loading
sequenceDiagram
  autonumber
  participant User
  participant Action as Delete Product Action
  participant App as Akeneo App
  participant API as Akeneo API

  User->>Action: Run(productId)
  Action->>App: deleteProduct({ productId })
  App->>API: DELETE /products/{productId}
  API-->>App: 204 No Content
  App-->>Action: Response
  Action-->>User: Summary "Successfully deleted product ..."
Loading
sequenceDiagram
  autonumber
  participant User
  participant Action as Submit For Approval Action
  participant App as Akeneo App
  participant API as Akeneo API

  User->>Action: Run(productId)
  Action->>App: submitDraft({ productId })
  App->>API: POST /products/{productId}/proposal
  API-->>App: 200/202 Accepted
  App-->>Action: Response
  Action-->>User: Summary "Successfully submitted product ... for approval"
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
List products (by identifier) — GET /products (#18052)
Get product by identifier — GET /products/{code} (#18052)
Delete product — DELETE /products/{code} (#18052)
Submit product for approval & Get product draft (Enterprise UUID endpoints) — POST /products-uuid/{uuid}/proposal; GET /products-uuid/{uuid}/draft (#18052) Changes implement identifier-based endpoints (/products/{id}) rather than the UUID-based enterprise endpoints specified in the issue.

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Media-file action prop made optional — components/akeneo/actions/create-a-new-product-media-file/create-a-new-product-media-file.mjs Media-file action prop adjustment is unrelated to the issue's stated endpoints (list/get/delete/submit/draft) and therefore outside the linked issue objectives.

"A nibble of code, a hop through API trees,
New actions sprout from curious knees.
Fetch, search, delete — I hop and sing,
Drafts and proposals on tiny spring.
This rabbit ships with a joyful zing." 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch issue-18052

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.
    • 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.
  • 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 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/Issue comments)

Type @coderabbitai help to get the list of available commands.

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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/akeneo/akeneo.app.mjs (1)

84-93: Locale Code options likely broken: channel payload uses locales, not locale.

get /channels/{code} returns an array field named locales (plural). Returning resp?.locale || [] will yield []. Replace with resp?.locales || []. (api.akeneo.com)

-        return resp?.locale || [];
+        return resp?.locales || [];
🧹 Nitpick comments (15)
components/akeneo/akeneo.app.mjs (5)

94-113: Add paging to Locale options to avoid truncating results (>10 locales).

The locales endpoint is paginated (default limit 10). Without passing page/limit, infinite scroll in the UI will stop after the first page. Recommend wiring the page argument and PAGE_SIZE. See Akeneo “Get a list of locales” docs (paginated HAL with page/limit). (api-dev.akeneo.com)

-    locale: {
-      type: "string",
-      label: "Locale",
-      description: "A code identifying the locale",
-      async options() {
-        const { _embedded: { items } } = await this.getLocales({
-          params: {
-            search: JSON.stringify({
-              enabled: [
-                {
-                  operator: "=",
-                  value: true,
-                },
-              ],
-            }),
-          },
-        });
-        return items.map((locale) => locale.code);
-      },
-    },
+    locale: {
+      type: "string",
+      label: "Locale",
+      description: "A code identifying the locale",
+      async options({ page }) {
+        page = (page || 0) + 1;
+        const { _embedded: { items } } = await this.getLocales({
+          params: {
+            limit: PAGE_SIZE,
+            page,
+            search: JSON.stringify({
+              enabled: [
+                {
+                  operator: "=",
+                  value: true,
+                },
+              ],
+            }),
+          },
+        });
+        return items.map((locale) => locale.code);
+      },
+    },

129-135: Nit: typo in link text.

Add a space after “the” in the link text so it renders correctly.

-      description: "Return labels of attribute options in the response. See [the`linked_data` format](https://api.akeneo.com/concepts/products.html#the-linked_data-format) section for more details.",
+      description: "Return labels of attribute options in the response. See [the `linked_data` format](https://api.akeneo.com/concepts/products.html#the-linked_data-format) section for more details.",

191-198: Identifier-based draft endpoint is valid; consider adding UUID variant to match objectives.

/products/{code}/draft is correct for EE. To also cover UUID flows requested in the PR objectives, consider adding getProductDraftByUuid({ uuid, ... }) calling /products-uuid/{uuid}/draft. (api-dev.akeneo.com, api-prd.akeneo.com)


256-264: Add UUID companion for submit draft to fully cover EE flows.

/products/{code}/proposal is valid for EE, but adding submitDraftByUuid({ uuid, ... }) that POSTs to /products-uuid/{uuid}/proposal will align with the UUID-based actions requested. (api-prd.akeneo.com, api.akeneo.com)


21-24: UX nit: product picker label may show “undefined” UUIDs.

When listing products from /products, some responses/environments may not expose uuid in list payloads. Consider falling back to the identifier to avoid labels like “family - undefined”.

-        return resp?._embedded?.items?.map((product) => ({
-          label: `${product.family} - ${product.uuid}`,
-          value: product.identifier,
-        })) || [];
+        return resp?._embedded?.items?.map((product) => ({
+          label: `${product.family ?? "product"} - ${product.uuid ?? product.identifier}`,
+          value: product.identifier,
+        })) || [];
components/akeneo/actions/create-a-new-product-media-file/create-a-new-product-media-file.mjs (1)

74-81: Optional: increase upload size limits for large media files.

Axios’ defaults may reject large payloads. Consider setting maxBodyLength and maxContentLength to Infinity for robustness.

     await this.app.createProductMediaFile({
       $,
       headers: {
         "Content-Length": contentLength,
         ...data.getHeaders(),
       },
-      data,
+      data,
+      maxBodyLength: Infinity,
+      maxContentLength: Infinity,
     });
components/akeneo/actions/delete-product/delete-product.mjs (1)

19-26: Optional: friendlier summary on 404/4xx.

Wrap in try/catch to set a clearer $summary when the product doesn’t exist (404) or permissions fail (403).

-  async run({ $ }) {
-    const response = await this.akeneo.deleteProduct({
-      $,
-      productId: this.productId,
-    });
-    $.export("$summary", `Successfully deleted product ${this.productId}`);
-    return response;
-  },
+  async run({ $ }) {
+    try {
+      const response = await this.akeneo.deleteProduct({
+        $,
+        productId: this.productId,
+      });
+      $.export("$summary", `Successfully deleted product ${this.productId}`);
+      return response;
+    } catch (err) {
+      if (err.response?.status === 404) {
+        $.export("$summary", `Product ${this.productId} not found`);
+      }
+      throw err;
+    }
+  },
components/akeneo/actions/get-draft/get-draft.mjs (1)

19-26: Optional: reflect EE-only behavior in summary/messages.

If feasible, detect 403/404 to export a clearer summary (e.g., “Draft not found” or “Insufficient permissions”), similar to the delete action suggestion.

components/akeneo/actions/search-products/search-products.mjs (4)

81-91: Validate pagination inputs (min/max) to prevent server-side errors.

Akeneo enforces bounds (e.g., page >= 1, limit within a reasonable range). Add guardrails at the prop level.

Apply this diff:

     page: {
       type: "integer",
       label: "Page",
       description: "The page number to return",
       optional: true,
+      min: 1,
     },
     limit: {
       type: "integer",
       label: "Limit",
       description: "The number of items to return per page",
       optional: true,
+      min: 1,
+      max: 100,
     },

98-114: Avoid sending undefined query params and normalize array props to comma-separated lists.

Some HTTP clients serialize undefined/null unexpectedly; also Akeneo expects comma-separated values for arrays (locales, attributes).

Apply this diff:

-    const response = await this.akeneo.getProducts({
-      $,
-      params: {
-        search: this.search,
-        scope: this.scope,
-        locales: this.locales,
-        attributes: this.attributes,
-        convert_measurements: this.convertMeasurements,
-        with_count: this.withCount,
-        with_attribute_options: this.withAttributeOptions,
-        with_asset_share_links: this.withAssetShareLinks,
-        with_quality_scores: this.withQualityScores,
-        with_completeness: this.withCompleteness,
-        page: this.page,
-        limit: this.limit,
-      },
-    });
+    // normalize potential arrays to comma-separated lists
+    const locales = Array.isArray(this.locales) ? this.locales.join(",") : this.locales;
+    const attributes = Array.isArray(this.attributes) ? this.attributes.join(",") : this.attributes;
+    const rawParams = {
+      search: this.search,
+      scope: this.scope,
+      locales,
+      attributes,
+      convert_measurements: this.convertMeasurements,
+      with_count: this.withCount,
+      with_attribute_options: this.withAttributeOptions,
+      with_asset_share_links: this.withAssetShareLinks,
+      with_quality_scores: this.withQualityScores,
+      with_completeness: this.withCompleteness,
+      page: this.page,
+      limit: this.limit,
+    };
+    const params = Object.fromEntries(
+      Object.entries(rawParams).filter(([, v]) => v !== undefined && v !== null && v !== "")
+    );
+    const response = await this.akeneo.getProducts({
+      $,
+      params,
+    });

12-17: Consider supporting a structured “search” object to reduce JSON escaping errors.

Akeneo’s GET /products expects the “search” filter as JSON. Allowing an object here improves UX and prevents malformed strings.

If acceptable, change the prop and serialize it:

-    search: {
-      type: "string",
+    search: {
+      type: "object",
       label: "Search",
-      description: "Filter products, for more details see the [Filters](https://api.akeneo.com/documentation/filter.html) section",
+      description: "Filter object, as defined in the [Filters](https://api.akeneo.com/documentation/filter.html) section. It will be JSON-encoded when sent.",
       optional: true,
     },

And in run(), update:

-    const rawParams = {
-      search: this.search,
+    const rawParams = {
+      search: this.search && JSON.stringify(this.search),
      ...

116-117: Harden summary and prefer items_count when available.

Avoids runtime errors if _embedded is missing and leverages Akeneo’s count when requested.

Apply this diff:

-    $.export("$summary", `Found ${response._embedded.items.length} products`);
+    const count = response?.items_count ?? response?._embedded?.items?.length ?? 0;
+    $.export("$summary", `Found ${count} products`);
components/akeneo/actions/submit-product-for-approval/submit-product-for-approval.mjs (1)

6-6: Clarify that this endpoint is Enterprise-only.

Akeneo’s proposal workflow is an Enterprise feature. Calling this out in the description will set expectations and reduce support load.

Apply this diff:

-  description: "Submit a product for approval. [See the documentation](https://api.akeneo.com/api-reference.html#post_proposal)",
+  description: "Submit a product for approval (Enterprise Edition). [See the documentation](https://api.akeneo.com/api-reference.html#post_proposal)",
components/akeneo/actions/get-product/get-product.mjs (2)

55-56: Resilient summary and UX.

Using response.identifier is helpful context to users; consider a fallback to this.productId if identifier is absent.

Apply this diff:

-    $.export("$summary", `Found product ${response.identifier}`);
+    $.export("$summary", `Found product ${response?.identifier ?? this.productId}`);

18-42: Optional: Expose scope/locales/attributes to match API capabilities and parity with the search action.

Akeneo allows filtering returned values by scope/locales/attributes when fetching a single product; adding these increases utility and consistency.

Before proceeding, please confirm these params are supported on GET /products/{code} in your target Akeneo version.

Proposed diff:

   props: {
     akeneo,
     productId: {
@@
       description: "Identifier of the product to get",
     },
+    scope: {
+      propDefinition: [ akeneo, "channelCode" ],
+      label: "Scope",
+      description: "Filter returned values to the given channel (scopable attributes and non-scopable).",
+      optional: true,
+    },
+    locales: {
+      propDefinition: [ akeneo, "locale" ],
+      description: "Filter returned values to the given locales (localizable attributes and non-localizable).",
+      optional: true,
+    },
+    attributes: {
+      propDefinition: [ akeneo, "attribute" ],
+      label: "Attributes",
+      description: "Only return values for the specified attributes.",
+      optional: true,
+    },
@@
     const response = await this.akeneo.getProduct({
       $,
       productId: this.productId,
       params: {
+        scope: Array.isArray(this.scope) ? this.scope.join(",") : this.scope,
+        locales: Array.isArray(this.locales) ? this.locales.join(",") : this.locales,
+        attributes: Array.isArray(this.attributes) ? this.attributes.join(",") : this.attributes,
         with_attribute_options: this.withAttributeOptions,
         with_asset_share_links: this.withAssetShareLinks,
         with_quality_scores: this.withQualityScores,
         with_completeness: this.withCompleteness,
       },
     });

If you prefer not to add new props now, consider at least normalizing arrays and omitting undefined values like in the search action.

Also applies to: 47-52

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7a12929 and cfed3d1.

📒 Files selected for processing (8)
  • components/akeneo/actions/create-a-new-product-media-file/create-a-new-product-media-file.mjs (2 hunks)
  • components/akeneo/actions/delete-product/delete-product.mjs (1 hunks)
  • components/akeneo/actions/get-draft/get-draft.mjs (1 hunks)
  • components/akeneo/actions/get-product/get-product.mjs (1 hunks)
  • components/akeneo/actions/search-products/search-products.mjs (1 hunks)
  • components/akeneo/actions/submit-product-for-approval/submit-product-for-approval.mjs (1 hunks)
  • components/akeneo/akeneo.app.mjs (3 hunks)
  • components/akeneo/package.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
components/akeneo/actions/delete-product/delete-product.mjs (4)
components/akeneo/actions/get-draft/get-draft.mjs (1)
  • response (20-23)
components/akeneo/actions/get-product/get-product.mjs (1)
  • response (44-53)
components/akeneo/actions/search-products/search-products.mjs (1)
  • response (98-114)
components/akeneo/actions/submit-product-for-approval/submit-product-for-approval.mjs (1)
  • response (20-23)
components/akeneo/actions/search-products/search-products.mjs (4)
components/akeneo/actions/delete-product/delete-product.mjs (1)
  • response (20-23)
components/akeneo/actions/get-draft/get-draft.mjs (1)
  • response (20-23)
components/akeneo/actions/get-product/get-product.mjs (1)
  • response (44-53)
components/akeneo/actions/submit-product-for-approval/submit-product-for-approval.mjs (1)
  • response (20-23)
components/akeneo/actions/submit-product-for-approval/submit-product-for-approval.mjs (3)
components/akeneo/actions/delete-product/delete-product.mjs (1)
  • response (20-23)
components/akeneo/actions/get-draft/get-draft.mjs (1)
  • response (20-23)
components/akeneo/actions/get-product/get-product.mjs (1)
  • response (44-53)
components/akeneo/actions/get-draft/get-draft.mjs (4)
components/akeneo/actions/delete-product/delete-product.mjs (1)
  • response (20-23)
components/akeneo/actions/get-product/get-product.mjs (1)
  • response (44-53)
components/akeneo/actions/search-products/search-products.mjs (1)
  • response (98-114)
components/akeneo/actions/submit-product-for-approval/submit-product-for-approval.mjs (1)
  • response (20-23)
components/akeneo/actions/get-product/get-product.mjs (2)
components/akeneo/actions/delete-product/delete-product.mjs (1)
  • response (20-23)
components/akeneo/actions/search-products/search-products.mjs (1)
  • response (98-114)
⏰ 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). (4)
  • GitHub Check: pnpm publish
  • GitHub Check: Lint Code Base
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Verify TypeScript components
🔇 Additional comments (12)
components/akeneo/package.json (1)

3-3: Version bump looks good.

The package version update to 0.2.0 aligns with the expanded Akeneo surface in this PR.

components/akeneo/akeneo.app.mjs (2)

177-184: getProduct method looks good.

Endpoint and wrapper are consistent with Akeneo identifier-based “Get a product” endpoint. (api.akeneo.com)


247-255: deleteProduct wrapper LGTM.

Matches the identifier-based delete endpoint. Note Akeneo returns 204 No Content on success. (api.akeneo.com)

components/akeneo/actions/create-a-new-product-media-file/create-a-new-product-media-file.mjs (2)

10-10: Version bump acknowledged.

Action manifest now at 0.1.3.


16-21: Making productId optional here is appropriate.

This action supports either Product Identifier or Product Model Code; overriding the app-level default keeps the UI ergonomic while runtime validation enforces “one of two” semantics.

components/akeneo/actions/delete-product/delete-product.mjs (1)

1-27: Delete action is correct and minimal.

Uses the identifier-based delete route via the app wrapper and exports a helpful summary.

components/akeneo/actions/get-draft/get-draft.mjs (1)

1-27: Get Draft action looks good.

Calls the correct identifier-based EE endpoint via the app wrapper and returns the API response. Endpoint is EE-only; ensure users understand that requirement. (api-dev.akeneo.com)

components/akeneo/actions/search-products/search-products.mjs (2)

94-96: Good guard: enforce scope when convertMeasurements is enabled.

This prevents an API error up front and aligns with Akeneo’s requirement that convert_measurements depends on scope.


27-43: Parity and clarity across prop definitions looks good.

Nice reuse of app propDefinitions for scope/locales/attributes and the boolean flags; consistent naming with snake_cased query params in run().

components/akeneo/actions/submit-product-for-approval/submit-product-for-approval.mjs (2)

20-25: Run path and summary are straightforward.

Happy path is clear; response is returned and a succinct summary is exported.


16-17: No update needed: prop correctly expects product identifier, not UUID

Verified that submitDraft invokes the endpoint at /products/${productId}/proposal (not /products-uuid/{uuid}/proposal), so the action parameter truly represents the product’s identifier (code). The existing description—“Identifier of the product to submit for approval”—is accurate; you can disregard the suggestion to rename it to “UUID.”

Likely an incorrect or invalid review comment.

components/akeneo/actions/get-product/get-product.mjs (1)

44-53: Run flow and param mapping look correct.

Maps UI booleans to Akeneo’s snake_case query params and returns the payload. Good use of the app client.

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 (1)
components/akeneo/akeneo.app.mjs (1)

114-127: Double-pagination bug appears fixed here — good catch.

Using only page: page + 1 (without page++) resolves the earlier “skip pages” issue noted in a prior review. Nicely done.

🧹 Nitpick comments (5)
components/akeneo/akeneo.app.mjs (5)

94-113: Add pagination and null-safety to Locale options.

The Akeneo Locales endpoint is paginated; without paging you’ll only surface the first page (default 10). Also, destructuring will throw if the shape isn’t as expected. Suggest:

-      async options() {
-        const { _embedded: { items } } = await this.getLocales({
-          params: {
-            search: JSON.stringify({
-              enabled: [
-                {
-                  operator: "=",
-                  value: true,
-                },
-              ],
-            }),
-          },
-        });
-        return items.map((locale) => locale.code);
-      },
+      async options({ page }) {
+        const resp = await this.getLocales({
+          params: {
+            limit: PAGE_SIZE,
+            page: page + 1,
+            search: JSON.stringify({
+              enabled: [{ operator: "=", value: true }],
+            }),
+          },
+        });
+        const items = resp?._embedded?.items ?? [];
+        return items.map((l) => l.code);
+      },

Reference: Locales list is explicitly paginated (page, limit). (api.akeneo.com)


114-127: Guard against missing _embedded/items to avoid destructuring exceptions.

If the API returns an error payload or unexpected shape, destructuring { _embedded: { items } } will throw. Return an empty array when items is absent.

-      async options({ page }) {
-        const { _embedded: { items } } = await this.getAttributes({
+      async options({ page }) {
+        const resp = await this.getAttributes({
           params: {
             limit: PAGE_SIZE,
             page: page + 1,
           },
         });
-        return items.map((attribute) => attribute.code);
+        const items = resp?._embedded?.items ?? [];
+        return items.map((attribute) => attribute.code);
       },

128-151: Fix minor copy: pluralization + spacing in labels/descriptions.

  • “With Completeness” → “With Completenesses” (matches Akeneo’s wording).
  • Add missing space before backticks in “the linked_data format”.
   withAttributeOptions: {
     type: "boolean",
     label: "With Attribute Options",
-    description: "Return labels of attribute options in the response. See [the`linked_data` format](https://api.akeneo.com/concepts/products.html#the-linked_data-format) section for more details.",
+    description: "Return labels of attribute options in the response. See [the `linked_data` format](https://api.akeneo.com/concepts/products.html#the-linked_data-format) section for more details.",
     optional: true,
   },
   withCompletenesses: {
     type: "boolean",
-    label: "With Completeness",
+    label: "With Completenesses",
     description: "Return product completenesses in the response",
     optional: true,
   },

190-197: Endpoint is correct for identifier-based drafts; consider making the method async + consistent arg naming.

Akeneo supports drafts on identifier products at /api/rest/v1/products/{code}/draft. Returning a Promise without async is fine, but for consistency with other methods, consider:

-    getProductDraft({
-      productId, ...opts
-    }) {
-      return this._makeRequest({
-        path: `/products/${productId}/draft`,
-        ...opts,
-      });
-    },
+    async getProductDraft({ productId, ...args }) {
+      return this._makeRequest({
+        path: `/products/${productId}/draft`,
+        ...args,
+      });
+    },

Docs: Identifier-based draft endpoint exists. (api.akeneo.com)


255-263: Submit draft endpoint is valid; consider adding UUID-based counterparts to cover both APIs.

Identifier-based proposal: /api/rest/v1/products/{code}/proposal is supported. To align with PR objectives (also UUID-based endpoints), add UUID variants (submit/get draft) to avoid duplicating request logic in actions.

Docs: Identifier and UUID proposal endpoints are both documented. (api.akeneo.com, 6yemplx3ch.apidog.io)

Example additions (outside this hunk):

// Methods section additions
async getProductDraftByUuid({ uuid, ...args }) {
  return this._makeRequest({ path: `/products-uuid/${uuid}/draft`, ...args });
}
async submitDraftByUuid({ uuid, ...args }) {
  return this._makeRequest({ method: "POST", path: `/products-uuid/${uuid}/proposal`, ...args });
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between cfed3d1 and 6decb4d.

📒 Files selected for processing (3)
  • components/akeneo/actions/get-product/get-product.mjs (1 hunks)
  • components/akeneo/actions/search-products/search-products.mjs (1 hunks)
  • components/akeneo/akeneo.app.mjs (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • components/akeneo/actions/get-product/get-product.mjs
  • components/akeneo/actions/search-products/search-products.mjs
⏰ 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). (3)
  • GitHub Check: Lint Code Base
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Verify TypeScript components
🔇 Additional comments (3)
components/akeneo/akeneo.app.mjs (3)

176-183: Thin wrapper for GET /products/{code} looks good.

Signature and path align with the identifier-based product endpoint.


184-189: LGTM: getLocales wrapper.

Simple pass-through fits the usage above.


246-254: DELETE /products/{code} wrapper looks correct.

Nothing to add.

Copy link
Collaborator

@jcortes jcortes left a comment

Choose a reason for hiding this comment

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

Hi @michelle0927 lgtm! Ready for QA!

@vunguyenhung vunguyenhung merged commit 4019317 into master Aug 27, 2025
10 checks passed
@vunguyenhung vunguyenhung deleted the issue-18052 branch August 27, 2025 03:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Akeneo

4 participants