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