blob: e37d97b5aed3eb22efce834657a54f277905f3b3 [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
tierno87858ca2018-10-08 16:30:15 +020029 def __init__(self, logger_name='db', master_password=None):
30 super().__init__(logger_name, master_password)
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"])
41
42 @staticmethod
tierno87858ca2018-10-08 16:30:15 +020043 def _format_filter(q_filter):
44 return q_filter # TODO
tierno5c012612018-04-19 16:01:59 +020045
tierno87858ca2018-10-08 16:30:15 +020046 def _find(self, table, q_filter):
tierno5c012612018-04-19 16:01:59 +020047 for i, row in enumerate(self.db.get(table, ())):
48 match = True
tierno87858ca2018-10-08 16:30:15 +020049 if q_filter:
50 for k, v in q_filter.items():
tierno5c012612018-04-19 16:01:59 +020051 if k not in row or v != row[k]:
52 match = False
53 if match:
54 yield i, row
55
tierno87858ca2018-10-08 16:30:15 +020056 def get_list(self, table, q_filter=None):
57 """
58 Obtain a list of entries matching q_filter
59 :param table: collection or table
60 :param q_filter: Filter
61 :return: a list (can be empty) with the found entries. Raises DbException on error
62 """
tierno5c012612018-04-19 16:01:59 +020063 try:
tiernob20a9022018-05-22 12:07:05 +020064 result = []
tierno87858ca2018-10-08 16:30:15 +020065 for _, row in self._find(table, self._format_filter(q_filter)):
tiernob20a9022018-05-22 12:07:05 +020066 result.append(deepcopy(row))
67 return result
tierno5c012612018-04-19 16:01:59 +020068 except DbException:
69 raise
70 except Exception as e: # TODO refine
71 raise DbException(str(e))
72
tierno87858ca2018-10-08 16:30:15 +020073 def get_one(self, table, q_filter=None, fail_on_empty=True, fail_on_more=True):
74 """
75 Obtain one entry matching q_filter
76 :param table: collection or table
77 :param q_filter: Filter
78 :param fail_on_empty: If nothing matches filter it returns None unless this flag is set tu True, in which case
79 it raises a DbException
80 :param fail_on_more: If more than one matches filter it returns one of then unless this flag is set tu True, so
81 that it raises a DbException
82 :return: The requested element, or None
83 """
tierno5c012612018-04-19 16:01:59 +020084 try:
tiernob20a9022018-05-22 12:07:05 +020085 result = None
tierno87858ca2018-10-08 16:30:15 +020086 for _, row in self._find(table, self._format_filter(q_filter)):
tierno5c012612018-04-19 16:01:59 +020087 if not fail_on_more:
88 return deepcopy(row)
tiernob20a9022018-05-22 12:07:05 +020089 if result:
tierno87858ca2018-10-08 16:30:15 +020090 raise DbException("Found more than one entry with filter='{}'".format(q_filter),
tierno5c012612018-04-19 16:01:59 +020091 HTTPStatus.CONFLICT.value)
tiernob20a9022018-05-22 12:07:05 +020092 result = row
93 if not result and fail_on_empty:
tierno87858ca2018-10-08 16:30:15 +020094 raise DbException("Not found entry with filter='{}'".format(q_filter), HTTPStatus.NOT_FOUND)
tiernob20a9022018-05-22 12:07:05 +020095 return deepcopy(result)
tierno5c012612018-04-19 16:01:59 +020096 except Exception as e: # TODO refine
97 raise DbException(str(e))
98
tierno87858ca2018-10-08 16:30:15 +020099 def del_list(self, table, q_filter=None):
100 """
101 Deletes all entries that match q_filter
102 :param table: collection or table
103 :param q_filter: Filter
104 :return: Dict with the number of entries deleted
105 """
tierno5c012612018-04-19 16:01:59 +0200106 try:
107 id_list = []
tierno87858ca2018-10-08 16:30:15 +0200108 for i, _ in self._find(table, self._format_filter(q_filter)):
tierno5c012612018-04-19 16:01:59 +0200109 id_list.append(i)
110 deleted = len(id_list)
Eduardo Sousa857731b2018-04-26 15:55:05 +0100111 for i in reversed(id_list):
tierno5c012612018-04-19 16:01:59 +0200112 del self.db[table][i]
113 return {"deleted": deleted}
114 except DbException:
115 raise
116 except Exception as e: # TODO refine
117 raise DbException(str(e))
118
tierno87858ca2018-10-08 16:30:15 +0200119 def del_one(self, table, q_filter=None, fail_on_empty=True):
120 """
121 Deletes one entry that matches q_filter
122 :param table: collection or table
123 :param q_filter: Filter
124 :param fail_on_empty: If nothing matches filter it returns '0' deleted unless this flag is set tu True, in
125 which case it raises a DbException
126 :return: Dict with the number of entries deleted
127 """
tierno5c012612018-04-19 16:01:59 +0200128 try:
tierno87858ca2018-10-08 16:30:15 +0200129 for i, _ in self._find(table, self._format_filter(q_filter)):
tierno5c012612018-04-19 16:01:59 +0200130 break
131 else:
132 if fail_on_empty:
tierno87858ca2018-10-08 16:30:15 +0200133 raise DbException("Not found entry with filter='{}'".format(q_filter), HTTPStatus.NOT_FOUND)
tierno5c012612018-04-19 16:01:59 +0200134 return None
135 del self.db[table][i]
136 return {"deleted": 1}
137 except Exception as e: # TODO refine
138 raise DbException(str(e))
139
tierno87858ca2018-10-08 16:30:15 +0200140 def replace(self, table, _id, indata, fail_on_empty=True):
141 """
142 Replace the content of an entry
143 :param table: collection or table
144 :param _id: internal database id
145 :param indata: content to replace
146 :param fail_on_empty: If nothing matches filter it returns None unless this flag is set tu True, in which case
147 it raises a DbException
148 :return: Dict with the number of entries replaced
149 """
tierno5c012612018-04-19 16:01:59 +0200150 try:
tierno87858ca2018-10-08 16:30:15 +0200151 for i, _ in self._find(table, self._format_filter({"_id": _id})):
tierno5c012612018-04-19 16:01:59 +0200152 break
153 else:
154 if fail_on_empty:
tierno87858ca2018-10-08 16:30:15 +0200155 raise DbException("Not found entry with _id='{}'".format(_id), HTTPStatus.NOT_FOUND)
tierno5c012612018-04-19 16:01:59 +0200156 return None
157 self.db[table][i] = deepcopy(indata)
Eduardo Sousa22f0fcd2018-04-26 15:43:28 +0100158 return {"updated": 1}
tierno5c012612018-04-19 16:01:59 +0200159 except Exception as e: # TODO refine
160 raise DbException(str(e))
161
162 def create(self, table, indata):
tierno87858ca2018-10-08 16:30:15 +0200163 """
164 Add a new entry at database
165 :param table: collection or table
166 :param indata: content to be added
167 :return: database id of the inserted element. Raises a DbException on error
168 """
tierno5c012612018-04-19 16:01:59 +0200169 try:
170 id = indata.get("_id")
171 if not id:
172 id = str(uuid4())
173 indata["_id"] = id
174 if table not in self.db:
175 self.db[table] = []
176 self.db[table].append(deepcopy(indata))
177 return id
178 except Exception as e: # TODO refine
179 raise DbException(str(e))
180
181
182if __name__ == '__main__':
183 # some test code
tierno3054f782018-04-25 16:59:53 +0200184 db = DbMemory()
tierno5c012612018-04-19 16:01:59 +0200185 db.create("test", {"_id": 1, "data": 1})
186 db.create("test", {"_id": 2, "data": 2})
187 db.create("test", {"_id": 3, "data": 3})
188 print("must be 3 items:", db.get_list("test"))
189 print("must return item 2:", db.get_list("test", {"_id": 2}))
190 db.del_one("test", {"_id": 2})
191 print("must be emtpy:", db.get_list("test", {"_id": 2}))