admin,storage: added more administrator functionalities
administrators can now: - Set displayname of users - Update user avatars - Search for users by user_id - Browse all users in a paginated API - Reset user passwords - Deactivate users Helpers for doing paginated queries has also been added to storage Signed-off-by: Morteza Araby <morteza.araby@ericsson.com>
This commit is contained in:
parent
5ae38b65c1
commit
2849d3f29d
|
@ -19,7 +19,6 @@ from ._base import BaseHandler
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,3 +53,46 @@ class AdminHandler(BaseHandler):
|
||||||
}
|
}
|
||||||
|
|
||||||
defer.returnValue(ret)
|
defer.returnValue(ret)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_users(self):
|
||||||
|
"""Function to reterive a list of users in users table.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
Returns:
|
||||||
|
defer.Deferred: resolves to list[dict[str, Any]]
|
||||||
|
"""
|
||||||
|
ret = yield self.store.get_users()
|
||||||
|
|
||||||
|
defer.returnValue(ret)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_users_paginate(self, order, start, limit):
|
||||||
|
"""Function to reterive a paginated list of users from
|
||||||
|
users list. This will return a json object, which contains
|
||||||
|
list of users and the total number of users in users table.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
order (str): column name to order the select by this column
|
||||||
|
start (int): start number to begin the query from
|
||||||
|
limit (int): number of rows to reterive
|
||||||
|
Returns:
|
||||||
|
defer.Deferred: resolves to json object {list[dict[str, Any]], count}
|
||||||
|
"""
|
||||||
|
ret = yield self.store.get_users_paginate(order, start, limit)
|
||||||
|
|
||||||
|
defer.returnValue(ret)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def search_users(self, term):
|
||||||
|
"""Function to search users list for one or more users with
|
||||||
|
the matched term.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
term (str): search term
|
||||||
|
Returns:
|
||||||
|
defer.Deferred: resolves to list[dict[str, Any]]
|
||||||
|
"""
|
||||||
|
ret = yield self.store.search_users(term)
|
||||||
|
|
||||||
|
defer.returnValue(ret)
|
||||||
|
|
|
@ -17,6 +17,7 @@ from twisted.internet import defer
|
||||||
|
|
||||||
from synapse.api.errors import AuthError, SynapseError
|
from synapse.api.errors import AuthError, SynapseError
|
||||||
from synapse.types import UserID
|
from synapse.types import UserID
|
||||||
|
from synapse.http.servlet import parse_json_object_from_request
|
||||||
|
|
||||||
from .base import ClientV1RestServlet, client_path_patterns
|
from .base import ClientV1RestServlet, client_path_patterns
|
||||||
|
|
||||||
|
@ -25,6 +26,34 @@ import logging
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class UsersRestServlet(ClientV1RestServlet):
|
||||||
|
PATTERNS = client_path_patterns("/admin/users/(?P<user_id>[^/]*)")
|
||||||
|
|
||||||
|
def __init__(self, hs):
|
||||||
|
super(UsersRestServlet, self).__init__(hs)
|
||||||
|
self.handlers = hs.get_handlers()
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def on_GET(self, request, user_id):
|
||||||
|
target_user = UserID.from_string(user_id)
|
||||||
|
requester = yield self.auth.get_user_by_req(request)
|
||||||
|
is_admin = yield self.auth.is_server_admin(requester.user)
|
||||||
|
|
||||||
|
if not is_admin:
|
||||||
|
raise AuthError(403, "You are not a server admin")
|
||||||
|
|
||||||
|
# To allow all users to get the users list
|
||||||
|
# if not is_admin and target_user != auth_user:
|
||||||
|
# raise AuthError(403, "You are not a server admin")
|
||||||
|
|
||||||
|
if not self.hs.is_mine(target_user):
|
||||||
|
raise SynapseError(400, "Can only users a local user")
|
||||||
|
|
||||||
|
ret = yield self.handlers.admin_handler.get_users()
|
||||||
|
|
||||||
|
defer.returnValue((200, ret))
|
||||||
|
|
||||||
|
|
||||||
class WhoisRestServlet(ClientV1RestServlet):
|
class WhoisRestServlet(ClientV1RestServlet):
|
||||||
PATTERNS = client_path_patterns("/admin/whois/(?P<user_id>[^/]*)")
|
PATTERNS = client_path_patterns("/admin/whois/(?P<user_id>[^/]*)")
|
||||||
|
|
||||||
|
@ -128,8 +157,199 @@ class DeactivateAccountRestServlet(ClientV1RestServlet):
|
||||||
defer.returnValue((200, {}))
|
defer.returnValue((200, {}))
|
||||||
|
|
||||||
|
|
||||||
|
class ResetPasswordRestServlet(ClientV1RestServlet):
|
||||||
|
"""Post request to allow an administrator reset password for a user.
|
||||||
|
This need a user have a administrator access in Synapse.
|
||||||
|
Example:
|
||||||
|
http://localhost:8008/_matrix/client/api/v1/admin/reset_password/
|
||||||
|
@user:to_reset_password?access_token=admin_access_token
|
||||||
|
JsonBodyToSend:
|
||||||
|
{
|
||||||
|
"new_password": "secret"
|
||||||
|
}
|
||||||
|
Returns:
|
||||||
|
200 OK with empty object if success otherwise an error.
|
||||||
|
"""
|
||||||
|
PATTERNS = client_path_patterns("/admin/reset_password/(?P<target_user_id>[^/]*)")
|
||||||
|
|
||||||
|
def __init__(self, hs):
|
||||||
|
self.store = hs.get_datastore()
|
||||||
|
super(ResetPasswordRestServlet, self).__init__(hs)
|
||||||
|
self.hs = hs
|
||||||
|
self.auth = hs.get_auth()
|
||||||
|
self.auth_handler = hs.get_auth_handler()
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def on_POST(self, request, target_user_id):
|
||||||
|
"""Post request to allow an administrator reset password for a user.
|
||||||
|
This need a user have a administrator access in Synapse.
|
||||||
|
"""
|
||||||
|
UserID.from_string(target_user_id)
|
||||||
|
requester = yield self.auth.get_user_by_req(request)
|
||||||
|
is_admin = yield self.auth.is_server_admin(requester.user)
|
||||||
|
|
||||||
|
if not is_admin:
|
||||||
|
raise AuthError(403, "You are not a server admin")
|
||||||
|
|
||||||
|
params = parse_json_object_from_request(request)
|
||||||
|
new_password = params['new_password']
|
||||||
|
if not new_password:
|
||||||
|
raise SynapseError(400, "Missing 'new_password' arg")
|
||||||
|
|
||||||
|
logger.info("new_password: %r", new_password)
|
||||||
|
|
||||||
|
yield self.auth_handler.set_password(
|
||||||
|
target_user_id, new_password, requester
|
||||||
|
)
|
||||||
|
defer.returnValue((200, {}))
|
||||||
|
|
||||||
|
|
||||||
|
class GetUsersPaginatedRestServlet(ClientV1RestServlet):
|
||||||
|
"""Get request to get specific number of users from Synapse.
|
||||||
|
This need a user have a administrator access in Synapse.
|
||||||
|
Example:
|
||||||
|
http://localhost:8008/_matrix/client/api/v1/admin/users_paginate/
|
||||||
|
@admin:user?access_token=admin_access_token&start=0&limit=10
|
||||||
|
Returns:
|
||||||
|
200 OK with json object {list[dict[str, Any]], count} or empty object.
|
||||||
|
"""
|
||||||
|
PATTERNS = client_path_patterns("/admin/users_paginate/(?P<target_user_id>[^/]*)")
|
||||||
|
|
||||||
|
def __init__(self, hs):
|
||||||
|
self.store = hs.get_datastore()
|
||||||
|
super(GetUsersPaginatedRestServlet, self).__init__(hs)
|
||||||
|
self.hs = hs
|
||||||
|
self.auth = hs.get_auth()
|
||||||
|
self.handlers = hs.get_handlers()
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def on_GET(self, request, target_user_id):
|
||||||
|
"""Get request to get specific number of users from Synapse.
|
||||||
|
This need a user have a administrator access in Synapse.
|
||||||
|
"""
|
||||||
|
target_user = UserID.from_string(target_user_id)
|
||||||
|
requester = yield self.auth.get_user_by_req(request)
|
||||||
|
is_admin = yield self.auth.is_server_admin(requester.user)
|
||||||
|
|
||||||
|
if not is_admin:
|
||||||
|
raise AuthError(403, "You are not a server admin")
|
||||||
|
|
||||||
|
# To allow all users to get the users list
|
||||||
|
# if not is_admin and target_user != auth_user:
|
||||||
|
# raise AuthError(403, "You are not a server admin")
|
||||||
|
|
||||||
|
if not self.hs.is_mine(target_user):
|
||||||
|
raise SynapseError(400, "Can only users a local user")
|
||||||
|
|
||||||
|
order = "name" # order by name in user table
|
||||||
|
start = request.args.get("start")[0]
|
||||||
|
limit = request.args.get("limit")[0]
|
||||||
|
if not limit:
|
||||||
|
raise SynapseError(400, "Missing 'limit' arg")
|
||||||
|
if not start:
|
||||||
|
raise SynapseError(400, "Missing 'start' arg")
|
||||||
|
logger.info("limit: %s, start: %s", limit, start)
|
||||||
|
|
||||||
|
ret = yield self.handlers.admin_handler.get_users_paginate(
|
||||||
|
order, start, limit
|
||||||
|
)
|
||||||
|
defer.returnValue((200, ret))
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def on_POST(self, request, target_user_id):
|
||||||
|
"""Post request to get specific number of users from Synapse..
|
||||||
|
This need a user have a administrator access in Synapse.
|
||||||
|
Example:
|
||||||
|
http://localhost:8008/_matrix/client/api/v1/admin/users_paginate/
|
||||||
|
@admin:user?access_token=admin_access_token
|
||||||
|
JsonBodyToSend:
|
||||||
|
{
|
||||||
|
"start": "0",
|
||||||
|
"limit": "10
|
||||||
|
}
|
||||||
|
Returns:
|
||||||
|
200 OK with json object {list[dict[str, Any]], count} or empty object.
|
||||||
|
"""
|
||||||
|
UserID.from_string(target_user_id)
|
||||||
|
requester = yield self.auth.get_user_by_req(request)
|
||||||
|
is_admin = yield self.auth.is_server_admin(requester.user)
|
||||||
|
|
||||||
|
if not is_admin:
|
||||||
|
raise AuthError(403, "You are not a server admin")
|
||||||
|
|
||||||
|
order = "name" # order by name in user table
|
||||||
|
params = parse_json_object_from_request(request)
|
||||||
|
limit = params['limit']
|
||||||
|
start = params['start']
|
||||||
|
if not limit:
|
||||||
|
raise SynapseError(400, "Missing 'limit' arg")
|
||||||
|
if not start:
|
||||||
|
raise SynapseError(400, "Missing 'start' arg")
|
||||||
|
logger.info("limit: %s, start: %s", limit, start)
|
||||||
|
|
||||||
|
ret = yield self.handlers.admin_handler.get_users_paginate(
|
||||||
|
order, start, limit
|
||||||
|
)
|
||||||
|
defer.returnValue((200, ret))
|
||||||
|
|
||||||
|
|
||||||
|
class SearchUsersRestServlet(ClientV1RestServlet):
|
||||||
|
"""Get request to search user table for specific users according to
|
||||||
|
search term.
|
||||||
|
This need a user have a administrator access in Synapse.
|
||||||
|
Example:
|
||||||
|
http://localhost:8008/_matrix/client/api/v1/admin/search_users/
|
||||||
|
@admin:user?access_token=admin_access_token&term=alice
|
||||||
|
Returns:
|
||||||
|
200 OK with json object {list[dict[str, Any]], count} or empty object.
|
||||||
|
"""
|
||||||
|
PATTERNS = client_path_patterns("/admin/search_users/(?P<target_user_id>[^/]*)")
|
||||||
|
|
||||||
|
def __init__(self, hs):
|
||||||
|
self.store = hs.get_datastore()
|
||||||
|
super(SearchUsersRestServlet, self).__init__(hs)
|
||||||
|
self.hs = hs
|
||||||
|
self.auth = hs.get_auth()
|
||||||
|
self.handlers = hs.get_handlers()
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def on_GET(self, request, target_user_id):
|
||||||
|
"""Get request to search user table for specific users according to
|
||||||
|
search term.
|
||||||
|
This need a user have a administrator access in Synapse.
|
||||||
|
"""
|
||||||
|
target_user = UserID.from_string(target_user_id)
|
||||||
|
requester = yield self.auth.get_user_by_req(request)
|
||||||
|
is_admin = yield self.auth.is_server_admin(requester.user)
|
||||||
|
|
||||||
|
if not is_admin:
|
||||||
|
raise AuthError(403, "You are not a server admin")
|
||||||
|
|
||||||
|
# To allow all users to get the users list
|
||||||
|
# if not is_admin and target_user != auth_user:
|
||||||
|
# raise AuthError(403, "You are not a server admin")
|
||||||
|
|
||||||
|
if not self.hs.is_mine(target_user):
|
||||||
|
raise SynapseError(400, "Can only users a local user")
|
||||||
|
|
||||||
|
term = request.args.get("term")[0]
|
||||||
|
if not term:
|
||||||
|
raise SynapseError(400, "Missing 'term' arg")
|
||||||
|
|
||||||
|
logger.info("term: %s ", term)
|
||||||
|
|
||||||
|
ret = yield self.handlers.admin_handler.search_users(
|
||||||
|
term
|
||||||
|
)
|
||||||
|
defer.returnValue((200, ret))
|
||||||
|
|
||||||
|
|
||||||
def register_servlets(hs, http_server):
|
def register_servlets(hs, http_server):
|
||||||
WhoisRestServlet(hs).register(http_server)
|
WhoisRestServlet(hs).register(http_server)
|
||||||
PurgeMediaCacheRestServlet(hs).register(http_server)
|
PurgeMediaCacheRestServlet(hs).register(http_server)
|
||||||
DeactivateAccountRestServlet(hs).register(http_server)
|
DeactivateAccountRestServlet(hs).register(http_server)
|
||||||
PurgeHistoryRestServlet(hs).register(http_server)
|
PurgeHistoryRestServlet(hs).register(http_server)
|
||||||
|
UsersRestServlet(hs).register(http_server)
|
||||||
|
ResetPasswordRestServlet(hs).register(http_server)
|
||||||
|
GetUsersPaginatedRestServlet(hs).register(http_server)
|
||||||
|
SearchUsersRestServlet(hs).register(http_server)
|
||||||
|
|
|
@ -46,6 +46,7 @@ class ProfileDisplaynameRestServlet(ClientV1RestServlet):
|
||||||
def on_PUT(self, request, user_id):
|
def on_PUT(self, request, user_id):
|
||||||
requester = yield self.auth.get_user_by_req(request, allow_guest=True)
|
requester = yield self.auth.get_user_by_req(request, allow_guest=True)
|
||||||
user = UserID.from_string(user_id)
|
user = UserID.from_string(user_id)
|
||||||
|
is_admin = yield self.auth.is_server_admin(requester.user)
|
||||||
|
|
||||||
content = parse_json_object_from_request(request)
|
content = parse_json_object_from_request(request)
|
||||||
|
|
||||||
|
@ -55,7 +56,7 @@ class ProfileDisplaynameRestServlet(ClientV1RestServlet):
|
||||||
defer.returnValue((400, "Unable to parse name"))
|
defer.returnValue((400, "Unable to parse name"))
|
||||||
|
|
||||||
yield self.handlers.profile_handler.set_displayname(
|
yield self.handlers.profile_handler.set_displayname(
|
||||||
user, requester, new_name)
|
user, requester, new_name, is_admin)
|
||||||
|
|
||||||
defer.returnValue((200, {}))
|
defer.returnValue((200, {}))
|
||||||
|
|
||||||
|
@ -88,6 +89,7 @@ class ProfileAvatarURLRestServlet(ClientV1RestServlet):
|
||||||
def on_PUT(self, request, user_id):
|
def on_PUT(self, request, user_id):
|
||||||
requester = yield self.auth.get_user_by_req(request)
|
requester = yield self.auth.get_user_by_req(request)
|
||||||
user = UserID.from_string(user_id)
|
user = UserID.from_string(user_id)
|
||||||
|
is_admin = yield self.auth.is_server_admin(requester.user)
|
||||||
|
|
||||||
content = parse_json_object_from_request(request)
|
content = parse_json_object_from_request(request)
|
||||||
try:
|
try:
|
||||||
|
@ -96,7 +98,7 @@ class ProfileAvatarURLRestServlet(ClientV1RestServlet):
|
||||||
defer.returnValue((400, "Unable to parse name"))
|
defer.returnValue((400, "Unable to parse name"))
|
||||||
|
|
||||||
yield self.handlers.profile_handler.set_avatar_url(
|
yield self.handlers.profile_handler.set_avatar_url(
|
||||||
user, requester, new_name)
|
user, requester, new_name, is_admin)
|
||||||
|
|
||||||
defer.returnValue((200, {}))
|
defer.returnValue((200, {}))
|
||||||
|
|
||||||
|
|
|
@ -297,6 +297,82 @@ class DataStore(RoomMemberStore, RoomStore,
|
||||||
desc="get_user_ip_and_agents",
|
desc="get_user_ip_and_agents",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_users(self):
|
||||||
|
"""Function to reterive a list of users in users table.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
Returns:
|
||||||
|
defer.Deferred: resolves to list[dict[str, Any]]
|
||||||
|
"""
|
||||||
|
return self._simple_select_list(
|
||||||
|
table="users",
|
||||||
|
keyvalues={},
|
||||||
|
retcols=[
|
||||||
|
"name",
|
||||||
|
"password_hash",
|
||||||
|
"is_guest",
|
||||||
|
"admin"
|
||||||
|
],
|
||||||
|
desc="get_users",
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_users_paginate(self, order, start, limit):
|
||||||
|
"""Function to reterive a paginated list of users from
|
||||||
|
users list. This will return a json object, which contains
|
||||||
|
list of users and the total number of users in users table.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
order (str): column name to order the select by this column
|
||||||
|
start (int): start number to begin the query from
|
||||||
|
limit (int): number of rows to reterive
|
||||||
|
Returns:
|
||||||
|
defer.Deferred: resolves to json object {list[dict[str, Any]], count}
|
||||||
|
"""
|
||||||
|
is_guest = 0
|
||||||
|
i_start = (int)(start)
|
||||||
|
i_limit = (int)(limit)
|
||||||
|
return self.get_user_list_paginate(
|
||||||
|
table="users",
|
||||||
|
keyvalues={
|
||||||
|
"is_guest": is_guest
|
||||||
|
},
|
||||||
|
pagevalues=[
|
||||||
|
order,
|
||||||
|
i_limit,
|
||||||
|
i_start
|
||||||
|
],
|
||||||
|
retcols=[
|
||||||
|
"name",
|
||||||
|
"password_hash",
|
||||||
|
"is_guest",
|
||||||
|
"admin"
|
||||||
|
],
|
||||||
|
desc="get_users_paginate",
|
||||||
|
)
|
||||||
|
|
||||||
|
def search_users(self, term):
|
||||||
|
"""Function to search users list for one or more users with
|
||||||
|
the matched term.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
term (str): search term
|
||||||
|
col (str): column to query term should be matched to
|
||||||
|
Returns:
|
||||||
|
defer.Deferred: resolves to list[dict[str, Any]]
|
||||||
|
"""
|
||||||
|
return self._simple_search_list(
|
||||||
|
table="users",
|
||||||
|
term=term,
|
||||||
|
col="name",
|
||||||
|
retcols=[
|
||||||
|
"name",
|
||||||
|
"password_hash",
|
||||||
|
"is_guest",
|
||||||
|
"admin"
|
||||||
|
],
|
||||||
|
desc="search_users",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def are_all_users_on_domain(txn, database_engine, domain):
|
def are_all_users_on_domain(txn, database_engine, domain):
|
||||||
sql = database_engine.convert_param_style(
|
sql = database_engine.convert_param_style(
|
||||||
|
|
|
@ -934,6 +934,165 @@ class SQLBaseStore(object):
|
||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
def _simple_select_list_paginate(self, table, keyvalues, pagevalues, retcols,
|
||||||
|
desc="_simple_select_list_paginate"):
|
||||||
|
"""Executes a SELECT query on the named table with start and limit,
|
||||||
|
of row numbers, which may return zero or number of rows from start to limit,
|
||||||
|
returning the result as a list of dicts.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
table (str): the table name
|
||||||
|
keyvalues (dict[str, Any] | None):
|
||||||
|
column names and values to select the rows with, or None to not
|
||||||
|
apply a WHERE clause.
|
||||||
|
retcols (iterable[str]): the names of the columns to return
|
||||||
|
order (str): order the select by this column
|
||||||
|
start (int): start number to begin the query from
|
||||||
|
limit (int): number of rows to reterive
|
||||||
|
Returns:
|
||||||
|
defer.Deferred: resolves to list[dict[str, Any]]
|
||||||
|
"""
|
||||||
|
return self.runInteraction(
|
||||||
|
desc,
|
||||||
|
self._simple_select_list_paginate_txn,
|
||||||
|
table, keyvalues, pagevalues, retcols
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _simple_select_list_paginate_txn(cls, txn, table, keyvalues, pagevalues, retcols):
|
||||||
|
"""Executes a SELECT query on the named table with start and limit,
|
||||||
|
of row numbers, which may return zero or number of rows from start to limit,
|
||||||
|
returning the result as a list of dicts.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
txn : Transaction object
|
||||||
|
table (str): the table name
|
||||||
|
keyvalues (dict[str, T] | None):
|
||||||
|
column names and values to select the rows with, or None to not
|
||||||
|
apply a WHERE clause.
|
||||||
|
pagevalues ([]):
|
||||||
|
order (str): order the select by this column
|
||||||
|
start (int): start number to begin the query from
|
||||||
|
limit (int): number of rows to reterive
|
||||||
|
retcols (iterable[str]): the names of the columns to return
|
||||||
|
Returns:
|
||||||
|
defer.Deferred: resolves to list[dict[str, Any]]
|
||||||
|
|
||||||
|
"""
|
||||||
|
if keyvalues:
|
||||||
|
sql = "SELECT %s FROM %s WHERE %s ORDER BY %s" % (
|
||||||
|
", ".join(retcols),
|
||||||
|
table,
|
||||||
|
" AND ".join("%s = ?" % (k,) for k in keyvalues),
|
||||||
|
" ? ASC LIMIT ? OFFSET ?"
|
||||||
|
)
|
||||||
|
txn.execute(sql, keyvalues.values() + pagevalues)
|
||||||
|
else:
|
||||||
|
sql = "SELECT %s FROM %s ORDER BY %s" % (
|
||||||
|
", ".join(retcols),
|
||||||
|
table,
|
||||||
|
" ? ASC LIMIT ? OFFSET ?"
|
||||||
|
)
|
||||||
|
txn.execute(sql, pagevalues)
|
||||||
|
|
||||||
|
return cls.cursor_to_dict(txn)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_user_list_paginate(self, table, keyvalues, pagevalues, retcols,
|
||||||
|
desc="get_user_list_paginate"):
|
||||||
|
"""Get a list of users from start row to a limit number of rows. This will
|
||||||
|
return a json object with users and total number of users in users list.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
table (str): the table name
|
||||||
|
keyvalues (dict[str, Any] | None):
|
||||||
|
column names and values to select the rows with, or None to not
|
||||||
|
apply a WHERE clause.
|
||||||
|
pagevalues ([]):
|
||||||
|
order (str): order the select by this column
|
||||||
|
start (int): start number to begin the query from
|
||||||
|
limit (int): number of rows to reterive
|
||||||
|
retcols (iterable[str]): the names of the columns to return
|
||||||
|
Returns:
|
||||||
|
defer.Deferred: resolves to json object {list[dict[str, Any]], count}
|
||||||
|
"""
|
||||||
|
users = yield self.runInteraction(
|
||||||
|
desc,
|
||||||
|
self._simple_select_list_paginate_txn,
|
||||||
|
table, keyvalues, pagevalues, retcols
|
||||||
|
)
|
||||||
|
count = yield self.runInteraction(
|
||||||
|
desc,
|
||||||
|
self.get_user_count_txn
|
||||||
|
)
|
||||||
|
retval = {
|
||||||
|
"users": users,
|
||||||
|
"total": count
|
||||||
|
}
|
||||||
|
defer.returnValue(retval)
|
||||||
|
|
||||||
|
def get_user_count_txn(self, txn):
|
||||||
|
"""Get a total number of registerd users in the users list.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
txn : Transaction object
|
||||||
|
Returns:
|
||||||
|
defer.Deferred: resolves to int
|
||||||
|
"""
|
||||||
|
sql_count = "SELECT COUNT(*) FROM users WHERE is_guest = 0;"
|
||||||
|
txn.execute(sql_count)
|
||||||
|
count = txn.fetchone()[0]
|
||||||
|
defer.returnValue(count)
|
||||||
|
|
||||||
|
def _simple_search_list(self, table, term, col, retcols,
|
||||||
|
desc="_simple_search_list"):
|
||||||
|
"""Executes a SELECT query on the named table, which may return zero or
|
||||||
|
more rows, returning the result as a list of dicts.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
table (str): the table name
|
||||||
|
term (str | None):
|
||||||
|
term for searching the table matched to a column.
|
||||||
|
col (str): column to query term should be matched to
|
||||||
|
retcols (iterable[str]): the names of the columns to return
|
||||||
|
Returns:
|
||||||
|
defer.Deferred: resolves to list[dict[str, Any]] or None
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.runInteraction(
|
||||||
|
desc,
|
||||||
|
self._simple_search_list_txn,
|
||||||
|
table, term, col, retcols
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _simple_search_list_txn(cls, txn, table, term, col, retcols):
|
||||||
|
"""Executes a SELECT query on the named table, which may return zero or
|
||||||
|
more rows, returning the result as a list of dicts.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
txn : Transaction object
|
||||||
|
table (str): the table name
|
||||||
|
term (str | None):
|
||||||
|
term for searching the table matched to a column.
|
||||||
|
col (str): column to query term should be matched to
|
||||||
|
retcols (iterable[str]): the names of the columns to return
|
||||||
|
Returns:
|
||||||
|
defer.Deferred: resolves to list[dict[str, Any]] or None
|
||||||
|
"""
|
||||||
|
if term:
|
||||||
|
sql = "SELECT %s FROM %s WHERE %s LIKE ?" % (
|
||||||
|
", ".join(retcols),
|
||||||
|
table,
|
||||||
|
col
|
||||||
|
)
|
||||||
|
termvalues = ["%%" + term + "%%"]
|
||||||
|
txn.execute(sql, termvalues)
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
return cls.cursor_to_dict(txn)
|
||||||
|
|
||||||
|
|
||||||
class _RollbackButIsFineException(Exception):
|
class _RollbackButIsFineException(Exception):
|
||||||
""" This exception is used to rollback a transaction without implying
|
""" This exception is used to rollback a transaction without implying
|
||||||
|
|
Loading…
Reference in New Issue