hivemind-sqlite-database 0.0.3a1__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hivemind_sqlite_database-0.2.1/PKG-INFO +13 -0
- {hivemind-sqlite-database-0.0.3a1 → hivemind_sqlite_database-0.2.1}/hivemind_sqlite_database/__init__.py +27 -9
- hivemind_sqlite_database-0.2.1/hivemind_sqlite_database/version.py +8 -0
- hivemind_sqlite_database-0.2.1/hivemind_sqlite_database.egg-info/PKG-INFO +13 -0
- {hivemind-sqlite-database-0.0.3a1 → hivemind_sqlite_database-0.2.1}/hivemind_sqlite_database.egg-info/SOURCES.txt +3 -2
- hivemind_sqlite_database-0.2.1/hivemind_sqlite_database.egg-info/entry_points.txt +2 -0
- {hivemind-sqlite-database-0.0.3a1 → hivemind_sqlite_database-0.2.1}/hivemind_sqlite_database.egg-info/requires.txt +2 -0
- hivemind_sqlite_database-0.2.1/pyproject.toml +33 -0
- hivemind_sqlite_database-0.2.1/tests/test_sqlitedb.py +306 -0
- hivemind-sqlite-database-0.0.3a1/PKG-INFO +0 -10
- hivemind-sqlite-database-0.0.3a1/hivemind_sqlite_database/version.py +0 -6
- hivemind-sqlite-database-0.0.3a1/hivemind_sqlite_database.egg-info/PKG-INFO +0 -10
- hivemind-sqlite-database-0.0.3a1/hivemind_sqlite_database.egg-info/entry_points.txt +0 -3
- hivemind-sqlite-database-0.0.3a1/setup.py +0 -55
- {hivemind-sqlite-database-0.0.3a1 → hivemind_sqlite_database-0.2.1}/LICENSE.md +0 -0
- {hivemind-sqlite-database-0.0.3a1 → hivemind_sqlite_database-0.2.1}/README.md +0 -0
- {hivemind-sqlite-database-0.0.3a1 → hivemind_sqlite_database-0.2.1}/hivemind_sqlite_database.egg-info/dependency_links.txt +0 -0
- {hivemind-sqlite-database-0.0.3a1 → hivemind_sqlite_database-0.2.1}/hivemind_sqlite_database.egg-info/top_level.txt +0 -0
- {hivemind-sqlite-database-0.0.3a1 → hivemind_sqlite_database-0.2.1}/setup.cfg +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hivemind-sqlite-database
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: sqlite database plugin for hivemind-core
|
|
5
|
+
Author-email: jarbasAi <jarbasai@mailfence.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/JarbasHiveMind/hivemind-sqlite-database
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
License-File: LICENSE.md
|
|
10
|
+
Requires-Dist: hivemind-plugin-manager<1.0.0,>=0.1.0
|
|
11
|
+
Requires-Dist: hivemind-bus-client
|
|
12
|
+
Requires-Dist: ovos-utils
|
|
13
|
+
Dynamic: license-file
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os.path
|
|
3
3
|
import sqlite3
|
|
4
|
+
import threading
|
|
4
5
|
from typing import List, Union, Iterable
|
|
5
6
|
|
|
6
7
|
from ovos_utils.log import LOG
|
|
@@ -10,6 +11,13 @@ from hivemind_plugin_manager.database import Client, AbstractDB
|
|
|
10
11
|
|
|
11
12
|
from dataclasses import dataclass
|
|
12
13
|
|
|
14
|
+
_VALID_COLUMNS = frozenset({
|
|
15
|
+
"client_id", "api_key", "name", "description", "is_admin",
|
|
16
|
+
"last_seen", "intent_blacklist", "skill_blacklist", "message_blacklist",
|
|
17
|
+
"allowed_types", "crypto_key", "password",
|
|
18
|
+
"can_broadcast", "can_escalate", "can_propagate",
|
|
19
|
+
})
|
|
20
|
+
|
|
13
21
|
|
|
14
22
|
@dataclass
|
|
15
23
|
class SQLiteDB(AbstractDB):
|
|
@@ -25,8 +33,10 @@ class SQLiteDB(AbstractDB):
|
|
|
25
33
|
LOG.debug(f"sqlite database path: {db_path}")
|
|
26
34
|
os.makedirs(os.path.dirname(db_path), exist_ok=True)
|
|
27
35
|
|
|
28
|
-
self.conn = sqlite3.connect(db_path)
|
|
36
|
+
self.conn = sqlite3.connect(db_path, check_same_thread=False)
|
|
29
37
|
self.conn.row_factory = sqlite3.Row
|
|
38
|
+
self.conn.execute("PRAGMA journal_mode=WAL")
|
|
39
|
+
self._write_lock = threading.Lock()
|
|
30
40
|
self._initialize_database()
|
|
31
41
|
|
|
32
42
|
def _initialize_database(self):
|
|
@@ -65,7 +75,7 @@ class SQLiteDB(AbstractDB):
|
|
|
65
75
|
True if the addition was successful, False otherwise.
|
|
66
76
|
"""
|
|
67
77
|
try:
|
|
68
|
-
with self.conn:
|
|
78
|
+
with self._write_lock, self.conn:
|
|
69
79
|
self.conn.execute("""
|
|
70
80
|
INSERT OR REPLACE INTO clients (
|
|
71
81
|
client_id, api_key, name, description, is_admin,
|
|
@@ -99,6 +109,9 @@ class SQLiteDB(AbstractDB):
|
|
|
99
109
|
Returns:
|
|
100
110
|
A list of clients that match the search criteria.
|
|
101
111
|
"""
|
|
112
|
+
if key not in _VALID_COLUMNS:
|
|
113
|
+
LOG.error(f"Invalid search key: {key!r}")
|
|
114
|
+
return []
|
|
102
115
|
try:
|
|
103
116
|
with self.conn:
|
|
104
117
|
cur = self.conn.execute(f"SELECT * FROM clients WHERE {key} = ?", (val,))
|
|
@@ -110,8 +123,12 @@ class SQLiteDB(AbstractDB):
|
|
|
110
123
|
|
|
111
124
|
def __len__(self) -> int:
|
|
112
125
|
"""Get the number of clients in the database."""
|
|
113
|
-
|
|
114
|
-
|
|
126
|
+
try:
|
|
127
|
+
cur = self.conn.execute("SELECT COUNT(*) FROM clients")
|
|
128
|
+
return cur.fetchone()[0]
|
|
129
|
+
except sqlite3.Error as e:
|
|
130
|
+
LOG.error(f"Failed to count clients in SQLite: {e}")
|
|
131
|
+
return 0
|
|
115
132
|
|
|
116
133
|
def __iter__(self) -> Iterable['Client']:
|
|
117
134
|
"""
|
|
@@ -127,7 +144,8 @@ class SQLiteDB(AbstractDB):
|
|
|
127
144
|
def commit(self) -> bool:
|
|
128
145
|
"""Commit changes to the SQLite database."""
|
|
129
146
|
try:
|
|
130
|
-
self.
|
|
147
|
+
with self._write_lock:
|
|
148
|
+
self.conn.commit()
|
|
131
149
|
return True
|
|
132
150
|
except sqlite3.Error as e:
|
|
133
151
|
LOG.error(f"Failed to commit SQLite database: {e}")
|
|
@@ -141,7 +159,7 @@ class SQLiteDB(AbstractDB):
|
|
|
141
159
|
api_key=row["api_key"],
|
|
142
160
|
name=row["name"],
|
|
143
161
|
description=row["description"],
|
|
144
|
-
is_admin=row["is_admin"]
|
|
162
|
+
is_admin=bool(row["is_admin"]),
|
|
145
163
|
last_seen=row["last_seen"],
|
|
146
164
|
intent_blacklist=json.loads(row["intent_blacklist"] or "[]"),
|
|
147
165
|
skill_blacklist=json.loads(row["skill_blacklist"] or "[]"),
|
|
@@ -149,7 +167,7 @@ class SQLiteDB(AbstractDB):
|
|
|
149
167
|
allowed_types=json.loads(row["allowed_types"] or "[]"),
|
|
150
168
|
crypto_key=row["crypto_key"],
|
|
151
169
|
password=row["password"],
|
|
152
|
-
can_broadcast=row["can_broadcast"],
|
|
153
|
-
can_escalate=row["can_escalate"],
|
|
154
|
-
can_propagate=row["can_propagate"]
|
|
170
|
+
can_broadcast=bool(row["can_broadcast"]),
|
|
171
|
+
can_escalate=bool(row["can_escalate"]),
|
|
172
|
+
can_propagate=bool(row["can_propagate"])
|
|
155
173
|
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hivemind-sqlite-database
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: sqlite database plugin for hivemind-core
|
|
5
|
+
Author-email: jarbasAi <jarbasai@mailfence.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/JarbasHiveMind/hivemind-sqlite-database
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
License-File: LICENSE.md
|
|
10
|
+
Requires-Dist: hivemind-plugin-manager<1.0.0,>=0.1.0
|
|
11
|
+
Requires-Dist: hivemind-bus-client
|
|
12
|
+
Requires-Dist: ovos-utils
|
|
13
|
+
Dynamic: license-file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
LICENSE.md
|
|
2
2
|
README.md
|
|
3
|
-
|
|
3
|
+
pyproject.toml
|
|
4
4
|
hivemind_sqlite_database/__init__.py
|
|
5
5
|
hivemind_sqlite_database/version.py
|
|
6
6
|
hivemind_sqlite_database.egg-info/PKG-INFO
|
|
@@ -8,4 +8,5 @@ hivemind_sqlite_database.egg-info/SOURCES.txt
|
|
|
8
8
|
hivemind_sqlite_database.egg-info/dependency_links.txt
|
|
9
9
|
hivemind_sqlite_database.egg-info/entry_points.txt
|
|
10
10
|
hivemind_sqlite_database.egg-info/requires.txt
|
|
11
|
-
hivemind_sqlite_database.egg-info/top_level.txt
|
|
11
|
+
hivemind_sqlite_database.egg-info/top_level.txt
|
|
12
|
+
tests/test_sqlitedb.py
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "hivemind-sqlite-database"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "sqlite database plugin for hivemind-core"
|
|
9
|
+
license = { text = "Apache-2.0" }
|
|
10
|
+
authors = [{ name = "jarbasAi", email = "jarbasai@mailfence.com" }]
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"hivemind-plugin-manager>=0.1.0,<1.0.0",
|
|
14
|
+
"hivemind-bus-client",
|
|
15
|
+
"ovos-utils",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
[project.urls]
|
|
20
|
+
Homepage = "https://github.com/JarbasHiveMind/hivemind-sqlite-database"
|
|
21
|
+
|
|
22
|
+
[project.entry-points."hivemind.database"]
|
|
23
|
+
"hivemind-sqlite-db-plugin" = "hivemind_sqlite_database:SQLiteDB"
|
|
24
|
+
|
|
25
|
+
[tool.setuptools.dynamic]
|
|
26
|
+
version = {attr = "hivemind_sqlite_database.version.__version__"}
|
|
27
|
+
|
|
28
|
+
[tool.setuptools.packages.find]
|
|
29
|
+
where = ["."]
|
|
30
|
+
include = ["hivemind_sqlite_database*"]
|
|
31
|
+
|
|
32
|
+
[tool.pytest.ini_options]
|
|
33
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for SQLiteDB using an in-memory database.
|
|
3
|
+
No external services required.
|
|
4
|
+
"""
|
|
5
|
+
import sqlite3
|
|
6
|
+
import tempfile
|
|
7
|
+
import threading
|
|
8
|
+
import os
|
|
9
|
+
import unittest
|
|
10
|
+
|
|
11
|
+
from hivemind_plugin_manager.database import Client
|
|
12
|
+
from hivemind_sqlite_database import SQLiteDB
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def make_db() -> SQLiteDB:
|
|
16
|
+
"""Return a fresh in-memory SQLiteDB instance."""
|
|
17
|
+
db = object.__new__(SQLiteDB)
|
|
18
|
+
db.name = "clients"
|
|
19
|
+
db.subfolder = "hivemind-core"
|
|
20
|
+
db.conn = sqlite3.connect(":memory:", check_same_thread=False)
|
|
21
|
+
db.conn.row_factory = sqlite3.Row
|
|
22
|
+
db.conn.execute("PRAGMA journal_mode=WAL")
|
|
23
|
+
db._write_lock = threading.Lock()
|
|
24
|
+
db._initialize_database()
|
|
25
|
+
return db
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def make_client(client_id: int = 1, api_key: str = "key-abc", **kwargs) -> Client:
|
|
29
|
+
return Client(client_id=client_id, api_key=api_key, **kwargs)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class TestSQLiteDBWAL(unittest.TestCase):
|
|
33
|
+
def test_wal_mode_pragma_in_source(self):
|
|
34
|
+
"""WAL pragma must be present in __post_init__ source code."""
|
|
35
|
+
import inspect
|
|
36
|
+
from hivemind_sqlite_database import SQLiteDB as _DB
|
|
37
|
+
src = inspect.getsource(_DB.__post_init__)
|
|
38
|
+
self.assertIn("journal_mode=WAL", src)
|
|
39
|
+
|
|
40
|
+
def test_wal_mode_active_on_file_db(self):
|
|
41
|
+
"""WAL mode is confirmed active on a real file-based SQLite DB."""
|
|
42
|
+
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
|
|
43
|
+
path = f.name
|
|
44
|
+
try:
|
|
45
|
+
conn = sqlite3.connect(path, check_same_thread=False)
|
|
46
|
+
conn.row_factory = sqlite3.Row
|
|
47
|
+
conn.execute("PRAGMA journal_mode=WAL")
|
|
48
|
+
cur = conn.execute("PRAGMA journal_mode")
|
|
49
|
+
mode = cur.fetchone()[0]
|
|
50
|
+
conn.close()
|
|
51
|
+
self.assertEqual(mode, "wal")
|
|
52
|
+
finally:
|
|
53
|
+
os.unlink(path)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class TestSQLiteDBLen(unittest.TestCase):
|
|
57
|
+
def test_empty_db_has_len_zero(self):
|
|
58
|
+
db = make_db()
|
|
59
|
+
self.assertEqual(len(db), 0)
|
|
60
|
+
|
|
61
|
+
def test_len_increments_after_add(self):
|
|
62
|
+
db = make_db()
|
|
63
|
+
db.add_item(make_client(1, "k1"))
|
|
64
|
+
db.add_item(make_client(2, "k2"))
|
|
65
|
+
self.assertEqual(len(db), 2)
|
|
66
|
+
|
|
67
|
+
def test_len_includes_revoked_entries(self):
|
|
68
|
+
db = make_db()
|
|
69
|
+
c = make_client(1, "k1")
|
|
70
|
+
db.add_item(c)
|
|
71
|
+
db.delete_item(c)
|
|
72
|
+
self.assertEqual(len(db), 1)
|
|
73
|
+
|
|
74
|
+
def test_len_returns_zero_on_error(self):
|
|
75
|
+
db = make_db()
|
|
76
|
+
db.add_item(make_client(1, "k1"))
|
|
77
|
+
db.conn.close()
|
|
78
|
+
self.assertEqual(len(db), 0)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class TestSQLiteDBAddItem(unittest.TestCase):
|
|
82
|
+
def test_add_returns_true_on_success(self):
|
|
83
|
+
db = make_db()
|
|
84
|
+
result = db.add_item(make_client(1, "key"))
|
|
85
|
+
self.assertTrue(result)
|
|
86
|
+
|
|
87
|
+
def test_add_upserts_existing_client_id(self):
|
|
88
|
+
db = make_db()
|
|
89
|
+
db.add_item(make_client(1, "original"))
|
|
90
|
+
db.add_item(make_client(1, "updated"))
|
|
91
|
+
results = db.search_by_value("api_key", "updated")
|
|
92
|
+
self.assertEqual(len(results), 1)
|
|
93
|
+
self.assertEqual(results[0].api_key, "updated")
|
|
94
|
+
# original key is gone
|
|
95
|
+
self.assertEqual(db.search_by_value("api_key", "original"), [])
|
|
96
|
+
|
|
97
|
+
def test_add_returns_false_on_error(self):
|
|
98
|
+
db = make_db()
|
|
99
|
+
db.conn.close() # closing the connection causes sqlite3.Error on next operation
|
|
100
|
+
result = db.add_item(make_client(1, "key"))
|
|
101
|
+
self.assertFalse(result)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class TestSQLiteDBDeleteItem(unittest.TestCase):
|
|
105
|
+
def test_delete_sets_api_key_to_revoked(self):
|
|
106
|
+
db = make_db()
|
|
107
|
+
c = make_client(1, "live-key")
|
|
108
|
+
db.add_item(c)
|
|
109
|
+
db.delete_item(c)
|
|
110
|
+
results = db.search_by_value("api_key", "revoked")
|
|
111
|
+
self.assertEqual(len(results), 1)
|
|
112
|
+
self.assertEqual(results[0].client_id, 1)
|
|
113
|
+
|
|
114
|
+
def test_delete_does_not_remove_row(self):
|
|
115
|
+
db = make_db()
|
|
116
|
+
c = make_client(1, "live-key")
|
|
117
|
+
db.add_item(c)
|
|
118
|
+
db.delete_item(c)
|
|
119
|
+
self.assertEqual(len(db), 1)
|
|
120
|
+
|
|
121
|
+
def test_original_key_no_longer_searchable_after_delete(self):
|
|
122
|
+
db = make_db()
|
|
123
|
+
c = make_client(1, "live-key")
|
|
124
|
+
db.add_item(c)
|
|
125
|
+
db.delete_item(c)
|
|
126
|
+
self.assertEqual(db.search_by_value("api_key", "live-key"), [])
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class TestSQLiteDBSearchByValue(unittest.TestCase):
|
|
130
|
+
def test_search_by_api_key_returns_client(self):
|
|
131
|
+
db = make_db()
|
|
132
|
+
db.add_item(make_client(1, "my-key"))
|
|
133
|
+
results = db.search_by_value("api_key", "my-key")
|
|
134
|
+
self.assertEqual(len(results), 1)
|
|
135
|
+
self.assertEqual(results[0].api_key, "my-key")
|
|
136
|
+
|
|
137
|
+
def test_search_returns_empty_list_on_no_match(self):
|
|
138
|
+
db = make_db()
|
|
139
|
+
db.add_item(make_client(1, "real-key"))
|
|
140
|
+
self.assertEqual(db.search_by_value("api_key", "ghost"), [])
|
|
141
|
+
|
|
142
|
+
def test_search_by_is_admin_true(self):
|
|
143
|
+
db = make_db()
|
|
144
|
+
db.add_item(make_client(1, "admin-key", is_admin=True))
|
|
145
|
+
db.add_item(make_client(2, "user-key", is_admin=False))
|
|
146
|
+
results = db.search_by_value("is_admin", True)
|
|
147
|
+
self.assertEqual(len(results), 1)
|
|
148
|
+
self.assertTrue(results[0].is_admin)
|
|
149
|
+
|
|
150
|
+
def test_search_by_is_admin_false(self):
|
|
151
|
+
db = make_db()
|
|
152
|
+
db.add_item(make_client(1, "admin-key", is_admin=True))
|
|
153
|
+
db.add_item(make_client(2, "user-key", is_admin=False))
|
|
154
|
+
results = db.search_by_value("is_admin", False)
|
|
155
|
+
self.assertEqual(len(results), 1)
|
|
156
|
+
self.assertFalse(results[0].is_admin)
|
|
157
|
+
|
|
158
|
+
def test_search_by_name(self):
|
|
159
|
+
db = make_db()
|
|
160
|
+
db.add_item(make_client(1, "k1", name="alice"))
|
|
161
|
+
db.add_item(make_client(2, "k2", name="bob"))
|
|
162
|
+
results = db.search_by_value("name", "alice")
|
|
163
|
+
self.assertEqual(len(results), 1)
|
|
164
|
+
self.assertEqual(results[0].name, "alice")
|
|
165
|
+
|
|
166
|
+
def test_search_by_nonexistent_name_returns_empty(self):
|
|
167
|
+
db = make_db()
|
|
168
|
+
db.add_item(make_client(1, "k1", name="alice"))
|
|
169
|
+
self.assertEqual(db.search_by_value("name", "ghost"), [])
|
|
170
|
+
|
|
171
|
+
def test_search_returns_empty_on_sqlite_error(self):
|
|
172
|
+
db = make_db()
|
|
173
|
+
db.add_item(make_client(1, "k1"))
|
|
174
|
+
db.conn.close()
|
|
175
|
+
results = db.search_by_value("api_key", "k1")
|
|
176
|
+
self.assertEqual(results, [])
|
|
177
|
+
|
|
178
|
+
def test_search_rejects_invalid_column_name(self):
|
|
179
|
+
db = make_db()
|
|
180
|
+
db.add_item(make_client(1, "k1"))
|
|
181
|
+
results = db.search_by_value("1=1; DROP TABLE clients; --", "x")
|
|
182
|
+
self.assertEqual(results, [])
|
|
183
|
+
# table must still exist
|
|
184
|
+
self.assertEqual(len(db), 1)
|
|
185
|
+
|
|
186
|
+
def test_search_rejects_unknown_column(self):
|
|
187
|
+
db = make_db()
|
|
188
|
+
results = db.search_by_value("nonexistent_column", "value")
|
|
189
|
+
self.assertEqual(results, [])
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class TestSQLiteDBIter(unittest.TestCase):
|
|
193
|
+
def test_iter_yields_all_rows(self):
|
|
194
|
+
db = make_db()
|
|
195
|
+
db.add_item(make_client(1, "k1"))
|
|
196
|
+
db.add_item(make_client(2, "k2"))
|
|
197
|
+
all_clients = list(db)
|
|
198
|
+
self.assertEqual(len(all_clients), 2)
|
|
199
|
+
|
|
200
|
+
def test_iter_includes_revoked_entries(self):
|
|
201
|
+
db = make_db()
|
|
202
|
+
c = make_client(1, "k1")
|
|
203
|
+
db.add_item(c)
|
|
204
|
+
db.delete_item(c)
|
|
205
|
+
all_clients = list(db)
|
|
206
|
+
self.assertEqual(len(all_clients), 1)
|
|
207
|
+
self.assertEqual(all_clients[0].api_key, "revoked")
|
|
208
|
+
|
|
209
|
+
def test_iter_yields_client_instances(self):
|
|
210
|
+
db = make_db()
|
|
211
|
+
db.add_item(make_client(1, "k1"))
|
|
212
|
+
for client in db:
|
|
213
|
+
self.assertIsInstance(client, Client)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class TestSQLiteDBRoundTrip(unittest.TestCase):
|
|
217
|
+
def test_list_fields_survive_round_trip(self):
|
|
218
|
+
db = make_db()
|
|
219
|
+
intent_bl = ["skill:action1", "skill:action2"]
|
|
220
|
+
allowed = ["recognizer_loop:utterance", "speak:b64_audio"]
|
|
221
|
+
c = make_client(1, "k1",
|
|
222
|
+
intent_blacklist=intent_bl,
|
|
223
|
+
allowed_types=allowed)
|
|
224
|
+
db.add_item(c)
|
|
225
|
+
results = db.search_by_value("api_key", "k1")
|
|
226
|
+
self.assertEqual(len(results), 1)
|
|
227
|
+
self.assertEqual(results[0].intent_blacklist, intent_bl)
|
|
228
|
+
self.assertEqual(results[0].allowed_types, allowed)
|
|
229
|
+
|
|
230
|
+
def test_boolean_fields_remain_bool_after_round_trip(self):
|
|
231
|
+
db = make_db()
|
|
232
|
+
db.add_item(make_client(1, "k1", is_admin=True, can_broadcast=False))
|
|
233
|
+
results = db.search_by_value("api_key", "k1")
|
|
234
|
+
self.assertIs(type(results[0].is_admin), bool)
|
|
235
|
+
self.assertTrue(results[0].is_admin)
|
|
236
|
+
self.assertIs(type(results[0].can_broadcast), bool)
|
|
237
|
+
self.assertFalse(results[0].can_broadcast)
|
|
238
|
+
|
|
239
|
+
def test_full_client_fields_preserved(self):
|
|
240
|
+
db = make_db()
|
|
241
|
+
c = Client(
|
|
242
|
+
client_id=42,
|
|
243
|
+
api_key="full-key",
|
|
244
|
+
name="test-client",
|
|
245
|
+
description="a test",
|
|
246
|
+
is_admin=False,
|
|
247
|
+
last_seen=1234567890.0,
|
|
248
|
+
intent_blacklist=["a:b"],
|
|
249
|
+
skill_blacklist=["c:d"],
|
|
250
|
+
message_blacklist=["e:f"],
|
|
251
|
+
allowed_types=["recognizer_loop:utterance"],
|
|
252
|
+
crypto_key="1234567890123456",
|
|
253
|
+
password="secret",
|
|
254
|
+
can_broadcast=True,
|
|
255
|
+
can_escalate=False,
|
|
256
|
+
can_propagate=True,
|
|
257
|
+
)
|
|
258
|
+
db.add_item(c)
|
|
259
|
+
results = db.search_by_value("api_key", "full-key")
|
|
260
|
+
self.assertEqual(len(results), 1)
|
|
261
|
+
r = results[0]
|
|
262
|
+
self.assertEqual(r.client_id, 42)
|
|
263
|
+
self.assertEqual(r.name, "test-client")
|
|
264
|
+
self.assertEqual(r.description, "a test")
|
|
265
|
+
self.assertFalse(r.is_admin)
|
|
266
|
+
self.assertEqual(r.last_seen, 1234567890.0)
|
|
267
|
+
self.assertEqual(r.crypto_key, "1234567890123456")
|
|
268
|
+
self.assertEqual(r.password, "secret")
|
|
269
|
+
self.assertFalse(r.can_escalate)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class TestSQLiteDBCommit(unittest.TestCase):
|
|
273
|
+
def test_commit_returns_true(self):
|
|
274
|
+
db = make_db()
|
|
275
|
+
self.assertTrue(db.commit())
|
|
276
|
+
|
|
277
|
+
def test_commit_returns_false_on_error(self):
|
|
278
|
+
db = make_db()
|
|
279
|
+
db.conn.close()
|
|
280
|
+
result = db.commit()
|
|
281
|
+
self.assertFalse(result)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
class TestSQLiteDBUpdateAndReplace(unittest.TestCase):
|
|
285
|
+
def test_update_item_changes_fields(self):
|
|
286
|
+
db = make_db()
|
|
287
|
+
db.add_item(make_client(1, "k1", name="old"))
|
|
288
|
+
updated = make_client(1, "k1", name="new")
|
|
289
|
+
db.update_item(updated)
|
|
290
|
+
results = db.search_by_value("name", "new")
|
|
291
|
+
self.assertEqual(len(results), 1)
|
|
292
|
+
self.assertEqual(results[0].name, "new")
|
|
293
|
+
|
|
294
|
+
def test_replace_item(self):
|
|
295
|
+
db = make_db()
|
|
296
|
+
old = make_client(1, "old-key")
|
|
297
|
+
new = make_client(2, "new-key")
|
|
298
|
+
db.add_item(old)
|
|
299
|
+
db.replace_item(old, new)
|
|
300
|
+
self.assertEqual(db.search_by_value("api_key", "new-key")[0].client_id, 2)
|
|
301
|
+
# old entry is revoked, not gone
|
|
302
|
+
self.assertEqual(db.search_by_value("api_key", "revoked")[0].client_id, 1)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
if __name__ == "__main__":
|
|
306
|
+
unittest.main()
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 1.0
|
|
2
|
-
Name: hivemind-sqlite-database
|
|
3
|
-
Version: 0.0.3a1
|
|
4
|
-
Summary: sqlite database plugin for hivemind-core
|
|
5
|
-
Home-page: https://github.com/JarbasHiveMind/hivemind-sqlite-database
|
|
6
|
-
Author: jarbasAi
|
|
7
|
-
Author-email: jarbasai@mailfence.com
|
|
8
|
-
License: Apache-2.0
|
|
9
|
-
Description: UNKNOWN
|
|
10
|
-
Platform: UNKNOWN
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 1.0
|
|
2
|
-
Name: hivemind-sqlite-database
|
|
3
|
-
Version: 0.0.3a1
|
|
4
|
-
Summary: sqlite database plugin for hivemind-core
|
|
5
|
-
Home-page: https://github.com/JarbasHiveMind/hivemind-sqlite-database
|
|
6
|
-
Author: jarbasAi
|
|
7
|
-
Author-email: jarbasai@mailfence.com
|
|
8
|
-
License: Apache-2.0
|
|
9
|
-
Description: UNKNOWN
|
|
10
|
-
Platform: UNKNOWN
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from setuptools import setup
|
|
3
|
-
|
|
4
|
-
BASEDIR = os.path.abspath(os.path.dirname(__file__))
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def get_version():
|
|
8
|
-
""" Find the version of the package"""
|
|
9
|
-
version_file = os.path.join(BASEDIR, 'hivemind_sqlite_database', 'version.py')
|
|
10
|
-
major, minor, build, alpha = (None, None, None, None)
|
|
11
|
-
with open(version_file) as f:
|
|
12
|
-
for line in f:
|
|
13
|
-
if 'VERSION_MAJOR' in line:
|
|
14
|
-
major = line.split('=')[1].strip()
|
|
15
|
-
elif 'VERSION_MINOR' in line:
|
|
16
|
-
minor = line.split('=')[1].strip()
|
|
17
|
-
elif 'VERSION_BUILD' in line:
|
|
18
|
-
build = line.split('=')[1].strip()
|
|
19
|
-
elif 'VERSION_ALPHA' in line:
|
|
20
|
-
alpha = line.split('=')[1].strip()
|
|
21
|
-
|
|
22
|
-
if ((major and minor and build and alpha) or
|
|
23
|
-
'# END_VERSION_BLOCK' in line):
|
|
24
|
-
break
|
|
25
|
-
version = f"{major}.{minor}.{build}"
|
|
26
|
-
if int(alpha) > 0:
|
|
27
|
-
version += f"a{alpha}"
|
|
28
|
-
return version
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def required(requirements_file):
|
|
32
|
-
""" Read requirements file and remove comments and empty lines. """
|
|
33
|
-
with open(os.path.join(BASEDIR, requirements_file), 'r') as f:
|
|
34
|
-
requirements = f.read().splitlines()
|
|
35
|
-
if 'MYCROFT_LOOSE_REQUIREMENTS' in os.environ:
|
|
36
|
-
print('USING LOOSE REQUIREMENTS!')
|
|
37
|
-
requirements = [r.replace('==', '>=').replace('~=', '>=') for r in requirements]
|
|
38
|
-
return [pkg for pkg in requirements
|
|
39
|
-
if pkg.strip() and not pkg.startswith("#")]
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
PLUGIN_ENTRY_POINT = 'hivemind-sqlite-db-plugin=hivemind_sqlite_database:SQLiteDB'
|
|
43
|
-
|
|
44
|
-
setup(
|
|
45
|
-
name='hivemind-sqlite-database',
|
|
46
|
-
version=get_version(),
|
|
47
|
-
packages=['hivemind_sqlite_database'],
|
|
48
|
-
url='https://github.com/JarbasHiveMind/hivemind-sqlite-database',
|
|
49
|
-
license='Apache-2.0',
|
|
50
|
-
author='jarbasAi',
|
|
51
|
-
install_requires=required("requirements.txt"),
|
|
52
|
-
entry_points={'hivemind.database': PLUGIN_ENTRY_POINT},
|
|
53
|
-
author_email='jarbasai@mailfence.com',
|
|
54
|
-
description='sqlite database plugin for hivemind-core'
|
|
55
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|