From f1fd8ef27565f51151b93925c573f1bcfd3c55e2 Mon Sep 17 00:00:00 2001 From: Silvan Calarco Date: Sat, 6 Jul 2024 17:10:15 +0200 Subject: [PATCH] DistroqueryAPI: getRepositoryPackages: added pagination from query string support --- src/DistroqueryAPI.cpp | 79 +++++++++++++++++++++++++++++++++++++----- src/DistroqueryAPI.hpp | 2 +- 2 files changed, 71 insertions(+), 10 deletions(-) diff --git a/src/DistroqueryAPI.cpp b/src/DistroqueryAPI.cpp index dc82225..5a4ab2d 100644 --- a/src/DistroqueryAPI.cpp +++ b/src/DistroqueryAPI.cpp @@ -104,11 +104,26 @@ json DistroqueryAPI::getPackageSourceDetailsById(configTag* ct, long id) { return j; } -json DistroqueryAPI::getRepositoryPackages(string repository) { +json DistroqueryAPI::getRepositoryPackages(string repository, int per_page, int page) { json j; string sql; sqlite3_stmt *stmt; + j["query"] = {}; + j["query"]["repository"] = repository; + j["query"]["per_page"] = per_page; + j["query"]["page"] = page; + + if (per_page < 1) { + j["error"] = "'per_page' query var must be >= 1"; + return j; + } + + if (page < 1) { + j["error"] = "'page' query var must be >= 1"; + return j; + } + struct configTag* ct = findRepositoryByTag(repository.c_str()); if (ct == NULL) { j["error"] = "repository with tag '" + repository + "' does not exist"; @@ -121,16 +136,29 @@ json DistroqueryAPI::getRepositoryPackages(string repository) { return j; } - sql = "SELECT * FROM sources ORDER BY name COLLATE NOCASE ASC"; + sql = "SELECT COUNT(*) OVER() AS total_count,* FROM sources ORDER BY name COLLATE NOCASE ASC" + " LIMIT " + to_string(per_page) + + " OFFSET " + to_string(per_page * (page -1)); if (sqlite3_prepare_v2(db, sql.c_str(), sql.length(), &stmt, NULL) == SQLITE_OK) { - j = json::array(); + json packages = json::array(); + long count = 0; while (sqlite3_step(stmt) == SQLITE_ROW) { + count++; + // Set total packages count + if (j["query"].find("total") == j["query"].end()) { + long total_count = sqlite3_column_int(stmt,sqlite3_find_column_id(stmt, NULL, "total_count")); + j["query"]["total"] = total_count; + j["query"]["pages"] = total_count / per_page + 1; + j["query"]["from"] = per_page * (page - 1) + 1; + } json package; package["name"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "name"))); package["summary"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "summary"))); - j.push_back(package); + packages.push_back(package); } sqlite3_finalize(stmt); + j["query"]["to"] = per_page * (page - 1) + count; + j["packages"] = packages; } else { j["error"] = "error preparing query '" + sql + "'"; return j; @@ -300,7 +328,7 @@ json DistroqueryAPI::getPackageDetails(string repository, string package, string } // Provides - sql = "SELECT * FROM provides,provided WHERE provides.id_package=" + to_string(id) + + sql = "SELECT * FROM provides,provided WHERE provides.id_package=" + to_string(id) + " AND provided.id=provides.id_provided ORDER BY provided.name"; j["provides"] = json::array(); if (sqlite3_prepare_v2(db, sql.c_str(), sql.length(), &stmt2, NULL) == SQLITE_OK) { @@ -375,15 +403,31 @@ void DistroqueryAPI::getApiResponse(string path_info) { cout << "Content-Type: application/json;charset=utf-8\n\n" << endl; string path, query_string; - std::vector path_split; + vector path_split; + map query_vars; - std::vector v = split(path_info, "?"); + vector v = split(path_info, "?"); if (v.size() > 0) { path = v[0]; path_split = split(path, "/"); } - if (v.size() > 1) query_string = v[1]; + + if (v.size() > 1) { + query_string = v[1]; + vector query_split = split(query_string, "&"); + + for (auto q:query_split) { + vector arg_split = split(q, "="); + if (arg_split.size() == 1) { + query_vars[arg_split[0]] = ""; + } else if (arg_split.size() == 2) { + query_vars[arg_split[0]] = arg_split[1]; + } else { + cerr << "Error parsing query_string entry '" << q << "'" << endl; + } + } + } if (path_split.size() > 0 ) { if (path_split[0] == "repositories") { @@ -395,7 +439,24 @@ void DistroqueryAPI::getApiResponse(string path_info) { // API service: repository if (path_split.size() != 2) sendErrorResponse("expected exactly 2 arguments for " + path_split[0]); - auto packages = getRepositoryPackages(path_split[1]); + // Pagination + unsigned int per_page = 100; + unsigned int page = 1; + if (query_vars.find("per_page") != query_vars.end()) { + try { + per_page = atoi(query_vars["per_page"].c_str()); + } catch (exception const & e) { + cerr << "Error converting '" << query_vars["per_page"] << " to int: " << e.what() << endl; + } + } + if (query_vars.find("page") != query_vars.end()) { + try { + page = atoi(query_vars["page"].c_str()); + } catch (exception const & e) { + cerr << "Error converting '" << query_vars["page"] << " to int: " << e.what() << endl; + } + } + auto packages = getRepositoryPackages(path_split[1], per_page, page); cout << packages.dump(); } else if (path_split[0] == "package") { // API service: package diff --git a/src/DistroqueryAPI.hpp b/src/DistroqueryAPI.hpp index dc1c60a..e71649b 100644 --- a/src/DistroqueryAPI.hpp +++ b/src/DistroqueryAPI.hpp @@ -41,7 +41,7 @@ class DistroqueryAPI { configTag *config; json configToJsonRepositories(); void sendErrorResponse(string message); - json getRepositoryPackages(string repository); + json getRepositoryPackages(string repository, int per_page, int page); json getPackageSourceDetailsById(configTag* ct, long id); json getPackageSourceDetails(string repository, string package); json getBuiltPackagesFromSourceID(configTag* ct, long id);