Product update validation
When an admin creates or updates a product, a third-party system is used to validate the product attributes. For example, a third-party system can validate the new product name.
Webhook name
observer.catalog_product_save_after
Payloads
The following observer.catalog_product_save_after
payload was obtained from execution of the application code. Some data has been adjusted or deleted for brevity.
Default payload
Configured payload
Copied to your clipboard{"eventName": "catalog_product_save_after","data": {"product": {"_edit_mode": true,"store_id": 0,"entity_id": "1","attribute_set_id": "16","type_id": "simple","sku": "Pr-1","name": "Product 1","tax_class_id": "0","description": "<p>Product 1 description</p>","price": "10.00","extension_attributes": {...},"quantity_and_stock_status": {...},"category_ids": {...},"stock_data": {...},"media_gallery": {...},...},"data_object": {...}}}
Copied to your clipboard{"product": {"name": "Product 1"}}
The third-party endpoint receives the following payload, which is based on the configured fields:
Copied to your clipboard{"product": {"name": "Product 1"}}
Configuration
webhook.xml (PaaS)
Copied to your clipboard<method name="observer.catalog_product_save_after" type="before"><hooks><batch name="product_update"><hook name="validate_name" url="{env:APP_BUILDER_URL}/validate-product-name" fallbackErrorMessage="The product name cannot be validated" method="POST" timeout="5000" softTimeout="1000"><headers><header name="x-gw-ims-org-id">{env:APP_BUILDER_IMS_ORG_ID}</header><header name="Authorization">Bearer {env:APP_BUILDER_AUTH_TOKEN}</header></headers><fields><field name="product.name" source="data.product.name" /></fields></hook></batch></hooks></method>
Admin (SaaS)
Copied to your clipboardHook SettingsWebhook method: observer.catalog_product_save_afterWebhook type: afterBatch name: product_updateHook name: validate_nameURL: <Host>/validate-product-nameTimeout: 5000Soft timeout: 1000Fallback Error Message: The product name cannot be validatedRequired: RequiredActive: YesMethod: POSTDeveloper Console OAuthClient ID: The client ID for the OAuth credential.Client Secret: The client secret for the OAuth credential.Organization ID: The organization ID for the OAuth credential.Hook FieldsName: product.nameSource: data.product.nameActive: Yes
Copied to your clipboard<method name="observer.catalog_product_save_after" type="before"><hooks><batch name="product_update"><hook name="validate_name" url="{env:APP_BUILDER_URL}/validate-product-name" fallbackErrorMessage="The product name cannot be validated" method="POST" timeout="5000" softTimeout="1000"><headers><header name="x-gw-ims-org-id">{env:APP_BUILDER_IMS_ORG_ID}</header><header name="Authorization">Bearer {env:APP_BUILDER_AUTH_TOKEN}</header></headers><fields><field name="product.name" source="data.product.name" /></fields></hook></batch></hooks></method>
Copied to your clipboardHook SettingsWebhook method: observer.catalog_product_save_afterWebhook type: afterBatch name: product_updateHook name: validate_nameURL: <Host>/validate-product-nameTimeout: 5000Soft timeout: 1000Fallback Error Message: The product name cannot be validatedRequired: RequiredActive: YesMethod: POSTDeveloper Console OAuthClient ID: The client ID for the OAuth credential.Client Secret: The client secret for the OAuth credential.Organization ID: The organization ID for the OAuth credential.Hook FieldsName: product.nameSource: data.product.nameActive: Yes
Endpoint code example
The following code example shows how to implement the webhook on your custom endpoint.
Copied to your clipboardconst fetch = require('node-fetch')const { Core } = require('@adobe/aio-sdk')const { errorResponse, stringParameters, checkMissingRequestInputs } = require('../utils')// main function that will be executed by Adobe I/O Runtimeasync function main (params) {// create a Loggerconst logger = Core.Logger('main', { level: params.LOG_LEVEL || 'info' })try {// 'info' is the default level if not setlogger.info('Calling the main action')// log parameters, only if params.LOG_LEVEL === 'debug'logger.debug(stringParameters(params))//check for missing request input parameters and headersconst requiredParams = [/* add required params */]const requiredHeaders = ['Authorization']const errorMessage = checkMissingRequestInputs(params, requiredParams, requiredHeaders)if (errorMessage) {// return and log client errorsreturn errorResponse(400, errorMessage, logger)}// Place the real validation (calling 3rd party endpoints) here.// In this example, we check if the name contains the word test.// If it does, the request is considered invalid.const response = {statusCode: 200}if (/test/.test(params.product.name.toLowerCase())) {response.body = JSON.stringify({op: "exception",message: "Invalid product name"})} else {response.body = JSON.stringify({op: "success"})}return response} catch (error) {// log any server errorslogger.error(error)// return with 500return errorResponse(500, 'server error', logger)}}exports.main = main
If validation fails, the runtime AppBuilder action returns an exception message. The message is visible to the customer.
Copied to your clipboardresponse.body = JSON.stringify({op: "exception",message: "Invalid product name"})