@@ -174,6 +174,10 @@ def main():
total_rows = sum(t["rows"] for t in usage.values())
print(f"Total rows: {total_rows}")
+ def handle_get_db_query_columns(args, client):
+ columns = client.get_db_query_columns()
+ print("\n".join(sorted(columns)))
+
parser = argparse.ArgumentParser(description='Hash Equivalence Client')
parser.add_argument('--address', default=DEFAULT_ADDRESS, help='Server address (default "%(default)s")')
parser.add_argument('--log', default='WARNING', help='Set logging level')
@@ -239,6 +243,9 @@ def main():
db_usage_parser = subparsers.add_parser('get-db-usage', help="Database Usage")
db_usage_parser.set_defaults(func=handle_get_db_usage)
+ db_query_columns_parser = subparsers.add_parser('get-db-query-columns', help="Show columns that can be used in database queries")
+ db_query_columns_parser.set_defaults(func=handle_get_db_query_columns)
+
args = parser.parse_args()
logger = logging.getLogger('hashserv')
@@ -190,6 +190,10 @@ class AsyncClient(bb.asyncrpc.AsyncClient):
await self._set_mode(self.MODE_NORMAL)
return (await self.invoke({"get-db-usage": {}}))["usage"]
+ async def get_db_query_columns(self):
+ await self._set_mode(self.MODE_NORMAL)
+ return (await self.invoke({"get-db-query-columns": {}}))["columns"]
+
class Client(bb.asyncrpc.Client):
def __init__(self, username=None, password=None):
@@ -219,6 +223,7 @@ class Client(bb.asyncrpc.Client):
"delete_user",
"become_user",
"get_db_usage",
+ "get_db_query_columns",
)
def _get_async_client(self):
@@ -250,6 +250,7 @@ class ServerClient(bb.asyncrpc.AsyncServerConnection):
"get-stream": self.handle_get_stream,
"get-stats": self.handle_get_stats,
"get-db-usage": self.handle_get_db_usage,
+ "get-db-query-columns": self.handle_get_db_query_columns,
# Not always read-only, but internally checks if the server is
# read-only
"report": self.handle_report,
@@ -572,6 +573,10 @@ class ServerClient(bb.asyncrpc.AsyncServerConnection):
async def handle_get_db_usage(self, request):
return {"usage": await self.db.get_usage()}
+ @permissions(DB_ADMIN_PERM)
+ async def handle_get_db_query_columns(self, request):
+ return {"columns": await self.db.get_query_columns()}
+
# The authentication API is always allowed
async def handle_auth(self, request):
username = str(request["username"])
@@ -415,3 +415,13 @@ class Database(object):
}
return usage
+
+ async def get_query_columns(self):
+ columns = set()
+ for table in (UnihashesV2, OuthashesV2):
+ for c in table.__table__.columns:
+ if not isinstance(c.type, Text):
+ continue
+ columns.add(c.key)
+
+ return list(columns)
@@ -399,3 +399,10 @@ class Database(object):
"rows": cursor.fetchone()[0],
}
return usage
+
+ async def get_query_columns(self):
+ columns = set()
+ for name, typ, _ in UNIHASH_TABLE_DEFINITION + OUTHASH_TABLE_DEFINITION:
+ if typ.startswith("TEXT"):
+ columns.add(name)
+ return list(columns)
@@ -776,6 +776,14 @@ class HashEquivalenceCommonTests(object):
self.assertIn("rows", usage[name])
self.assertTrue(isinstance(usage[name]["rows"], int))
+ def test_get_db_query_columns(self):
+ columns = self.client.get_db_query_columns()
+
+ self.assertTrue(isinstance(columns, list))
+ self.assertTrue(len(columns) > 0)
+
+ for col in columns:
+ self.client.remove({col: ""})
class TestHashEquivalenceUnixServer(HashEquivalenceTestSetup, HashEquivalenceCommonTests, unittest.TestCase):
def get_server_addr(self, server_idx):
Adds an API to retrieve the columns that can be queried on from the database backend. This prevents front end applications from needing to hardcode the query columns Signed-off-by: Joshua Watt <JPEWhacker@gmail.com> --- bin/bitbake-hashclient | 7 +++++++ lib/hashserv/client.py | 5 +++++ lib/hashserv/server.py | 5 +++++ lib/hashserv/sqlalchemy.py | 10 ++++++++++ lib/hashserv/sqlite.py | 7 +++++++ lib/hashserv/tests.py | 8 ++++++++ 6 files changed, 42 insertions(+)