Passed
Push — master ( 523dd6...561579 )
by
unknown
03:11
created

KeyValueLookup   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 57
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 57
rs 10
c 0
b 0
f 0
wmc 14

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __str__() 0 2 1
A __init__() 0 11 3
A __float__() 0 2 1
A _get() 0 20 3
A __getitem__() 0 2 1
A __getattr__() 0 2 1
A _get_kv() 0 7 3
A __int__() 0 2 1
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 st2common import log as logging
17
18
from st2common.constants.keyvalue import SYSTEM_SCOPE, FULL_SYSTEM_SCOPE
19
from st2common.constants.keyvalue import USER_SCOPE, FULL_USER_SCOPE
20
from st2common.constants.keyvalue import ALLOWED_SCOPES
21
from st2common.constants.keyvalue import DATASTORE_KEY_SEPARATOR
22
from st2common.exceptions.keyvalue import InvalidScopeException, InvalidUserException
23
from st2common.models.system.keyvalue import UserKeyReference
24
from st2common.persistence.keyvalue import KeyValuePair
25
26
__all__ = [
27
    'get_kvp_for_name',
28
    'get_values_for_names',
29
30
    'KeyValueLookup',
31
    'UserKeyValueLookup'
32
]
33
34
LOG = logging.getLogger(__name__)
35
36
37
def get_kvp_for_name(name):
38
    try:
39
        kvp_db = KeyValuePair.get_by_name(name)
40
    except ValueError:
41
        kvp_db = None
42
43
    return kvp_db
44
45
46
def get_values_for_names(names, default_value=None):
47
    """
48
    Retrieve values for the provided key names (multi get).
49
50
    If a KeyValuePair objects for a particular name doesn't exist, the dictionary will contain
51
    default_value for that name.
52
53
    :rtype: ``dict``
54
    """
55
    result = {}
56
    kvp_dbs = KeyValuePair.get_by_names(names=names)
57
58
    name_to_kvp_db_map = {}
59
    for kvp_db in kvp_dbs:
60
        name_to_kvp_db_map[kvp_db.name] = kvp_db.value
61
62
    for name in names:
63
        result[name] = name_to_kvp_db_map.get(name, default_value)
64
65
    return result
66
67
68
class KeyValueLookup(object):
69
70
    def __init__(self, prefix=None, key_prefix=None, cache=None, scope=FULL_SYSTEM_SCOPE):
71
        if not scope:
72
            scope = FULL_SYSTEM_SCOPE
73
74
        if scope == SYSTEM_SCOPE:
75
            scope = FULL_SYSTEM_SCOPE
76
77
        self._prefix = prefix
78
        self._key_prefix = key_prefix or ''
79
        self._value_cache = cache or {}
80
        self._scope = scope
81
82
    def __str__(self):
83
        return self._value_cache[self._key_prefix]
84
85
    def __int__(self):
86
        return int(float(self))
87
88
    def __float__(self):
89
        return float(str(self))
90
91
    def __getitem__(self, key):
92
        return self._get(key)
93
94
    def __getattr__(self, name):
95
        return self._get(name)
96
97
    def _get(self, name):
98
        # get the value for this key and save in value_cache
99
        if self._key_prefix:
100
            key = '%s.%s' % (self._key_prefix, name)
101
        else:
102
            key = name
103
104
        if self._prefix:
105
            kvp_key = DATASTORE_KEY_SEPARATOR.join([self._prefix, key])
106
        else:
107
            kvp_key = key
108
109
        value = self._get_kv(kvp_key)
110
        self._value_cache[key] = value
111
        # return a KeyValueLookup as response since the lookup may not be complete e.g. if
112
        # the lookup is for 'key_base.key_value' it is likely that the calling code, e.g. Jinja,
113
        # will expect to do a dictionary style lookup for key_base and key_value as subsequent
114
        # calls. Saving the value in cache avoids extra DB calls.
115
        return KeyValueLookup(prefix=self._prefix, key_prefix=key, cache=self._value_cache,
116
                              scope=self._scope)
117
118
    def _get_kv(self, key):
119
        scope = self._scope
120
        LOG.debug('Lookup system kv: scope: %s and key: %s', scope, key)
121
        kvp = KeyValuePair.get_by_scope_and_name(scope=scope, name=key)
122
        if kvp:
123
            LOG.debug('Got value %s from datastore.', kvp.value)
124
        return kvp.value if kvp else ''
125
126
127
class UserKeyValueLookup(object):
128
129
    def __init__(self, user, prefix=None, key_prefix=None, cache=None, scope=FULL_USER_SCOPE):
130
        if not scope:
131
            scope = FULL_USER_SCOPE
132
133
        if scope == USER_SCOPE:
134
            scope = FULL_USER_SCOPE
135
136
        self._prefix = prefix
137
        self._key_prefix = key_prefix or ''
138
        self._value_cache = cache or {}
139
        self._user = user
140
        self._scope = scope
141
142
    def __str__(self):
143
        return self._value_cache[self._key_prefix]
144
145
    def __getitem__(self, key):
146
        return self._get(key)
147
148
    def __getattr__(self, name):
149
        return self._get(name)
150
151
    def _get(self, name):
152
        # get the value for this key and save in value_cache
153
        if self._key_prefix:
154
            key = '%s.%s' % (self._key_prefix, name)
155
        else:
156
            key = UserKeyReference(name=name, user=self._user).ref
157
158
        if self._prefix:
159
            kvp_key = DATASTORE_KEY_SEPARATOR.join([self._prefix, key])
160
        else:
161
            kvp_key = key
162
163
        value = self._get_kv(kvp_key)
164
        self._value_cache[key] = value
165
        # return a KeyValueLookup as response since the lookup may not be complete e.g. if
166
        # the lookup is for 'key_base.key_value' it is likely that the calling code, e.g. Jinja,
167
        # will expect to do a dictionary style lookup for key_base and key_value as subsequent
168
        # calls. Saving the value in cache avoids extra DB calls.
169
        return UserKeyValueLookup(prefix=self._prefix, user=self._user, key_prefix=key,
170
                                  cache=self._value_cache, scope=self._scope)
171
172
    def _get_kv(self, key):
173
        scope = self._scope
174
        kvp = KeyValuePair.get_by_scope_and_name(scope=scope, name=key)
175
        return kvp.value if kvp else ''
176
177
178
def get_key_reference(scope, name, user=None):
179
    """
180
    Given a key name and user this method returns a new name (string ref)
181
    to address the key value pair in the context of that user.
182
183
    :param user: User to whom key belongs.
184
    :type name: ``str``
185
186
    :param name: Original name of the key.
187
    :type name: ``str``
188
189
    :rtype: ``str``
190
    """
191
    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...
192
        return name
193
    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...
194
        if not user:
195
            raise InvalidUserException('A valid user must be specified for user key ref.')
196
        return UserKeyReference(name=name, user=user).ref
197
    else:
198
        raise InvalidScopeException('Scope "%s" is not valid. Allowed scopes are %s.' %
199
                                    (scope, ALLOWED_SCOPES))
200