Completed
Pull Request — master (#2333)
by Arma
06:24 queued 34s
created

get_all()   A

Complexity

Conditions 3

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 19
rs 9.4286
cc 3
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 pecan import abort
17
from pecan.rest import RestController
18
import six
19
from mongoengine import ValidationError
20
21
from st2common import log as logging
22
from st2common.models.api.keyvalue import KeyValuePairAPI
23
from st2common.models.api.base import jsexpose
24
from st2common.persistence.keyvalue import KeyValuePair
25
from st2common.services import coordination
26
27
http_client = six.moves.http_client
28
29
LOG = logging.getLogger(__name__)
30
31
__all__ = [
32
    'KeyValuePairController'
33
]
34
35
36
class KeyValuePairController(RestController):
37
    """
38
    Implements the REST endpoint for managing the key value store.
39
    """
40
41
    # TODO: Port to use ResourceController
42
    def __init__(self):
43
        super(KeyValuePairController, self).__init__()
44
        self._coordinator = coordination.get_coordinator()
45
        self.get_one_db_method = self.__get_by_name
46
47
    @jsexpose(arg_types=[str])
48
    def get_one(self, name):
49
        """
50
            List key by name.
51
52
            Handle:
53
                GET /keys/key1
54
        """
55
        kvp_db = self.__get_by_name(name=name)
56
57
        if not kvp_db:
58
            LOG.exception('Database lookup for name="%s" resulted in exception.', name)
59
            abort(http_client.NOT_FOUND)
60
            return
61
62
        try:
63
            kvp_api = KeyValuePairAPI.from_model(kvp_db)
64
        except (ValidationError, ValueError) as e:
65
            abort(http_client.INTERNAL_SERVER_ERROR, str(e))
66
            return
67
68
        return kvp_api
69
70
    @jsexpose(arg_types=[str])
71
    def get_all(self, **kw):
72
        """
73
            List all keys.
74
75
            Handles requests:
76
                GET /keys/
77
        """
78
        # Prefix filtering
79
        prefix_filter = kw.get('prefix', None)
80
81
        if prefix_filter:
82
            kw['name__startswith'] = prefix_filter
83
            del kw['prefix']
84
85
        kvp_dbs = KeyValuePair.get_all(**kw)
86
        kvps = [KeyValuePairAPI.from_model(kvp_db) for kvp_db in kvp_dbs]
87
88
        return kvps
89
90
    @jsexpose(arg_types=[str, str], body_cls=KeyValuePairAPI)
91
    def put(self, name, kvp):
92
        """
93
        Create a new entry or update an existing one.
94
        """
95
        lock_name = self._get_lock_name_for_key(name=name)
96
97
        # TODO: Custom permission check since the key doesn't need to exist here
98
99
        # Note: We use lock to avoid a race
100
        with self._coordinator.get_lock(lock_name):
101
            existing_kvp = self.__get_by_name(name=name)
102
103
            kvp.name = name
104
105
            try:
106
                kvp_db = KeyValuePairAPI.to_model(kvp)
107
108
                if existing_kvp:
109
                    kvp_db.id = existing_kvp.id
110
111
                kvp_db = KeyValuePair.add_or_update(kvp_db)
112
            except (ValidationError, ValueError) as e:
113
                LOG.exception('Validation failed for key value data=%s', kvp)
114
                abort(http_client.BAD_REQUEST, str(e))
115
                return
116
117
        extra = {'kvp_db': kvp_db}
118
        LOG.audit('KeyValuePair updated. KeyValuePair.id=%s' % (kvp_db.id), extra=extra)
119
120
        kvp_api = KeyValuePairAPI.from_model(kvp_db)
121
        return kvp_api
122
123
    @jsexpose(arg_types=[str], status_code=http_client.NO_CONTENT)
124
    def delete(self, name):
125
        """
126
            Delete the key value pair.
127
128
            Handles requests:
129
                DELETE /keys/1
130
        """
131
        lock_name = self._get_lock_name_for_key(name=name)
132
133
        # Note: We use lock to avoid a race
134
        with self._coordinator.get_lock(lock_name):
135
            kvp_db = self.__get_by_name(name=name)
136
137
            if not kvp_db:
138
                abort(http_client.NOT_FOUND)
139
                return
140
141
            LOG.debug('DELETE /keys/ lookup with name=%s found object: %s', name, kvp_db)
142
143
            try:
144
                KeyValuePair.delete(kvp_db)
145
            except Exception as e:
146
                LOG.exception('Database delete encountered exception during '
147
                              'delete of name="%s". ', name)
148
                abort(http_client.INTERNAL_SERVER_ERROR, str(e))
149
                return
150
151
        extra = {'kvp_db': kvp_db}
152
        LOG.audit('KeyValuePair deleted. KeyValuePair.id=%s' % (kvp_db.id), extra=extra)
153
154
    @staticmethod
155
    def __get_by_name(name):
156
        try:
157
            return KeyValuePair.get_by_name(name)
158
        except ValueError as e:
159
            LOG.debug('Database lookup for name="%s" resulted in exception : %s.', name, e)
160
            return None
161
162
    def _get_lock_name_for_key(self, name):
163
        """
164
        Retrieve a coordination lock name for the provided datastore item name.
165
166
        :param name: Datastore item name (PK).
167
        :type name: ``str``
168
        """
169
        lock_name = 'kvp-crud-%s' % (name)
170
        return lock_name
171