-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New Components - akeneo #18189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New Components - akeneo #18189
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
WalkthroughAdds 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
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
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
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 ..."
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"
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
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 unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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 useslocales, notlocale.
get /channels/{code}returns an array field namedlocales(plural). Returningresp?.locale || []will yield[]. Replace withresp?.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
pageargument andPAGE_SIZE. See Akeneo “Get a list of locales” docs (paginated HAL withpage/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}/draftis correct for EE. To also cover UUID flows requested in the PR objectives, consider addinggetProductDraftByUuid({ 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}/proposalis valid for EE, but addingsubmitDraftByUuid({ uuid, ... })that POSTs to/products-uuid/{uuid}/proposalwill 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 exposeuuidin 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
maxBodyLengthandmaxContentLengthtoInfinityfor 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
$summarywhen 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.productIdif 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.
📒 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: MakingproductIdoptional 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 UUIDVerified that
submitDraftinvokes 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.
There was a problem hiding this 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(withoutpage++) 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 whenitemsis 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_dataformat”.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 withoutasyncis 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}/proposalis 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.
📒 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.
jcortes
left a comment
There was a problem hiding this 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!
Resolves #18052
Summary by CodeRabbit
New Features
Refactor
Bug Fixes
Chores