diff --git a/.gitignore b/.gitignore index c51ef74..efcb904 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,6 @@ generator/openapitools.json # Demo demo/ + +# AI Rules +.cursor/ \ No newline at end of file diff --git a/generator/catalog-ingestion-openapi-spec.yaml b/generator/catalog-ingestion-openapi-spec.yaml index 77ef6df..7103612 100644 --- a/generator/catalog-ingestion-openapi-spec.yaml +++ b/generator/catalog-ingestion-openapi-spec.yaml @@ -40,8 +40,10 @@ tags: - Updating existing category information - Deleting categories from the catalog - Associating categories with product families for enhanced organization + - Adding SEO meta tags (title, description, keywords) to categories + - Associating images with categories - Categories use a slug-based hierarchy format to represent parent-child relationships, for example, "men/clothing/pants'. + Categories use a slug-based hierarchy format to represent parent-child relationships, for example, "men/clothing/pants". After you create categories and assign them to products, you can retrieve category data to render storefront menus and manage hierarchical category trees using the GraphQL `navigation` and `categorytree` queries. See @@ -318,6 +320,8 @@ paths: description: | Update existing product attribute metadata with new values. When the update is processed, the merge strategy is used to apply changes to `scalar` and `object` type fields. The replace strategy is used to apply changes for fields in an `array`. + + > **Note:** Before submitting an update request, verify that the target entity already exists. Update operations do not validate the existence of the entity — if it does not exist, the request is accepted, but the update is silently ignored. operationId: updateProductMetadata parameters: - $ref: "#/components/parameters/Authorization" @@ -419,7 +423,10 @@ paths: - A category `slug` string can contain only lowercase letters, numbers, and hyphens with `/` used as a separator for hierarchy. - Create each category as a separate entity. - Use the `name` field to define the display name for the category. + - Use the optional `description` field to provide a full-text description of the category. - Use the optional `families` field to associate categories with product families for enhanced organization. + - Use the optional `metaTags` field to define SEO meta tags (title, description, keywords) for the category. + - Use the optional `images` field to associate images with the category. After you create categories, link a product to a category using the `path` value for the [routes](#operation/createProducts!path=routes&t=request) field. When you create or update products. The value of `path` in the route must match the `slug` value for the category. @@ -460,12 +467,27 @@ paths: "slug": "men", "source": { "locale": "en-US" }, "name": "Men", - "families": ["apparel", "accessories"] + "description": "Men's clothing, shoes, and accessories", + "families": ["apparel", "accessories"], + "metaTags": { + "title": "Men's Collection", + "description": "Shop men's clothing, shoes, and accessories", + "keywords": ["men", "clothing", "accessories"] + }, + "images": [ + { + "url": "https://example.com/images/men-category.png", + "label": "Men's Category", + "roles": ["SMALL"], + "customRoles": [] + } + ] }, { "slug": "men/clothing", "source": { "locale": "en-US" }, "name": "Men's Clothing", + "description": "Men's clothing and apparel", "families": ["apparel"] }, { @@ -482,6 +504,8 @@ paths: description: | Update existing product categories with new values. When the update is processed, the merge strategy is used to apply changes to `scalar` and `object` type fields. The replace strategy is used to apply changes for fields in an `array`. + + > **Note:** Before submitting an update request, verify that the target entity already exists. Update operations do not validate the existence of the entity — if it does not exist, the request is accepted, but the update is silently ignored. operationId: updateCategories parameters: - $ref: "#/components/parameters/Authorization" @@ -514,13 +538,29 @@ paths: The example below updates the following: * `name` - Change the category display name. * `families` - Replace the product families associated with the category. + * `metaTags` - Update the SEO meta tags for the category. + * `images` - Replace the images associated with the category. value: [ { "slug": "men/clothing", "source": { "locale": "en-US" }, "name": "Men's Apparel", - "families": ["clothing", "fashion"] + "description": "Updated collection of men's apparel and fashion", + "families": ["clothing", "fashion"], + "metaTags": { + "title": "Men's Apparel - Updated", + "description": "Updated collection of men's apparel", + "keywords": ["men", "apparel", "fashion"] + }, + "images": [ + { + "url": "https://example.com/images/mens-apparel.png", + "label": "Men's Apparel", + "roles": ["SMALL"], + "customRoles": [] + } + ] } ] /v1/catalog/categories/delete: @@ -1101,6 +1141,7 @@ paths: * `configurations` match on `type` and `attributeCode` * `externalIds` match on `type` and `origin` + > **Note:** Before submitting an update request, verify that the target entity already exists. Update operations do not validate the existence of the entity — if it does not exist, the request is accepted, but the update is silently ignored. operationId: updateProducts parameters: - $ref: "#/components/parameters/Authorization" @@ -1591,6 +1632,8 @@ paths: * **Base Price Books**: Update name and currency as needed * **Child Price Books**: Include correct `parentId` in request (will be ignored if different) * **Hierarchy Restructuring**: Delete child price book and recreate with new parent reference + + > **Note:** Before submitting an update request, verify that the target entity already exists. Update operations do not validate the existence of the entity — if it does not exist, the request is accepted, but the update is silently ignored. operationId: updatePriceBooks parameters: - name: Content-Type @@ -2006,6 +2049,7 @@ paths: * Ensure tier quantities are in ascending order * Test updates in a development environment first + > **Note:** Before submitting an update request, verify that the target entity already exists. Update operations do not validate the existence of the entity — if it does not exist, the request is accepted, but the update is silently ignored. operationId: updatePrices parameters: - $ref: "#/components/parameters/Authorization" @@ -2377,6 +2421,11 @@ components: maxLength: 128 description: Display name of the category example: "Men's Pants" + description: + type: string + nullable: true + description: Full-text description of the category. + example: "Men's clothing, shoes, and accessories" families: type: array nullable: true @@ -2386,6 +2435,13 @@ components: Optional array of product family identifiers that this category is associated with. Used for enhanced product organization and filtering. example: ["apparel", "clothing"] + metaTags: + $ref: "#/components/schemas/ProductMetaAttribute" + images: + type: array + description: A list of category images. + items: + $ref: "#/components/schemas/ProductImage" additionalProperties: false FeedCategoryUpdate: title: FeedCategoryUpdate @@ -2413,6 +2469,11 @@ components: maxLength: 128 description: Display name of the category example: "Men's Pants" + description: + type: string + nullable: true + description: Full-text description of the category. + example: "Men's clothing, shoes, and accessories" families: type: array nullable: true @@ -2424,6 +2485,13 @@ components: you can associate it with the "apparel" family. Note: This field uses the replace strategy to replace the entire array with the new values. example: ["apparel", "clothing"] + metaTags: + $ref: "#/components/schemas/ProductMetaAttribute" + images: + type: array + description: A list of category images. + items: + $ref: "#/components/schemas/ProductImage" additionalProperties: false FeedCategoryDelete: title: Delete category diff --git a/package.json b/package.json index f92cbab..e0300f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@adobe-commerce/aco-ts-sdk", - "version": "1.2.2", + "version": "1.2.3", "description": "TypeScript Client SDK for the Adobe Commerce Optimizer API", "type": "module", "license": "Apache-2.0", diff --git a/src/client.ts b/src/client.ts index fb1744b..a80cdba 100644 --- a/src/client.ts +++ b/src/client.ts @@ -46,11 +46,14 @@ export interface Client { * unique `slug` and `source`. - To create parent-child relationships, create the `slug` field in a hierarchical * format, for example `men/clothing/pants\'. - A category `slug`string can contain only lowercase letters, numbers, * and hyphens with`/`used as a separator for hierarchy. - Create each category as a separate entity. - Use - * the`name`field to define the display name for the category. - Use the optional`families`field to associate - * categories with product families for enhanced organization. After you create categories, link a product to a - * category using the`path`value for the [routes](#operation/createProducts!path=routes&t=request) field. When you - * create or update products. The value of`path`in the route must match the`slug` value for the category. To update - * existing categories, use the update operation. + * the`name`field to define the display name for the category. - Use the optional`description`field to provide a + * full-text description of the category. - Use the optional`families`field to associate categories with product + * families for enhanced organization. - Use the optional`metaTags`field to define SEO meta tags (title, description, + * keywords) for the category. - Use the optional`images`field to associate images with the category. After you create + * categories, link a product to a category using the`path`value for the + * [routes](#operation/createProducts!path=routes&t=request) field. When you create or update products. The value + * of`path`in the route must match the`slug` value for the category. To update existing categories, use the update + * operation. * * @param data - FeedCategory[] payload * @returns {Promise} Feed response indicating the number of accepted items @@ -75,7 +78,9 @@ export interface Client { /** * Update categories Update existing product categories with new values. When the update is processed, the merge * strategy is used to apply changes to `scalar` and `object` type fields. The replace strategy is used to apply - * changes for fields in an `array`. + * changes for fields in an `array`. > **Note:** Before submitting an update request, verify that the target entity + * already exists. Update operations do not validate the existence of the entity — if it does not exist, the request + * is accepted, but the update is silently ignored. * * @param data - FeedCategoryUpdate[] payload * @returns {Promise} Feed response indicating the number of accepted items @@ -110,7 +115,9 @@ export interface Client { /** * Update product attribute metadata Update existing product attribute metadata with new values. When the update is * processed, the merge strategy is used to apply changes to `scalar` and `object` type fields. The replace strategy - * is used to apply changes for fields in an `array`. + * is used to apply changes for fields in an `array`. > **Note:** Before submitting an update request, verify that the + * target entity already exists. Update operations do not validate the existence of the entity — if it does not exist, + * the request is accepted, but the update is silently ignored. * * @param data - FeedMetadataUpdate[] payload * @returns {Promise} Feed response indicating the number of accepted items @@ -169,7 +176,9 @@ export interface Client { * validates that `parentId` references exist and hierarchy depth is maintained

Update Strategies

* **Base * Price Books**: Update name and currency as needed * **Child Price Books**: Include correct `parentId` in request * (will be ignored if different) * **Hierarchy Restructuring**: Delete child price book and recreate with new - * parent reference + * parent reference > **Note:** Before submitting an update request, verify that the target entity already exists. + * Update operations do not validate the existence of the entity — if it does not exist, the request is accepted, + * but the update is silently ignored. * * @param data - FeedPricebook[] payload * @returns {Promise} Feed response indicating the number of accepted items @@ -216,7 +225,9 @@ export interface Client { * discounts/tiers, send an empty array * To add new discounts/tiers, include both existing and new items

Best * practices

* Always include the complete array of discounts/tiers when updating * Use descriptive discount * codes for easier management * Ensure tier quantities are in ascending order * Test updates in a development - * environment first + * environment first > **Note:** Before submitting an update request, verify that the target entity already exists. + * Update operations do not validate the existence of the entity — if it does not exist, the request is accepted, but + * the update is silently ignored. * * @param data - FeedPricesUpdate[] payload * @returns {Promise} Feed response indicating the number of accepted items @@ -333,7 +344,9 @@ export interface Client { * list, you can update a specific object by matching on a key field. The following fields are supported: * * `attributes` - match on `code` * `images` - match on `url` * `routes` - match on `path` * `links` - match on `type` * and `sku` * `bundles` match on `type` and `group` * `configurations` match on `type` and `attributeCode` * - * `externalIds` match on `type` and `origin` + * `externalIds` match on `type` and `origin` > **Note:** Before submitting an update request, verify that the target + * entity already exists. Update operations do not validate the existence of the entity — if it does not exist, the + * request is accepted, but the update is silently ignored. * * @param data - FeedProductUpdate[] payload * @returns {Promise} Feed response indicating the number of accepted items diff --git a/src/types.ts b/src/types.ts index 68ea9d6..7b0b16e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -90,6 +90,13 @@ export interface FeedCategory { * @type {string} */ name: string; + /** + * Full-text description of the category. + * + * @memberof FeedCategory + * @type {string} + */ + description?: string; /** * Optional array of product family identifiers that this category is associated with. Used for enhanced product * organization and filtering. @@ -98,6 +105,18 @@ export interface FeedCategory { * @type {string[]} */ families?: string[]; + /** + * @memberof FeedCategory + * @type {ProductMetaAttribute} + */ + metaTags?: ProductMetaAttribute; + /** + * A list of category images. + * + * @memberof FeedCategory + * @type {ProductImage[]} + */ + images?: ProductImage[]; } /** * Delete category information for removing categories from the catalog. @@ -147,6 +166,13 @@ export interface FeedCategoryUpdate { * @type {string} */ name?: string; + /** + * Full-text description of the category. + * + * @memberof FeedCategoryUpdate + * @type {string} + */ + description?: string; /** * Optional array of product family identifiers that this category is associated with. Used for enhanced product * organization and filtering. For example, for a clothing category, you can associate it with the "apparel" family. @@ -156,6 +182,18 @@ export interface FeedCategoryUpdate { * @type {string[]} */ families?: string[]; + /** + * @memberof FeedCategoryUpdate + * @type {ProductMetaAttribute} + */ + metaTags?: ProductMetaAttribute; + /** + * A list of category images. + * + * @memberof FeedCategoryUpdate + * @type {ProductImage[]} + */ + images?: ProductImage[]; } /** * @export diff --git a/test/integration/categories.test.ts b/test/integration/categories.test.ts index 1a27030..51c81ca 100644 --- a/test/integration/categories.test.ts +++ b/test/integration/categories.test.ts @@ -17,7 +17,15 @@ import { config } from 'dotenv'; import { describe, test, beforeAll, expect } from 'vitest'; import { Client, createClient } from '../../src/client'; -import { FeedCategory, FeedCategoryUpdate, Environment, Region, ClientConfig, LogLevel } from '../../src/types'; +import { + FeedCategory, + FeedCategoryUpdate, + Environment, + Region, + ClientConfig, + LogLevel, + ProductImageRolesEnum, +} from '../../src/types'; import { consoleLogger } from '../../src/logger'; config(); @@ -38,12 +46,38 @@ describe('Categories Integration Tests', () => { source: { locale: 'en-US' }, name: 'Electronics', families: ['tech-products'], + description: 'Electronics category', + metaTags: { + title: 'Electronics', + description: 'Electronics category', + keywords: ['electronics', 'tech', 'products'], + }, + images: [ + { + url: 'https://placehold.co/150', + label: 'Electronics category image', + roles: [ProductImageRolesEnum.Base], + }, + ], }; const topLevelCategory2: FeedCategory = { slug: 'clothing', source: { locale: 'en-US' }, name: 'Clothing', + description: 'Clothing category', + metaTags: { + title: 'Clothing', + description: 'Clothing category', + keywords: ['clothing', 'fashion', 'products'], + }, + images: [ + { + url: 'https://placehold.co/150', + label: 'Clothing category image', + roles: [ProductImageRolesEnum.Base], + }, + ], }; const childCategory1: FeedCategory = { @@ -137,12 +171,38 @@ describe('Categories Integration Tests', () => { source: { locale: 'en-US' }, name: 'Updated Electronics', families: ['tech-products', 'gadgets'], + description: 'Updated electronics category', + metaTags: { + title: 'Updated Electronics', + description: 'Updated electronics category', + keywords: ['electronics', 'tech', 'products'], + }, + images: [ + { + url: 'https://placehold.co/200', + label: 'Updated electronics category image', + roles: [ProductImageRolesEnum.Base], + }, + ], }; const categoryUpdate2: FeedCategoryUpdate = { slug: 'clothing', source: { locale: 'en-US' }, name: 'Updated Clothing', + description: 'Updated clothing category', + metaTags: { + title: 'Updated Clothing', + description: 'Updated clothing category', + keywords: ['clothing', 'fashion', 'products'], + }, + images: [ + { + url: 'https://placehold.co/200', + label: 'Updated clothing category image', + roles: [ProductImageRolesEnum.Base], + }, + ], }; const response = await client.updateCategories([categoryUpdate1, categoryUpdate2]);