95 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			95 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Python
		
	
	
# -*- coding: utf-8 -*-
 | 
						|
# Copyright 2014 OpenMarket Ltd
 | 
						|
#
 | 
						|
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
# you may not use this file except in compliance with the License.
 | 
						|
# You may obtain a copy of the License at
 | 
						|
#
 | 
						|
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
#
 | 
						|
# Unless required by applicable law or agreed to in writing, software
 | 
						|
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
# See the License for the specific language governing permissions and
 | 
						|
# limitations under the License.
 | 
						|
 | 
						|
"""This module contains logic for storing HTTP PUT transactions. This is used
 | 
						|
to ensure idempotency when performing PUTs using the REST API."""
 | 
						|
import logging
 | 
						|
 | 
						|
logger = logging.getLogger(__name__)
 | 
						|
 | 
						|
 | 
						|
class HttpTransactionStore(object):
 | 
						|
 | 
						|
    def __init__(self):
 | 
						|
        # { key : (txn_id, response) }
 | 
						|
        self.transactions = {}
 | 
						|
 | 
						|
    def get_response(self, key, txn_id):
 | 
						|
        """Retrieve a response for this request.
 | 
						|
 | 
						|
        Args:
 | 
						|
            key (str): A transaction-independent key for this request. Usually
 | 
						|
                this is a combination of the path (without the transaction id)
 | 
						|
                and the user's access token.
 | 
						|
            txn_id (str): The transaction ID for this request
 | 
						|
        Returns:
 | 
						|
            A tuple of (HTTP response code, response content) or None.
 | 
						|
        """
 | 
						|
        try:
 | 
						|
            logger.debug("get_response Key: %s TxnId: %s", key, txn_id)
 | 
						|
            (last_txn_id, response) = self.transactions[key]
 | 
						|
            if txn_id == last_txn_id:
 | 
						|
                logger.info("get_response: Returning a response for %s", key)
 | 
						|
                return response
 | 
						|
        except KeyError:
 | 
						|
            pass
 | 
						|
        return None
 | 
						|
 | 
						|
    def store_response(self, key, txn_id, response):
 | 
						|
        """Stores an HTTP response tuple.
 | 
						|
 | 
						|
        Args:
 | 
						|
            key (str): A transaction-independent key for this request. Usually
 | 
						|
                this is a combination of the path (without the transaction id)
 | 
						|
                and the user's access token.
 | 
						|
            txn_id (str): The transaction ID for this request.
 | 
						|
            response (tuple): A tuple of (HTTP response code, response content)
 | 
						|
        """
 | 
						|
        logger.debug("store_response Key: %s TxnId: %s", key, txn_id)
 | 
						|
        self.transactions[key] = (txn_id, response)
 | 
						|
 | 
						|
    def store_client_transaction(self, request, txn_id, response):
 | 
						|
        """Stores the request/response pair of an HTTP transaction.
 | 
						|
 | 
						|
        Args:
 | 
						|
            request (twisted.web.http.Request): The twisted HTTP request. This
 | 
						|
            request must have the transaction ID as the last path segment.
 | 
						|
            response (tuple): A tuple of (response code, response dict)
 | 
						|
            txn_id (str): The transaction ID for this request.
 | 
						|
        """
 | 
						|
        self.store_response(self._get_key(request), txn_id, response)
 | 
						|
 | 
						|
    def get_client_transaction(self, request, txn_id):
 | 
						|
        """Retrieves a stored response if there was one.
 | 
						|
 | 
						|
        Args:
 | 
						|
            request (twisted.web.http.Request): The twisted HTTP request. This
 | 
						|
            request must have the transaction ID as the last path segment.
 | 
						|
            txn_id (str): The transaction ID for this request.
 | 
						|
        Returns:
 | 
						|
            The response tuple.
 | 
						|
        Raises:
 | 
						|
            KeyError if the transaction was not found.
 | 
						|
        """
 | 
						|
        response = self.get_response(self._get_key(request), txn_id)
 | 
						|
        if response is None:
 | 
						|
            raise KeyError("Transaction not found.")
 | 
						|
        return response
 | 
						|
 | 
						|
    def _get_key(self, request):
 | 
						|
        token = request.args["access_token"][0]
 | 
						|
        path_without_txn_id = request.path.rsplit("/", 1)[0]
 | 
						|
        return path_without_txn_id + "/" + token
 |