Test Failed
Push — master ( e380d0...f5671d )
by W
02:58
created

st2common/st2common/services/keyvalues.py (2 issues)

1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
from __future__ import absolute_import
17
from st2common import log as logging
18
19
from st2common.constants.keyvalue import SYSTEM_SCOPE, FULL_SYSTEM_SCOPE
20
from st2common.constants.keyvalue import USER_SCOPE, FULL_USER_SCOPE
21
from st2common.constants.keyvalue import ALLOWED_SCOPES
22
from st2common.constants.keyvalue import DATASTORE_KEY_SEPARATOR
23
from st2common.exceptions.keyvalue import InvalidScopeException, InvalidUserException
24
from st2common.models.system.keyvalue import UserKeyReference
25
from st2common.persistence.keyvalue import KeyValuePair
26
27
__all__ = [
28
    'get_kvp_for_name',
29
    'get_values_for_names',
30
31
    'KeyValueLookup',
32
    'UserKeyValueLookup'
33
]
34
35
LOG = logging.getLogger(__name__)
36
37
38
def get_kvp_for_name(name):
39
    try:
40
        kvp_db = KeyValuePair.get_by_name(name)
41
    except ValueError:
42
        kvp_db = None
43
44
    return kvp_db
45
46
47
def get_values_for_names(names, default_value=None):
48
    """
49
    Retrieve values for the provided key names (multi get).
50
51
    If a KeyValuePair objects for a particular name doesn't exist, the dictionary will contain
52
    default_value for that name.
53
54
    :rtype: ``dict``
55
    """
56
    result = {}
57
    kvp_dbs = KeyValuePair.get_by_names(names=names)
58
59
    name_to_kvp_db_map = {}
60
    for kvp_db in kvp_dbs:
61
        name_to_kvp_db_map[kvp_db.name] = kvp_db.value
62
63
    for name in names:
64
        result[name] = name_to_kvp_db_map.get(name, default_value)
65
66
    return result
67
68
69
class KeyValueLookup(object):
70
71
    def __init__(self, prefix=None, key_prefix=None, cache=None, scope=FULL_SYSTEM_SCOPE):
72
        if not scope:
73
            scope = FULL_SYSTEM_SCOPE
74
75
        if scope == SYSTEM_SCOPE:
76
            scope = FULL_SYSTEM_SCOPE
77
78
        self._prefix = prefix
79
        self._key_prefix = key_prefix or ''
80
        self._value_cache = cache or {}
81
        self._scope = scope
82
83
    def __str__(self):
84
        return self._value_cache[self._key_prefix]
85
86
    def __int__(self):
87
        return int(float(self))
88
89
    def __float__(self):
90
        return float(str(self))
91
92
    def __getitem__(self, key):
93
        return self._get(key)
94
95
    def __getattr__(self, name):
96
        return self._get(name)
97
98
    def _get(self, name):
99
        # get the value for this key and save in value_cache
100
        if self._key_prefix:
101
            key = '%s.%s' % (self._key_prefix, name)
102
        else:
103
            key = name
104
105
        if self._prefix:
106
            kvp_key = DATASTORE_KEY_SEPARATOR.join([self._prefix, key])
107
        else:
108
            kvp_key = key
109
110
        value = self._get_kv(kvp_key)
111
        self._value_cache[key] = value
112
        # return a KeyValueLookup as response since the lookup may not be complete e.g. if
113
        # the lookup is for 'key_base.key_value' it is likely that the calling code, e.g. Jinja,
114
        # will expect to do a dictionary style lookup for key_base and key_value as subsequent
115
        # calls. Saving the value in cache avoids extra DB calls.
116
        return KeyValueLookup(prefix=self._prefix, key_prefix=key, cache=self._value_cache,
117
                              scope=self._scope)
118
119
    def _get_kv(self, key):
120
        scope = self._scope
121
        LOG.debug('Lookup system kv: scope: %s and key: %s', scope, key)
122
        kvp = KeyValuePair.get_by_scope_and_name(scope=scope, name=key)
123
        if kvp:
124
            LOG.debug('Got value %s from datastore.', kvp.value)
125
        return kvp.value if kvp else ''
126
127
128
class UserKeyValueLookup(object):
129
130
    def __init__(self, user, prefix=None, key_prefix=None, cache=None, scope=FULL_USER_SCOPE):
131
        if not scope:
132
            scope = FULL_USER_SCOPE
133
134
        if scope == USER_SCOPE:
135
            scope = FULL_USER_SCOPE
136
137
        self._prefix = prefix
138
        self._key_prefix = key_prefix or ''
139
        self._value_cache = cache or {}
140
        self._user = user
141
        self._scope = scope
142
143
    def __str__(self):
144
        return self._value_cache[self._key_prefix]
145
146
    def __getitem__(self, key):
147
        return self._get(key)
148
149
    def __getattr__(self, name):
150
        return self._get(name)
151
152
    def _get(self, name):
153
        # get the value for this key and save in value_cache
154
        if self._key_prefix:
155
            key = '%s.%s' % (self._key_prefix, name)
156
        else:
157
            key = UserKeyReference(name=name, user=self._user).ref
158
159
        if self._prefix:
160
            kvp_key = DATASTORE_KEY_SEPARATOR.join([self._prefix, key])
161
        else:
162
            kvp_key = key
163
164
        value = self._get_kv(kvp_key)
165
        self._value_cache[key] = value
166
        # return a KeyValueLookup as response since the lookup may not be complete e.g. if
167
        # the lookup is for 'key_base.key_value' it is likely that the calling code, e.g. Jinja,
168
        # will expect to do a dictionary style lookup for key_base and key_value as subsequent
169
        # calls. Saving the value in cache avoids extra DB calls.
170
        return UserKeyValueLookup(prefix=self._prefix, user=self._user, key_prefix=key,
171
                                  cache=self._value_cache, scope=self._scope)
172
173
    def _get_kv(self, key):
174
        scope = self._scope
175
        kvp = KeyValuePair.get_by_scope_and_name(scope=scope, name=key)
176
        return kvp.value if kvp else ''
177
178
179
def get_key_reference(scope, name, user=None):
180
    """
181
    Given a key name and user this method returns a new name (string ref)
182
    to address the key value pair in the context of that user.
183
184
    :param user: User to whom key belongs.
185
    :type user: ``str``
186
187
    :param name: Original name of the key.
188
    :type name: ``str``
189
190
    :rtype: ``str``
191
    """
192
    if (scope == SYSTEM_SCOPE or scope == FULL_SYSTEM_SCOPE):
0 ignored issues
show
Unused Code Coding Style introduced by
There is an unnecessary parenthesis after if.
Loading history...
193
        return name
194
    elif (scope == USER_SCOPE or scope == FULL_USER_SCOPE):
0 ignored issues
show
Unused Code Coding Style introduced by
There is an unnecessary parenthesis after elif.
Loading history...
195
        if not user:
196
            raise InvalidUserException('A valid user must be specified for user key ref.')
197
        return UserKeyReference(name=name, user=user).ref
198
    else:
199
        raise InvalidScopeException('Scope "%s" is not valid. Allowed scopes are %s.' %
200
                                    (scope, ALLOWED_SCOPES))
201