Locking & Unlocking Resources

Overview

There are different approaches to handling what happens when multiple clients are trying to update a specific resource on a server, such as a customer record. The OnSite API uses Pessimistic Locking as its approach, permitting only one session of one application to update a specific resource at any one time. This is achieved by locking and unlocking resources.

To this end, in addition to the HTTP methods GET, POST, PUT and DELETE, there are two additional non-standard HTTP methods used in the OnSite API. These are LOCK and UNLOCK.

In order to update most records through the OnSite API, we need to follow a three-step process:

  1. Lock the record with a LOCK.
  2. Update the record with a PUT.
  3. Unlock the record we updated with an UNLOCK.

Locking a Resource

Before updating the record, you need to lock it, or the subsequent update will fail.

# Request
$ curl -k \
--header 'X-PAPPID: 12345678-90ab-cdef-1234-567890abcdef' \
--header 'User-Agent: com.lightspeed.onsite.demo/1.0' \
--header 'Authorization: Basic bGlnaHRzcGVlZDphZG1pbg==' \
--cookie-jar $HOME'/Library/Cookies/com.acme.app.cookies' \
--cookie $HOME'/Library/Cookies/com.acme.app.cookies' \
--request LOCK 'https://localhost:9630/api/products/34/'

# Response
<?xml version='1.0' encoding='UTF-8'?>
<lock acquired="true">Lock Acquired</lock>

Unlocking a Resource

After updating the record, you need to unlock it, or it won’t be editable by other users.

# Request
$ curl -k \
--header 'X-PAPPID: 12345678-90ab-cdef-1234-567890abcdef' \
--header 'User-Agent: com.lightspeed.onsite.demo/1.0' \
--header 'Authorization: Basic bGlnaHRzcGVlZDphZG1pbg==' \
--cookie-jar $HOME'/Library/Cookies/com.acme.app.cookies' \
--cookie $HOME'/Library/Cookies/com.acme.app.cookies' \
--request UNLOCK 'https://localhost:9630/api/products/34/'

# Response
<?xml version='1.0' encoding='UTF-8'?>
<lock released="true">Unlocked</lock>

Locking and Unlocking with Python

With a bit more set-up, that first request can also be accomplished using a script. The language of choice in this example, and the tutorials, is Python. Before writing code, the project has to be setup. See the instructions in Sending Your First API Request.

Create the following locking_and_unlocking.py file. The file will contain the following:

"""
Unlock/lock a customer using the OnSite API.
"""
import requests
from xml.dom import minidom

# Customize these to your install.
ONSITE_HOST = 'localhost'
ONSITE_PORT = 9630
ONSITE_USERNAME = 'lightspeed'
ONSITE_PASSWORD = 'admin'
APP_ID = 'com.lightspeed.onsite.demo'
APP_VERSION = '1.0'
APP_PRIVATE_ID = '12345678-90ab-cdef-1234-567890abcdef'

url = 'https://%s:%d/api/customers/' % (ONSITE_HOST, ONSITE_PORT)
logout_url = 'https://%s:%d/api/sessions/current/logout/' % (ONSITE_HOST, ONSITE_PORT)

# This is the customer that will be created.
customer_xml = """
<customer>
    <name>
        <first>Jane</first>
        <last>Smith</last>
    </name>
</customer>
"""

# Create a session. This will persist cookies across all requests.
session = requests.Session()
session.auth = (ONSITE_USERNAME, ONSITE_PASSWORD)
session.headers.update({
    'user-agent': '%s/%s' % (APP_ID, APP_VERSION),
    'x-pappid': APP_PRIVATE_ID})
session.verify = False

# Create a customer to use for this example.
post_response = session.post(url, data=customer_xml)
assert post_response.status_code == 201

# Get the ID for the customer.
post_response_xml = minidom.parseString(post_response.text)
customer_url = post_response_xml.getElementsByTagName("customer")[0].attributes["uri"].value

# Lock the customer before making the change.
lock_response = session.request('LOCK', customer_url)
assert lock_response.status_code == 200

lock_response_xml = minidom.parseString(lock_response.text)
print(lock_response_xml.toprettyxml())

# An update would take place here.
print("Update would take place here...")

# Unlock the customer after the change is complete.
unlock_response = session.request('UNLOCK', customer_url)
assert unlock_response.status_code == 200

unlock_response_xml = minidom.parseString(unlock_response.text)
print(unlock_response_xml.toprettyxml())

# Log out.
logout_request = session.post('https://%s:%d/api/sessions/current/logout/' % (ONSITE_HOST, ONSITE_PORT))
assert logout_request.status_code == 204

You can now execute the script from the terminal:

# Execute the code.
python locking_and_unlocking.py

# Output
<?xml version="1.0" ?>
<lock acquired="true">Lock Acquired</lock>

Update would take place here...

<?xml version="1.0" ?>
<lock released="true">Unlocked</lock>