blob: b796e81c21d9cec2dff0a98c5572834a9d65aa20 [file] [log] [blame]
tierno87858ca2018-10-08 16:30:15 +02001# -*- coding: utf-8 -*-
2
3# Copyright 2018 Telefonica S.A.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14# implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17
tierno5c012612018-04-19 16:01:59 +020018import logging
tierno3054f782018-04-25 16:59:53 +020019from osm_common.dbbase import DbException, DbBase
tierno5c012612018-04-19 16:01:59 +020020from http import HTTPStatus
21from uuid import uuid4
22from copy import deepcopy
23
24__author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
25
26
27class DbMemory(DbBase):
28
tierno1e9a3292018-11-05 18:18:45 +010029 def __init__(self, logger_name='db', lock=False):
30 super().__init__(logger_name, lock)
tierno5c012612018-04-19 16:01:59 +020031 self.db = {}
32
33 def db_connect(self, config):
tierno87858ca2018-10-08 16:30:15 +020034 """
35 Connect to database
36 :param config: Configuration of database
37 :return: None or raises DbException on error
38 """
tierno5c012612018-04-19 16:01:59 +020039 if "logger_name" in config:
40 self.logger = logging.getLogger(config["logger_name"])
tiernocfc52722018-10-23 11:41:49 +020041 self.master_password = config.get("masterpassword")
tierno5c012612018-04-19 16:01:59 +020042
43 @staticmethod
tierno87858ca2018-10-08 16:30:15 +020044 def _format_filter(q_filter):
45 return q_filter # TODO
tierno5c012612018-04-19 16:01:59 +020046
tierno87858ca2018-10-08 16:30:15 +020047 def _find(self, table, q_filter):
tierno5c012612018-04-19 16:01:59 +020048 for i, row in enumerate(self.db.get(table, ())):
49 match = True
tierno87858ca2018-10-08 16:30:15 +020050 if q_filter:
51 for k, v in q_filter.items():
tierno5c012612018-04-19 16:01:59 +020052 if k not in row or v != row[k]:
53 match = False
54 if match:
55 yield i, row
56
tierno87858ca2018-10-08 16:30:15 +020057 def get_list(self, table, q_filter=None):
58 """
59 Obtain a list of entries matching q_filter
60 :param table: collection or table
61 :param q_filter: Filter
62 :return: a list (can be empty) with the found entries. Raises DbException on error
63 """
tierno5c012612018-04-19 16:01:59 +020064 try:
tiernob20a9022018-05-22 12:07:05 +020065 result = []
tierno1e9a3292018-11-05 18:18:45 +010066 with self.lock:
67 for _, row in self._find(table, self._format_filter(q_filter)):
68 result.append(deepcopy(row))
tiernob20a9022018-05-22 12:07:05 +020069 return result
tierno5c012612018-04-19 16:01:59 +020070 except DbException:
71 raise
72 except Exception as e: # TODO refine
73 raise DbException(str(e))
74
tierno87858ca2018-10-08 16:30:15 +020075 def get_one(self, table, q_filter=None, fail_on_empty=True, fail_on_more=True):
76 """
77 Obtain one entry matching q_filter
78 :param table: collection or table
79 :param q_filter: Filter
80 :param fail_on_empty: If nothing matches filter it returns None unless this flag is set tu True, in which case
81 it raises a DbException
82 :param fail_on_more: If more than one matches filter it returns one of then unless this flag is set tu True, so
83 that it raises a DbException
84 :return: The requested element, or None
85 """
tierno5c012612018-04-19 16:01:59 +020086 try:
tiernob20a9022018-05-22 12:07:05 +020087 result = None
tierno1e9a3292018-11-05 18:18:45 +010088 with self.lock:
89 for _, row in self._find(table, self._format_filter(q_filter)):
90 if not fail_on_more:
91 return deepcopy(row)
92 if result:
93 raise DbException("Found more than one entry with filter='{}'".format(q_filter),
94 HTTPStatus.CONFLICT.value)
95 result = row
tiernob20a9022018-05-22 12:07:05 +020096 if not result and fail_on_empty:
tierno87858ca2018-10-08 16:30:15 +020097 raise DbException("Not found entry with filter='{}'".format(q_filter), HTTPStatus.NOT_FOUND)
tiernob20a9022018-05-22 12:07:05 +020098 return deepcopy(result)
tierno5c012612018-04-19 16:01:59 +020099 except Exception as e: # TODO refine
100 raise DbException(str(e))
101
tierno87858ca2018-10-08 16:30:15 +0200102 def del_list(self, table, q_filter=None):
103 """
104 Deletes all entries that match q_filter
105 :param table: collection or table
106 :param q_filter: Filter
107 :return: Dict with the number of entries deleted
108 """
tierno5c012612018-04-19 16:01:59 +0200109 try:
110 id_list = []
tierno1e9a3292018-11-05 18:18:45 +0100111 with self.lock:
112 for i, _ in self._find(table, self._format_filter(q_filter)):
113 id_list.append(i)
tierno5c012612018-04-19 16:01:59 +0200114 deleted = len(id_list)
Eduardo Sousa857731b2018-04-26 15:55:05 +0100115 for i in reversed(id_list):
tierno5c012612018-04-19 16:01:59 +0200116 del self.db[table][i]
117 return {"deleted": deleted}
118 except DbException:
119 raise
120 except Exception as e: # TODO refine
121 raise DbException(str(e))
122
tierno87858ca2018-10-08 16:30:15 +0200123 def del_one(self, table, q_filter=None, fail_on_empty=True):
124 """
125 Deletes one entry that matches q_filter
126 :param table: collection or table
127 :param q_filter: Filter
128 :param fail_on_empty: If nothing matches filter it returns '0' deleted unless this flag is set tu True, in
129 which case it raises a DbException
130 :return: Dict with the number of entries deleted
131 """
tierno5c012612018-04-19 16:01:59 +0200132 try:
tierno1e9a3292018-11-05 18:18:45 +0100133 with self.lock:
134 for i, _ in self._find(table, self._format_filter(q_filter)):
135 break
136 else:
137 if fail_on_empty:
138 raise DbException("Not found entry with filter='{}'".format(q_filter), HTTPStatus.NOT_FOUND)
139 return None
140 del self.db[table][i]
tierno5c012612018-04-19 16:01:59 +0200141 return {"deleted": 1}
142 except Exception as e: # TODO refine
143 raise DbException(str(e))
144
tierno87858ca2018-10-08 16:30:15 +0200145 def replace(self, table, _id, indata, fail_on_empty=True):
146 """
147 Replace the content of an entry
148 :param table: collection or table
149 :param _id: internal database id
150 :param indata: content to replace
151 :param fail_on_empty: If nothing matches filter it returns None unless this flag is set tu True, in which case
152 it raises a DbException
153 :return: Dict with the number of entries replaced
154 """
tierno5c012612018-04-19 16:01:59 +0200155 try:
tierno1e9a3292018-11-05 18:18:45 +0100156 with self.lock:
157 for i, _ in self._find(table, self._format_filter({"_id": _id})):
158 break
159 else:
160 if fail_on_empty:
161 raise DbException("Not found entry with _id='{}'".format(_id), HTTPStatus.NOT_FOUND)
162 return None
163 self.db[table][i] = deepcopy(indata)
Eduardo Sousa22f0fcd2018-04-26 15:43:28 +0100164 return {"updated": 1}
tierno136f2952018-10-19 13:01:03 +0200165 except DbException:
166 raise
tierno5c012612018-04-19 16:01:59 +0200167 except Exception as e: # TODO refine
168 raise DbException(str(e))
169
170 def create(self, table, indata):
tierno87858ca2018-10-08 16:30:15 +0200171 """
172 Add a new entry at database
173 :param table: collection or table
174 :param indata: content to be added
175 :return: database id of the inserted element. Raises a DbException on error
176 """
tierno5c012612018-04-19 16:01:59 +0200177 try:
178 id = indata.get("_id")
179 if not id:
180 id = str(uuid4())
181 indata["_id"] = id
tierno1e9a3292018-11-05 18:18:45 +0100182 with self.lock:
183 if table not in self.db:
184 self.db[table] = []
185 self.db[table].append(deepcopy(indata))
tierno5c012612018-04-19 16:01:59 +0200186 return id
187 except Exception as e: # TODO refine
188 raise DbException(str(e))
189
190
191if __name__ == '__main__':
192 # some test code
tierno3054f782018-04-25 16:59:53 +0200193 db = DbMemory()
tierno5c012612018-04-19 16:01:59 +0200194 db.create("test", {"_id": 1, "data": 1})
195 db.create("test", {"_id": 2, "data": 2})
196 db.create("test", {"_id": 3, "data": 3})
197 print("must be 3 items:", db.get_list("test"))
198 print("must return item 2:", db.get_list("test", {"_id": 2}))
199 db.del_one("test", {"_id": 2})
200 print("must be emtpy:", db.get_list("test", {"_id": 2}))