1# Copyright 2014 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""Module contains code used in dealing with data resources.""" 6 7import logging 8import uuid 9 10import common 11from fake_device_server import server_errors 12 13 14class ResourceDelegate(object): 15 """Delegate for resources held by the various server methods. 16 17 The fake_device_server methods are all fairly similar in that they 18 have similar dictionary representations. Server methods use this class to 19 delegate access to their data. 20 21 Data is stored based on a combination of <id> + <api_key> 22 tuples. The api_key can be passed in to any command with ?key=<api_key>. 23 This isn't necessary though as using a default of None is ok. 24 """ 25 26 def __init__(self, data): 27 # Dictionary of data blobs with keys of <id, api_key> pairs that map 28 # to the data e.g. for devices, the values are the device dicts, for 29 # registration tickets, the values are the ticket dicts. 30 self._data = data 31 32 33 def get_data_val(self, id, api_key): 34 """Returns the data value for the given id, api_key pair. 35 36 @param id: ID for data val. 37 @param api_key: optional api_key for the data_val. 38 39 Raises: 40 server_errors.HTTPError if the data_val doesn't exist. 41 """ 42 key = (id, api_key) 43 data_val = self._data.get(key) 44 if not data_val: 45 # Put the tuple we want inside another tuple, so that Python doesn't 46 # unroll |key| and complain that we haven't asked to printf two 47 # values. 48 raise server_errors.HTTPError(400, 'Invalid data key: %r' % (key,)) 49 return data_val 50 51 52 def get_data_vals(self): 53 """Returns a list of all data values.""" 54 return self._data.values() 55 56 57 def del_data_val(self, id, api_key): 58 """Deletes the data value for the given id, api_key pair. 59 60 @param id: ID for data val. 61 @param api_key: optional api_key for the data_val. 62 63 Raises: 64 server_errors.HTTPError if the data_val doesn't exist. 65 """ 66 key = (id, api_key) 67 if key not in self._data: 68 # Put the tuple we want inside another tuple, so that Python doesn't 69 # unroll |key| and complain that we haven't asked to printf two 70 # values. 71 raise server_errors.HTTPError(400, 'Invalid data key: %r' % (key,)) 72 del self._data[key] 73 74 75 def update_data_val(self, id, api_key, data_in=None, update=True): 76 """Helper method for all mutations to data vals. 77 78 If the id isn't given, creates a new template default with a new id. 79 Otherwise updates/replaces the given dict with the data based on update. 80 81 @param id: id (if None, creates a new data val). 82 @param api_key: optional api_key. 83 @param data_in: data dictionary to either update or replace current. 84 @param update: fully replace data_val given by id, api_key with data_in. 85 86 Raises: 87 server_errors.HTTPError if the id is non-None and not in self._data. 88 """ 89 data_val = None 90 if not id: 91 # This is an insertion. 92 if not data_in: 93 raise ValueError('Either id OR data_in must be specified.') 94 95 # Create a new id and insert the data blob into our dictionary. 96 id = uuid.uuid4().hex[0:6] 97 data_in['id'] = id 98 self._data[(id, api_key)] = data_in 99 return data_in 100 101 data_val = self.get_data_val(id, api_key) 102 if not data_in: 103 logging.warning('Received empty data update. Doing nothing.') 104 return data_val 105 106 # Update or replace the existing data val. 107 if update: 108 data_val.update(data_in) 109 else: 110 if data_val.get('id') != data_in.get('id'): 111 raise server_errors.HTTPError(400, "Ticket id doesn't match") 112 113 data_val = data_in 114 self._data[(id, api_key)] = data_in 115 116 return data_val 117