Merged in feature/HTTPASTE-10/backend/mysql (pull request #10)
feat(backend/mysql): initialize mysql backend
This commit is contained in:
commit
5a6c6431e9
7 changed files with 384 additions and 0 deletions
|
|
@ -14,3 +14,9 @@ Filesystem
|
|||
|
||||
.. autoclass:: httpaste.backend.file.Parameters
|
||||
:members:
|
||||
|
||||
MySQL
|
||||
-----
|
||||
|
||||
.. autoclass:: httpaste.backend.mysql.Parameters
|
||||
:members:
|
||||
|
|
@ -11,9 +11,13 @@ from .sqlite import Parameters as SqliteParameters
|
|||
from .sqlite import User as SqliteUser
|
||||
from .sqlite import Paste as SqlitePaste
|
||||
from .sqlite import get_connection as get_sqlite_connection
|
||||
from .mysql import get_connection as get_mysql_connection
|
||||
from .file import Parameters as FileParameters
|
||||
from .file import User as FileUser
|
||||
from .file import Paste as FilePaste
|
||||
from .mysql import Parameters as MySQLParameters
|
||||
from .mysql import User as MySQLUser
|
||||
from .mysql import Paste as MySQLPaste
|
||||
|
||||
|
||||
class SQLite(Backend):
|
||||
|
|
@ -46,6 +50,22 @@ class File(Backend):
|
|||
self.paste = FilePaste(parameters, Paste, PasteDataSchema)
|
||||
|
||||
|
||||
class MySQL(Backend):
|
||||
"""MySQL backend interface
|
||||
"""
|
||||
|
||||
parameter_class = MySQLParameters
|
||||
user: MySQLUser
|
||||
paste: MySQLPaste
|
||||
|
||||
def __init__(self, parameters: MySQLParameters):
|
||||
|
||||
parameters = MySQLParameters(*parameters[1:], get_mysql_connection(parameters))
|
||||
|
||||
self.user = MySQLUser(parameters, User)
|
||||
self.paste = MySQLPaste(parameters, Paste)
|
||||
|
||||
|
||||
def get_backend_map() -> Dict[str, Tuple[type, type]]:
|
||||
"""get a map of backend ids and their classes
|
||||
"""
|
||||
|
|
|
|||
120
src/httpaste/backend/mysql/__init__.py
Normal file
120
src/httpaste/backend/mysql/__init__.py
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
"""MySQL backend
|
||||
"""
|
||||
try:
|
||||
from mysql.connector import connect
|
||||
from mysql.connector.connection import MySQLConnection
|
||||
except ImportError as e:
|
||||
raise ImportError(' '.join((
|
||||
'\'mysql-connector-python\' is not installed.',
|
||||
'Install it by running',
|
||||
'\'python3 -m pip install mysql-connector-python\'.'
|
||||
))) from e
|
||||
|
||||
from typing import NamedTuple, Optional
|
||||
|
||||
from . import user
|
||||
from . import paste
|
||||
|
||||
|
||||
class Parameters(NamedTuple):
|
||||
"""MySQL parameters
|
||||
"""
|
||||
|
||||
#: user name
|
||||
user: str
|
||||
#: user password
|
||||
password: str
|
||||
#: hostname or IP address
|
||||
host: str
|
||||
#: database identifier
|
||||
database: str
|
||||
#: a mysql.connection.MySQLConnection object (does not apply to config)
|
||||
connection: Optional[MySQLConnection] = None
|
||||
|
||||
|
||||
class User(object):
|
||||
"""MySQL user model backend
|
||||
"""
|
||||
|
||||
connection: MySQLConnection
|
||||
|
||||
def __init__(self, parameters: Parameters, model_class: type) -> None:
|
||||
|
||||
self.model_class = model_class
|
||||
|
||||
self.connection = get_connection(parameters)
|
||||
|
||||
def load(self, proto: object) -> object:
|
||||
|
||||
return user.load(proto, self.connection, self.model_class)
|
||||
|
||||
def dump(self, model: object) -> None:
|
||||
|
||||
return user.dump(model, self.connection)
|
||||
|
||||
def delete(self, proto: object) -> None:
|
||||
|
||||
return user.delete(proto, self.connection)
|
||||
|
||||
def init(self) -> None:
|
||||
|
||||
return user.init(self.connection)
|
||||
|
||||
def sanitize(self) -> None:
|
||||
|
||||
return user.sanitize(self.connection, self.model_class)
|
||||
|
||||
|
||||
class Paste(object):
|
||||
"""MySQL paste model backend
|
||||
"""
|
||||
|
||||
connection: MySQLConnection
|
||||
|
||||
def __init__(self, parameters: Parameters, model_class: type) -> None:
|
||||
|
||||
self.model_class = model_class
|
||||
|
||||
self.connection = get_connection(parameters)
|
||||
|
||||
def load(self, proto: object) -> object:
|
||||
|
||||
return paste.load(proto, self.connection, self.model_class)
|
||||
|
||||
def dump(self, model: object) -> None:
|
||||
|
||||
return paste.dump(model, self.connection)
|
||||
|
||||
def delete(self, proto: object) -> None:
|
||||
|
||||
return paste.delete(proto, self.connection)
|
||||
|
||||
def init(self) -> None:
|
||||
|
||||
return paste.init(self.connection)
|
||||
|
||||
def sanitize(self) -> None:
|
||||
|
||||
return paste.sanitize(self.connection, self.model_class)
|
||||
|
||||
|
||||
def get_connection(parameters: Parameters) -> MySQLConnection:
|
||||
"""get a mysql.connection.MySQLConnection object
|
||||
"""
|
||||
|
||||
if parameters.connection is not None:
|
||||
|
||||
return parameters.connection
|
||||
|
||||
connection = connect(user=parameters.user, password=parameters.password,
|
||||
host=parameters.host,
|
||||
database=parameters.database)
|
||||
|
||||
return connection
|
||||
|
||||
|
||||
__all__ = [
|
||||
Parameters,
|
||||
User,
|
||||
Paste
|
||||
]
|
||||
122
src/httpaste/backend/mysql/paste.py
Normal file
122
src/httpaste/backend/mysql/paste.py
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
from os import path
|
||||
from time import time
|
||||
|
||||
try:
|
||||
from mysql.connector.connection import MySQLConnection
|
||||
except ImportError as e:
|
||||
raise ImportError(' '.join((
|
||||
'\'mysql-connector-python\' is not installed.',
|
||||
'Install it by running',
|
||||
'\'python3 -m pip install mysql-connector-python\'.'
|
||||
))) from e
|
||||
|
||||
|
||||
def load(proto:object, connection: MySQLConnection, model_class: type):
|
||||
"""load a paste model
|
||||
|
||||
:param model: model prototype
|
||||
:param connection: mysql connector connection object
|
||||
:param model_class: model class
|
||||
"""
|
||||
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
statement = '''SELECT pid, data, data_hash, sub, expiration, encoding
|
||||
FROM httpaste_pastes
|
||||
WHERE pid=%s'''
|
||||
|
||||
cursor.execute(statement, (proto.pid,))
|
||||
|
||||
row = cursor.fetchone()
|
||||
|
||||
if row is not None:
|
||||
|
||||
return model_class(
|
||||
row['pid'],
|
||||
row['sub'],
|
||||
row['data'],
|
||||
row['data_hash'],
|
||||
row['expiration'],
|
||||
row['encoding'])
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def dump(model:object, connection: MySQLConnection):
|
||||
"""dump a paste model
|
||||
|
||||
:param model: model object
|
||||
:param connection: mysql connector connection object
|
||||
:param model_class: model class
|
||||
"""
|
||||
|
||||
cursor = connection.cursor()
|
||||
|
||||
statement = '''REPLACE INTO httpaste_pastes
|
||||
(pid, data, data_hash, sub, expiration, encoding)
|
||||
VALUES (%s, %s, %s, %s, %s, %s)'''
|
||||
|
||||
cursor.execute(statement, (model.pid, model.data, model.data_hash,
|
||||
model.sub, model.expiration, model.encoding))
|
||||
|
||||
connection.commit()
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def delete(proto: object, connection: MySQLConnection):
|
||||
"""delete a paste model
|
||||
|
||||
:param model: model prototype
|
||||
:param connection: mysql connector connection object
|
||||
:param model_class: model class
|
||||
"""
|
||||
|
||||
cursor = connection.cursor()
|
||||
|
||||
statement = '''DELETE FROM httpaste_pastes
|
||||
WHERE pid=%s'''
|
||||
|
||||
cursor.execute(statement, (proto.pid,))
|
||||
|
||||
connection.commit()
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def init(connection: MySQLConnection):
|
||||
"""initialize paste model table
|
||||
|
||||
:param connection: mysql connector connection object
|
||||
"""
|
||||
|
||||
cursor = connection.cursor()
|
||||
|
||||
with open(path.join(path.dirname(__file__), 'paste.sql'), 'r') as fh:
|
||||
|
||||
cursor.execute(fh.read())
|
||||
|
||||
connection.commit()
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def sanitize(connection: MySQLConnection, model_class:type):
|
||||
"""sanitize paste model table
|
||||
|
||||
:param connection: mysql connector connection object
|
||||
"""
|
||||
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
statement = '''SELECT pid
|
||||
FROM httpaste_pastes
|
||||
WHERE expiration < %s AND expiration > 0'''
|
||||
|
||||
cursor.execute(statement, (time(),))
|
||||
|
||||
for row in cursor.fetchall():
|
||||
|
||||
delete(model_class(row['pid']))
|
||||
|
||||
return None
|
||||
9
src/httpaste/backend/mysql/paste.sql
Normal file
9
src/httpaste/backend/mysql/paste.sql
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
CREATE TABLE `httpaste_pastes` (
|
||||
`pid` blob NOT NULL,
|
||||
`data` longblob NOT NULL,
|
||||
`data_hash` blob NOT NULL,
|
||||
`sub` blob DEFAULT NULL,
|
||||
`expiration` int(16) NOT NULL,
|
||||
`encoding` tinytext DEFAULT NULL,
|
||||
PRIMARY KEY (`pid`(128))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
101
src/httpaste/backend/mysql/user.py
Normal file
101
src/httpaste/backend/mysql/user.py
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
from os import path
|
||||
try:
|
||||
from mysql.connector.connection import MySQLConnection
|
||||
except ImportError as e:
|
||||
raise ImportError(' '.join((
|
||||
'\'mysql-connector-python\' is not installed.',
|
||||
'Install it by running',
|
||||
'\'python3 -m pip install mysql-connector-python\'.'
|
||||
))) from e
|
||||
|
||||
|
||||
def load(proto:object, connection: MySQLConnection, model_class: type):
|
||||
"""load a user model
|
||||
|
||||
:param model: model prototype
|
||||
:param connection: mysql connector connection object
|
||||
:param model_class: model class
|
||||
"""
|
||||
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
statement = '''SELECT sub, key_hash, paste_index
|
||||
FROM httpaste_users
|
||||
WHERE sub=%s'''
|
||||
|
||||
cursor.execute(statement, (proto.sub,))
|
||||
|
||||
row = cursor.fetchone()
|
||||
|
||||
if row is not None:
|
||||
|
||||
return model_class(row['sub'], row['key_hash'], row['paste_index'])
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def dump(model:object, connection: MySQLConnection):
|
||||
"""dump a user model
|
||||
|
||||
:param model: model object
|
||||
:param connection: mysql connector connection object
|
||||
:param model_class: model class
|
||||
"""
|
||||
|
||||
cursor = connection.cursor()
|
||||
|
||||
statement = '''REPLACE INTO httpaste_users
|
||||
(sub, key_hash, paste_index)
|
||||
VALUES (%s, %s, %s)'''
|
||||
|
||||
cursor.execute(statement, (model.sub, model.key_hash, model.index))
|
||||
|
||||
connection.commit()
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def delete(proto: object, connection: MySQLConnection):
|
||||
"""delete a user model
|
||||
|
||||
:param model: model prototype
|
||||
:param connection: mysql connector connection object
|
||||
:param model_class: model class
|
||||
"""
|
||||
|
||||
cursor = connection.cursor()
|
||||
|
||||
statement = '''DELETE FROM httpaste_users
|
||||
WHERE sub=%s'''
|
||||
|
||||
cursor.execute(statement, (proto.sub,))
|
||||
|
||||
connection.commit()
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def init(connection: MySQLConnection):
|
||||
"""initialize user model table
|
||||
|
||||
:param connection: mysql connector connection object
|
||||
"""
|
||||
|
||||
cursor = connection.cursor()
|
||||
|
||||
with open(path.join(path.dirname(__file__), 'user.sql'), 'r') as fh:
|
||||
|
||||
cursor.execute(fh.read())
|
||||
|
||||
connection.commit()
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def sanitize(connection: MySQLConnection, model_class: type):
|
||||
"""sanitize user model table
|
||||
|
||||
:param connection: mysql connector connection object
|
||||
"""
|
||||
|
||||
return None
|
||||
6
src/httpaste/backend/mysql/user.sql
Normal file
6
src/httpaste/backend/mysql/user.sql
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
CREATE TABLE `httpaste_users` (
|
||||
`sub` blob NOT NULL,
|
||||
`key_hash` blob NOT NULL,
|
||||
`paste_index` blob NOT NULL,
|
||||
PRIMARY KEY (`sub`(128))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
Loading…
Add table
Add a link
Reference in a new issue