forked from platypush/platypush
- Support for Platypush main configuration db, where plugins and backends can store their data
- Support for permanent cross-process storage of session variables through SQLite db - Support for db.select with table+filter instead of raw SQL query
This commit is contained in:
parent
7114d8bcaa
commit
2fda066e39
3 changed files with 97 additions and 17 deletions
|
@ -67,6 +67,11 @@ class Config(object):
|
|||
self._config['workdir'] = self._workdir_location
|
||||
os.makedirs(self._config['workdir'], exist_ok=True)
|
||||
|
||||
self._config['db'] = self._config.get('main_db', {
|
||||
'engine': 'sqlite:///' + os.path.join(
|
||||
os.environ['HOME'], '.local', 'share', 'platypush', 'main.db')
|
||||
})
|
||||
|
||||
logging_config = {
|
||||
'level': logging.INFO,
|
||||
'stream': sys.stdout,
|
||||
|
|
|
@ -36,6 +36,14 @@ class DbPlugin(Plugin):
|
|||
else:
|
||||
return self.engine
|
||||
|
||||
def _build_condition(self, table, column, value):
|
||||
if isinstance(value, str):
|
||||
value = "'{}'".format(value)
|
||||
elif not isinstance(value, int) and not isinstance(value, 'float'):
|
||||
value = "'{}'".format(str(value))
|
||||
|
||||
return eval('table.c.{}=={}'.format(column, value))
|
||||
|
||||
@action
|
||||
def execute(self, statement, engine=None, *args, **kwargs):
|
||||
"""
|
||||
|
@ -64,19 +72,23 @@ class DbPlugin(Plugin):
|
|||
|
||||
|
||||
@action
|
||||
def select(self, query, engine=None, *args, **kwargs):
|
||||
def select(self, query=None, table=None, filter=None, engine=None, *args, **kwargs):
|
||||
"""
|
||||
Returns rows (as a list of hashes) given a query.
|
||||
|
||||
:param query: SQL to be executed
|
||||
:type query: str
|
||||
:param filter: Query WHERE filter expressed as a dictionary. This approach is preferred over specifying raw SQL in ``query`` as the latter approach may be prone to SQL injection, unless you need to build some complex SQL logic.
|
||||
:type filter: dict
|
||||
:param table: If you specified a filter instead of a raw query, you'll have to specify the target table
|
||||
:type table: str
|
||||
:param engine: Engine to be used (default: default class engine)
|
||||
:type engine: str
|
||||
:param args: Extra arguments that will be passed to ``sqlalchemy.create_engine`` (see http://docs.sqlalchemy.org/en/latest/core/engines.html)
|
||||
:param kwargs: Extra kwargs that will be passed to ``sqlalchemy.create_engine`` (see http://docs.sqlalchemy.org/en/latest/core/engines.html)
|
||||
:returns: List of hashes representing the result rows.
|
||||
|
||||
Example:
|
||||
Examples:
|
||||
|
||||
Request::
|
||||
|
||||
|
@ -90,25 +102,46 @@ class DbPlugin(Plugin):
|
|||
}
|
||||
}
|
||||
|
||||
or::
|
||||
|
||||
{
|
||||
"type": "request",
|
||||
"target": "your_host",
|
||||
"action": "db.select",
|
||||
"args": {
|
||||
"engine": "sqlite:///:memory:",
|
||||
"table": "table",
|
||||
"filter": {"id": 1}
|
||||
}
|
||||
}
|
||||
|
||||
Response::
|
||||
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": foo
|
||||
},
|
||||
|
||||
{
|
||||
"id": 2,
|
||||
"name": bar
|
||||
}
|
||||
]
|
||||
"""
|
||||
|
||||
engine = self._get_engine(engine, *args, **kwargs)
|
||||
db = self._get_engine(engine, *args, **kwargs)
|
||||
|
||||
with engine.connect() as connection:
|
||||
if table:
|
||||
metadata = MetaData()
|
||||
table = Table(table, metadata, autoload=True, autoload_with=db)
|
||||
query = table.select()
|
||||
|
||||
if filter:
|
||||
for (k,v) in filter.items():
|
||||
query = query.where(self._build_condition(table, k, v))
|
||||
|
||||
if query is None:
|
||||
raise RuntimeError('You need to specify either "query", or "table" and "filter"')
|
||||
|
||||
with db.connect() as connection:
|
||||
result = connection.execute(query)
|
||||
|
||||
columns = result.keys()
|
||||
rows = [
|
||||
{ columns[i]: row[i] for i in range(0, len(columns)) }
|
||||
|
@ -235,7 +268,7 @@ class DbPlugin(Plugin):
|
|||
update = table.update()
|
||||
|
||||
for (k,v) in key.items():
|
||||
update = update.where(eval('table.c.{}=={}'.format(k, v)))
|
||||
update = update.where(self._build_condition(table, k, v))
|
||||
|
||||
update = update.values(**values)
|
||||
engine.execute(update)
|
||||
|
@ -282,7 +315,7 @@ class DbPlugin(Plugin):
|
|||
delete = table.delete()
|
||||
|
||||
for (k,v) in record.items():
|
||||
delete = delete.where(eval('table.c.{}=={}'.format(k, v)))
|
||||
delete = delete.where(self._build_condition(table, k, v))
|
||||
|
||||
engine.execute(delete)
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from platypush.config import Config
|
||||
from platypush.context import get_plugin
|
||||
from platypush.plugins import Plugin, action
|
||||
|
||||
|
||||
|
@ -7,9 +9,27 @@ class VariablePlugin(Plugin):
|
|||
accessed across your tasks.
|
||||
"""
|
||||
|
||||
_variable_table_name = 'variable'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._variables = {}
|
||||
self.db_plugin = get_plugin('db')
|
||||
|
||||
db = Config.get('db')
|
||||
self.db_config = {
|
||||
'engine': db.get('engine'),
|
||||
'args': db.get('args', []),
|
||||
'kwargs': db.get('kwargs', {})
|
||||
}
|
||||
|
||||
self._create_tables()
|
||||
# self._variables = {}
|
||||
|
||||
def _create_tables(self):
|
||||
self.db_plugin.execute("""CREATE TABLE IF NOT EXISTS {}(
|
||||
name varchar(255) not null primary key,
|
||||
value text
|
||||
)""".format(self._variable_table_name))
|
||||
|
||||
@action
|
||||
def get(self, name, default_value=None):
|
||||
|
@ -24,7 +44,13 @@ class VariablePlugin(Plugin):
|
|||
:returns: A map in the format ``{"<name>":"<value>"}``
|
||||
"""
|
||||
|
||||
return {name: self._variables.get(name, default_value)}
|
||||
rows = self.db_plugin.select(table=self._variable_table_name,
|
||||
filter={'name': name},
|
||||
engine=self.db_config['engine'],
|
||||
*self.db_config['args'],
|
||||
**self.db_config['kwargs']).output
|
||||
|
||||
return {name: rows[0]['value'] if rows else None}
|
||||
|
||||
@action
|
||||
def set(self, **kwargs):
|
||||
|
@ -34,10 +60,19 @@ class VariablePlugin(Plugin):
|
|||
:param kwargs: Key-value list of variables to set (e.g. ``foo='bar', answer=42``)
|
||||
"""
|
||||
|
||||
for (name, value) in kwargs.items():
|
||||
self._variables[name] = value
|
||||
records = [ { 'name': k, 'value': v }
|
||||
for (k,v) in kwargs.items() ]
|
||||
|
||||
self.db_plugin.insert(table=self._variable_table_name,
|
||||
records=records, key_columns=['name'],
|
||||
engine=self.db_config['engine'],
|
||||
on_duplicate_update=True,
|
||||
*self.db_config['args'],
|
||||
**self.db_config['kwargs'])
|
||||
|
||||
return kwargs
|
||||
|
||||
|
||||
@action
|
||||
def unset(self, name):
|
||||
"""
|
||||
|
@ -46,8 +81,15 @@ class VariablePlugin(Plugin):
|
|||
:param name: Name of the variable to remove
|
||||
:type name: str
|
||||
"""
|
||||
if name in self._variables:
|
||||
del self._variables[name]
|
||||
|
||||
records = [ { 'name': name } ]
|
||||
|
||||
self.db_plugin.delete(table=self._variable_table_name,
|
||||
records=records, engine=self.db_config['engine'],
|
||||
*self.db_config['args'],
|
||||
**self.db_config['kwargs'])
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
||||
|
|
Loading…
Reference in a new issue