Updating Products

with the Liferay Batch API

Introduction

This is a quick follow up to my last post where I discussed how to use Liferay's batch APIs to import products. I've had a few people asking how they could leverage the batch APIs to update products. The confusion stems from the fact that there is no batch version of a PUT or PATCH endpoint available when browsing the endpoints in the API explorer. Well never fear, even without a PUT or PATCH, we can still update existing products in Liferay.

External Reference Codes

Many Liferay entities, and most Commerce entities support an external reference code or ERC. The ERC is very useful in integration scenarios because it allows you to keep a reference to a unique identifier in an external system inside of Liferay. Sure, in the past you could have used a Custom Field to store this reference, but you would have had to handle all of the logic of looking up the ERC and comparing it in your custom code. With an ERC, their are often headless endpoints that use ERC instead of Liferay's internal IDs and even if there isn't a headless endpoint that specifically uses the ERC, it can still be used to ensure you're updating the right entity.

Now before we go any further, I need to revisit my last blog and correct one point. In that blog I mentioned that the only attributes you needed to provide Liferay in order to create a product through the headless APIs was:

{
  "active": "true",
  "catalogId": 32780,
  "name": {
    "en_US": "DEMO Sensor"
  },
  "productType": "simple"
}

While this is factually true, the reality is that if you are importing products from an external system, there will be a unique identifier in that external system that we want to include as an ERC in Liferay. So for all practical purposes, we should really treat this as the minimum attributes we need to supply for a product:

{
  "active": "true",
  "catalogId": 32780,
  "externalReferenceCode": "EXT-DEMO-1",
  "name": {
    "en_US": "DEMO Sensor"
  },
  "productType": "simple"
}

Say Hello to my Little Friend . . . 

Now that we have an ERC to uniquely identify our products from the external system, how do we perform an update without a PUT or PATCH endpoint? Well let me introduce you to my good friend, the upsert. What's an upsert you may be wondering? Well, it's a combination of an update and an insert and many of the commerce POST endpoints support it. The first time we POST the above snippet, we'll get a new product in the system, called a DEMO Sensor and an ERC of EXT-DEMO-1. After that, any subsequent POST with the same ERC will be treated as an update.

So, if we update our original batch JSON to include our ERC like this:

[
  {
    "active": "true",
    "catalogId": 32780,
    "externalReferenceCode": "EXT-DEMO-1",
    "name": {
      "en_US": "DEMO Sensor One"
    },
    "productType": "simple"
  },
  {
    "active": "true",
    "catalogId": 32780,
    "externalReferenceCode": "EXT-DEMO-2",
    "name": {
      "en_US": "DEMO Sensor Two"
    },
    "productType": "simple"
  },
  {
    "active": "true",
    "catalogId": 32780,
    "externalReferenceCode": "EXT-DEMO-3",
    "name": {
      "en_US": "DEMO Sensor Three"
    },
    "productType": "simple"
  }  
]

Then, in the future, let's say we wanted to add a product description to all of our products.  We can  easily  go back and do a batch update on all of those existing records with the following:

[
  {
    "active": "true",
    "catalogId": 32780,
    "description": {
      "en_US": "Product designed and manufactured to accommodate OEM applications. All products are tested and inspected in an ISO-9000 compliant environment"
    },
    "externalReferenceCode": "EXT-DEMO-1",
    "name": {
      "en_US": "DEMO Sensor One"
    },
    "productType": "simple"
  },
  {
    "active": "true",
    "catalogId": 32780,
    "description": {
      "en_US": "Product designed and manufactured to accommodate OEM applications. All products are tested and inspected in an ISO-9000 compliant environment"
    },
    "externalReferenceCode": "EXT-DEMO-2",
    "name": {
      "en_US": "DEMO Sensor Two"
    },
    "productType": "simple"
  },
  {
    "active": "true",
    "catalogId": 32780,
    "description": {
      "en_US": "Product designed and manufactured to accommodate OEM applications. All products are tested and inspected in an ISO-9000 compliant environment"
    },
    "externalReferenceCode": "EXT-DEMO-3",
    "name": {
      "en_US": "DEMO Sensor Three"
    },
    "productType": "simple"
  }  
]

Conclusion

With the help of our new friend, the upsert, we have everything we need right? Well, not exactly. While this does provide us with the ability to update products, it's not as developer friendly as a PATCH endpoint. With a PATCH endpoint, our update could be simplified so we only supply the ERC and the attribute we want to update:

[
  {
    "description": {
      "en_US": "Product designed and manufactured to accommodate OEM applications. All products are tested and inspected in an ISO-9000 compliant environment"
    },
    "externalReferenceCode": "EXT-DEMO-1"
  },
  {
    "description": {
      "en_US": "Product designed and manufactured to accommodate OEM applications. All products are tested and inspected in an ISO-9000 compliant environment"
    },
    "externalReferenceCode": "EXT-DEMO-2"
  },
  {
   "description": {
      "en_US": "Product designed and manufactured to accommodate OEM applications. All products are tested and inspected in an ISO-9000 compliant environment"
    },
    "externalReferenceCode": "EXT-DEMO-3"
  }  
]

We do have a Feature Request under review (LPD-388) so hopefully in future we will have the best of all worlds, the ability to create, update, and patch products using the batch APIs.

Blogs