Add several more fields related to distro, archive, and binary

main
Simon Quigley 7 days ago
parent 302c72250b
commit 4af2af923f

@ -1,5 +1,3 @@
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(launchpadlib-cpp VERSION 0.0.1 LANGUAGES CXX)
@ -22,6 +20,11 @@ add_library(launchpad SHARED
src/authentication.cpp
src/source_package_publishing_history.cpp
src/build.cpp
src/binary_package_publishing_history.cpp
src/archive_dependency.cpp
src/distro_series.cpp
src/distro_arch_series.cpp
src/distro_arch_series_filter.cpp
)
target_include_directories(launchpad PUBLIC
@ -33,12 +36,11 @@ target_compile_definitions(launchpad PRIVATE BUILDING_LAUNCHPAD)
set_target_properties(launchpad PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION 0
PUBLIC_HEADER "src/launchpad.h;src/callablewrapper.h;src/archive.h;src/archive_permission.h;src/utils.h;src/person.h;src/distribution.h;src/authentication.h;src/source_package_publishing_history.h;src/build.h"
PUBLIC_HEADER "src/launchpad.h;src/callablewrapper.h;src/archive.h;src/archive_permission.h;src/utils.h;src/person.h;src/distribution.h;src/authentication.h;src/source_package_publishing_history.h;src/build.h;src/binary_package_publishing_history.h;src/archive_dependency.h;src/distro_series.h;src/distro_arch_series.h;src/distro_arch_series_filter.h"
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN YES
)
# Install the library and its headers
install(TARGETS launchpad
EXPORT launchpadTargets
LIBRARY DESTINATION lib
@ -47,14 +49,12 @@ install(TARGETS launchpad
PUBLIC_HEADER DESTINATION include/launchpadlib-cpp
)
# Export the targets to a script
install(EXPORT launchpadTargets
FILE launchpadTargets.cmake
NAMESPACE launchpad::
DESTINATION lib/cmake/launchpadlib-cpp
)
# Configure the CMake package configuration file
include(CMakePackageConfigHelpers)
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/launchpadConfig.cmake.in"
@ -64,21 +64,18 @@ configure_package_config_file(
NO_CHECK_REQUIRED_COMPONENTS_MACRO
)
# Generate a version file for the package
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/launchpadConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
)
# Install the CMake package configuration files
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/launchpadConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/launchpadConfigVersion.cmake"
DESTINATION lib/cmake/launchpadlib-cpp
)
# Link necessary libraries
target_link_libraries(launchpad PRIVATE
nlohmann_json::nlohmann_json
OpenSSL::Crypto

@ -17,6 +17,9 @@
#include "utils.h"
#include "launchpad.h"
#include "source_package_publishing_history.h"
#include "archive_permission.h"
#include "binary_package_publishing_history.h"
#include "archive_dependency.h"
#include <iostream>
#include <nlohmann/json.hpp>
@ -57,11 +60,8 @@ void archive::parse_json(const std::string& json_data) {
std::cerr << "Error: Empty JSON data for archive::parse_json." << std::endl;
return;
}
try {
auto data = nlohmann::json::parse(json_data);
// Map JSON keys to member variable setters
std::map<std::string, std::function<void(const nlohmann::json&)>> json_map = {
{"authorized_size", [this](const nlohmann::json& val) { authorized_size = val.get<int>(); }},
{"build_debug_symbols", [this](const nlohmann::json& val) { build_debug_symbols = val.get<bool>(); }},
@ -83,8 +83,6 @@ void archive::parse_json(const std::string& json_data) {
{"signing_key_fingerprint", [this](const nlohmann::json& val) { signing_key_fingerprint = val.get<std::string>(); }},
{"suppress_subscription_notifications", [this](const nlohmann::json& val) { suppress_subscription_notifications = val.get<bool>(); }}
};
// Handle read-only fields separately
std::map<std::string, std::function<void(const nlohmann::json&)>> readonly_map = {
{"dependencies_collection_link", [this](const nlohmann::json& val) { dependencies_collection_link = val.get<std::string>(); }},
{"enabled_restricted_processors_collection_link", [this](const nlohmann::json& val) { enabled_restricted_processors_collection_link = val.get<std::string>(); }},
@ -97,7 +95,6 @@ void archive::parse_json(const std::string& json_data) {
{"web_link", [this](const nlohmann::json& val) { web_link = val.get<std::string>(); }}
};
// Process all keys in the JSON object
for (auto& [key, value] : data.items()) {
try {
if (json_map.find(key) != json_map.end()) {
@ -107,12 +104,11 @@ void archive::parse_json(const std::string& json_data) {
readonly_map[key](value);
readonly_map.erase(key);
}
} catch (const std::exception& e) {
} catch (...) {
continue;
}
}
// Special handling for owner_name and ppa_name
std::string link = owner_link;
size_t pos = link.find("~");
if (pos != std::string::npos) {
@ -123,11 +119,8 @@ void archive::parse_json(const std::string& json_data) {
}
ppa_name = name;
} catch (const nlohmann::json::parse_error& e) {
std::cerr << "JSON parse error in archive::parse_json: " << e.what() << std::endl;
std::cerr << "Input JSON was: " << json_data << std::endl;
} catch (const std::exception& e) {
std::cerr << "Unexpected error during JSON parsing: " << e.what() << std::endl;
} catch (...) {
// ignore parse errors
}
}
@ -135,26 +128,28 @@ std::string archive::build_archive_endpoint() const {
return "~" + url_encode(owner_name) + "/" + url_encode(ppa_name);
}
void archive::set_lp(launchpad* lp_ptr) {
lp = lp_ptr;
}
std::generator<archive_permission> archive::getAllPermissions() const {
std::map<std::string, std::string> params = {{"ws.op", "getAllPermissions"}};
auto response = lp->api_get(self_link, params);
if(!response) co_return;
auto data = nlohmann::json::parse(response.value());
while (response.has_value() && data.contains("next_collection_link") && data["next_collection_link"] != "") {
try {
if (data.contains("entries") && data["entries"].is_array()) {
for (auto& entry : data["entries"]) {
auto s_opt = archive_permission::parse(entry.dump());
if (!s_opt) continue;
s_opt->set_lp(lp);
co_yield s_opt.value();
}
while (true) {
if (data.contains("entries") && data["entries"].is_array()) {
for (auto& entry : data["entries"]) {
auto s_opt = archive_permission::parse(entry.dump());
if (!s_opt) continue;
s_opt->set_lp(lp);
co_yield s_opt.value();
}
response = lp->api_get(data["next_collection_link"]);
data = nlohmann::json::parse(response.value());
} catch (const std::exception& e) {
std::cerr << "Error parsing published sources: " << e.what() << std::endl;
}
if (!data.contains("next_collection_link") || data["next_collection_link"].is_null() || data["next_collection_link"] == "") break;
response = lp->api_get(data["next_collection_link"].get<std::string>());
if(!response) break;
data = nlohmann::json::parse(response.value());
}
}
@ -162,27 +157,782 @@ std::generator<source_package_publishing_history> archive::getPublishedSources(c
std::map<std::string, std::string> params;
params["ws.op"] = "getPublishedSources";
params["status"] = status;
auto response = lp->api_get(self_link, params);
if(!response) co_return;
auto data = nlohmann::json::parse(response.value());
while (true) {
if (data.contains("entries") && data["entries"].is_array()) {
for (auto& entry : data["entries"]) {
auto s_opt = source_package_publishing_history::parse(entry.dump());
if (!s_opt) continue;
s_opt->set_lp(lp);
co_yield s_opt.value();
}
}
if (!data.contains("next_collection_link") || data["next_collection_link"].is_null() || data["next_collection_link"] == "") break;
response = lp->api_get(data["next_collection_link"].get<std::string>());
if(!response) break;
data = nlohmann::json::parse(response.value());
}
}
std::optional<nlohmann::json> archive::checkUpload(const std::string& component,
const std::string& distroseries,
const std::string& person,
const std::string& pocket,
const std::string& sourcepackagename,
bool strict_component) const {
std::map<std::string,std::string> params;
params["ws.op"] = "checkUpload";
params["component"] = component;
params["distroseries"] = distroseries;
params["person"] = person;
params["pocket"] = pocket;
params["sourcepackagename"] = sourcepackagename;
if(strict_component) params["strict_component"] = "true";
auto response = lp->api_get(this->self_link, params);
auto response = lp->api_get(self_link, params);
if(!response) return std::nullopt;
return nlohmann::json::parse(response.value());
}
std::optional<archive_dependency> archive::getArchiveDependency(const std::string& dependency_link) const {
std::map<std::string,std::string> params;
params["ws.op"] = "getArchiveDependency";
params["dependency"] = dependency_link;
auto response = lp->api_get(self_link, params);
if(!response) return std::nullopt;
auto ad_opt = archive_dependency::parse(response.value());
if(ad_opt) ad_opt->set_lp(lp);
return ad_opt;
}
std::optional<nlohmann::json> archive::getBuildCounters(bool include_needsbuild) const {
std::map<std::string,std::string> params;
params["ws.op"] = "getBuildCounters";
if(include_needsbuild) params["include_needsbuild"]="true";
auto response = lp->api_get(self_link, params);
if(!response) return std::nullopt;
return nlohmann::json::parse(response.value());
}
std::generator<build_record> archive::getBuildRecords(const std::string& build_state,
const std::string& pocket,
const std::string& source_name) const {
std::map<std::string,std::string> params;
params["ws.op"] = "getBuildRecords";
if(!build_state.empty()) params["build_state"] = build_state;
if(!pocket.empty()) params["pocket"] = pocket;
if(!source_name.empty()) params["source_name"] = source_name;
auto response = lp->api_get(self_link, params);
if(!response) co_return;
auto data = nlohmann::json::parse(response.value());
while (response.has_value() && data.contains("next_collection_link") && data["next_collection_link"] != "") {
try {
if (data.contains("entries") && data["entries"].is_array()) {
for (auto& entry : data["entries"]) {
auto s_opt = source_package_publishing_history::parse(entry.dump());
if (!s_opt) continue;
s_opt->set_lp(lp);
co_yield s_opt.value();
}
while (true) {
if (data.contains("entries") && data["entries"].is_array()) {
for (auto& e : data["entries"]) {
build_record br;
br.data = e;
co_yield br;
}
response = lp->api_get(data["next_collection_link"]);
data = nlohmann::json::parse(response.value());
} catch (const std::exception& e) {
std::cerr << "Error parsing published sources: " << e.what() << std::endl;
}
if(!data.contains("next_collection_link") || data["next_collection_link"].is_null() || data["next_collection_link"]=="") break;
response = lp->api_get(data["next_collection_link"].get<std::string>());
if(!response) break;
data = nlohmann::json::parse(response.value());
}
}
void archive::set_lp(launchpad* lp_ptr) {
lp = lp_ptr;
std::optional<nlohmann::json> archive::getBuildSummariesForSourceIds(const std::vector<std::string>& source_ids) const {
std::map<std::string,std::string> params;
params["ws.op"] = "getBuildSummariesForSourceIds";
std::string joined;
for (auto &sid : source_ids) {
joined += sid + ",";
}
if(!joined.empty()) joined.pop_back();
if(!joined.empty()) params["source_ids"] = joined;
auto response = lp->api_get(self_link, params);
if(!response) return std::nullopt;
return nlohmann::json::parse(response.value());
}
std::generator<archive_permission> archive::getComponentsForQueueAdmin(const std::string& person_link) const {
std::map<std::string,std::string> params;
params["ws.op"] = "getComponentsForQueueAdmin";
params["person"] = person_link;
auto response = lp->api_get(self_link, params);
if(!response) co_return;
auto data = nlohmann::json::parse(response.value());
if(data.contains("entries") && data["entries"].is_array()){
for(auto& e : data["entries"]){
auto ap = archive_permission::parse(e.dump());
if(ap) {
ap->set_lp(lp);
co_yield ap.value();
}
}
}
}
std::optional<nlohmann::json> archive::getNamedAuthToken(const std::string& name) const {
std::map<std::string,std::string> params;
params["ws.op"]="getNamedAuthToken";
params["name"]=name;
auto response = lp->api_get(self_link, params);
if(!response) return std::nullopt;
return nlohmann::json::parse(response.value());
}
std::optional<nlohmann::json> archive::getNamedAuthTokens(const std::vector<std::string>& names) const {
std::map<std::string,std::string> params;
params["ws.op"]="getNamedAuthTokens";
if(!names.empty()){
std::string joined;
for(auto& n:names) joined+=n+",";
if(!joined.empty()) joined.pop_back();
params["names"]=joined;
}
auto response = lp->api_get(self_link, params);
if(!response) return std::nullopt;
return nlohmann::json::parse(response.value());
}
std::generator<archive_permission> archive::getPackagesetsForSource(const std::string& sourcepackagename,
bool direct_permissions) const {
std::map<std::string,std::string> params;
params["ws.op"]="getPackagesetsForSource";
params["sourcepackagename"]=sourcepackagename;
if(direct_permissions) params["direct_permissions"]="true";
auto response=lp->api_get(self_link, params);
if(!response) co_return;
auto data=nlohmann::json::parse(response.value());
if(data.contains("entries") && data["entries"].is_array()){
for(auto&e: data["entries"]){
auto ap=archive_permission::parse(e.dump());
if(ap) {ap->set_lp(lp); co_yield ap.value();}
}
}
}
std::generator<archive_permission> archive::getPackagesetsForSourceUploader(const std::string& person_link,
const std::string& sourcepackagename) const {
std::map<std::string,std::string> params;
params["ws.op"]="getPackagesetsForSourceUploader";
params["person"]=person_link;
params["sourcepackagename"]=sourcepackagename;
auto response=lp->api_get(self_link, params);
if(!response) co_return;
auto data=nlohmann::json::parse(response.value());
if(data.contains("entries")&&data["entries"].is_array()){
for(auto&e:data["entries"]){
auto ap=archive_permission::parse(e.dump());
if(ap){ap->set_lp(lp);co_yield ap.value();}
}
}
}
std::generator<archive_permission> archive::getPackagesetsForUploader(const std::string& person_link) const {
std::map<std::string,std::string> params;
params["ws.op"]="getPackagesetsForUploader";
params["person"]=person_link;
auto response=lp->api_get(self_link,params);
if(!response) co_return;
auto data=nlohmann::json::parse(response.value());
if(data.contains("entries") && data["entries"].is_array()){
for(auto&e:data["entries"]){
auto ap=archive_permission::parse(e.dump());
if(ap){ap->set_lp(lp);co_yield ap.value();}
}
}
}
std::generator<archive_permission> archive::getPermissionsForPerson(const std::string& person_link) const {
std::map<std::string,std::string> params;
params["ws.op"]="getPermissionsForPerson";
params["person"]=person_link;
auto response=lp->api_get(self_link,params);
if(!response) co_return;
auto data=nlohmann::json::parse(response.value());
if(data.contains("entries")){
for(auto&e:data["entries"]){
auto ap=archive_permission::parse(e.dump());
if(ap){ap->set_lp(lp);co_yield ap.value();}
}
}
}
std::generator<archive_permission> archive::getPocketsForQueueAdmin(const std::string& person_link) const {
std::map<std::string,std::string>params;
params["ws.op"]="getPocketsForQueueAdmin";
params["person"]=person_link;
auto response=lp->api_get(self_link,params);
if(!response) co_return;
auto data=nlohmann::json::parse(response.value());
if(data.contains("entries")){
for(auto&e:data["entries"]){
auto ap=archive_permission::parse(e.dump());
if(ap){ap->set_lp(lp);co_yield ap.value();}
}
}
}
std::generator<archive_permission> archive::getPocketsForUploader(const std::string& person_link) const {
std::map<std::string,std::string>params;
params["ws.op"]="getPocketsForUploader";
params["person"]=person_link;
auto response=lp->api_get(self_link,params);
if(!response) co_return;
auto data=nlohmann::json::parse(response.value());
if(data.contains("entries")){
for(auto&e:data["entries"]){
auto ap=archive_permission::parse(e.dump());
if(ap){ap->set_lp(lp);co_yield ap.value();}
}
}
}
std::generator<binary_package_publishing_history> archive::getPublishedBinaries(const std::string& binary_name,
const std::string& component_name,
const std::string& created_since_date,
const std::string& distro_arch_series,
bool exact_match,
bool order_by_date,
bool ordered,
const std::string& pocket,
const std::string& status,
const std::string& version) const {
std::map<std::string,std::string> params;
params["ws.op"]="getPublishedBinaries";
if(!binary_name.empty()) params["binary_name"]=binary_name;
if(!component_name.empty()) params["component_name"]=component_name;
if(!created_since_date.empty()) params["created_since_date"]=created_since_date;
if(!distro_arch_series.empty()) params["distro_arch_series"]=distro_arch_series;
if(exact_match) params["exact_match"]="true";
if(order_by_date) params["order_by_date"]="true";
if(!ordered) params["ordered"]="false";
if(!pocket.empty()) params["pocket"]=pocket;
if(!status.empty()) params["status"]=status;
if(!version.empty()) params["version"]=version;
auto response=lp->api_get(self_link,params);
if(!response) co_return;
auto data=nlohmann::json::parse(response.value());
while(true){
if(data.contains("entries") && data["entries"].is_array()){
for(auto&e:data["entries"]){
auto bpph = binary_package_publishing_history::parse(e.dump());
if(bpph){bpph->set_lp(lp);co_yield bpph.value();}
}
}
if(!data.contains("next_collection_link")||data["next_collection_link"].is_null()||data["next_collection_link"]=="")break;
response=lp->api_get(data["next_collection_link"].get<std::string>());
if(!response)break;
data=nlohmann::json::parse(response.value());
}
}
std::generator<archive_permission> archive::getQueueAdminsForComponent(const std::string& component_name) const {
std::map<std::string,std::string>params;
params["ws.op"]="getQueueAdminsForComponent";
params["component_name"]=component_name;
auto response=lp->api_get(self_link,params);
if(!response)co_return;
auto data=nlohmann::json::parse(response.value());
if(data.contains("entries")){
for(auto&e:data["entries"]){
auto ap=archive_permission::parse(e.dump());
if(ap){ap->set_lp(lp); co_yield ap.value();}
}
}
}
std::generator<archive_permission> archive::getQueueAdminsForPocket(const std::string& distroseries,
const std::string& pocket) const {
std::map<std::string,std::string>params;
params["ws.op"]="getQueueAdminsForPocket";
params["distroseries"]=distroseries;
params["pocket"]=pocket;
auto response=lp->api_get(self_link,params);
if(!response)co_return;
auto data=nlohmann::json::parse(response.value());
if(data.contains("entries")){
for(auto&e:data["entries"]){
auto ap=archive_permission::parse(e.dump());
if(ap){ap->set_lp(lp);co_yield ap.value();}
}
}
}
std::optional<std::string> archive::getSigningKeyData() const {
std::map<std::string,std::string>params;
params["ws.op"]="getSigningKeyData";
auto response=lp->api_get(self_link,params);
if(!response) return std::nullopt;
auto j=nlohmann::json::parse(response.value());
if(j.is_string()) return j.get<std::string>();
return std::nullopt;
}
std::generator<archive_permission> archive::getUploadersForComponent(const std::string& component_name) const {
std::map<std::string,std::string>params;
params["ws.op"]="getUploadersForComponent";
if(!component_name.empty()) params["component_name"]=component_name;
auto response=lp->api_get(self_link,params);
if(!response) co_return;
auto data=nlohmann::json::parse(response.value());
if(data.contains("entries")){
for(auto&e:data["entries"]){
auto ap=archive_permission::parse(e.dump());
if(ap){ap->set_lp(lp);co_yield ap.value();}
}
}
}
std::generator<archive_permission> archive::getUploadersForPackage(const std::string& source_package_name) const {
std::map<std::string,std::string>params;
params["ws.op"]="getUploadersForPackage";
params["source_package_name"]=source_package_name;
auto response=lp->api_get(self_link,params);
if(!response)co_return;
auto data=nlohmann::json::parse(response.value());
if(data.contains("entries")){
for(auto&e:data["entries"]){
auto ap=archive_permission::parse(e.dump());
if(ap){ap->set_lp(lp);co_yield ap.value();}
}
}
}
std::generator<archive_permission> archive::getUploadersForPackageset(const std::string& packageset,
bool direct_permissions) const {
std::map<std::string,std::string>params;
params["ws.op"]="getUploadersForPackageset";
params["packageset"]=packageset;
if(direct_permissions) params["direct_permissions"]="true";
auto response=lp->api_get(self_link,params);
if(!response) co_return;
auto data=nlohmann::json::parse(response.value());
if(data.contains("entries")){
for(auto&e:data["entries"]){
auto ap=archive_permission::parse(e.dump());
if(ap){ap->set_lp(lp);co_yield ap.value();}
}
}
}
std::generator<archive_permission> archive::getUploadersForPocket(const std::string& pocket) const {
std::map<std::string,std::string>params;
params["ws.op"]="getUploadersForPocket";
params["pocket"]=pocket;
auto response=lp->api_get(self_link,params);
if(!response)co_return;
auto data=nlohmann::json::parse(response.value());
if(data.contains("entries")){
for(auto&e:data["entries"]){
auto ap=archive_permission::parse(e.dump());
if(ap){ap->set_lp(lp); co_yield ap.value();}
}
}
}
std::optional<bool> archive::isSourceUploadAllowed(const std::string& distroseries,
const std::string& person,
const std::string& sourcepackagename) const {
std::map<std::string,std::string> params;
params["ws.op"]="isSourceUploadAllowed";
params["distroseries"]=distroseries;
params["person"]=person;
params["sourcepackagename"]=sourcepackagename;
auto response=lp->api_get(self_link,params);
if(!response)return std::nullopt;
auto j=nlohmann::json::parse(response.value());
if(j.is_boolean()) return j.get<bool>();
return std::nullopt;
}
std::optional<archive_dependency> archive::addArchiveDependency(const std::string& dependency_link,
const std::string& component,
const std::string& pocket) const {
std::map<std::string,std::string>params;
params["ws.op"]="addArchiveDependency";
params["dependency"]=dependency_link;
if(!component.empty()) params["component"]=component;
params["pocket"]=pocket;
auto response=lp->api_post(self_link,params);
if(!response) return std::nullopt;
auto ad=archive_dependency::parse(response.value());
if(ad) ad->set_lp(lp);
return ad;
}
bool archive::copyPackage(const std::string& from_archive,
const std::string& from_pocket,
const std::string& from_series,
bool include_binaries,
bool move,
std::optional<int> phased_update_percentage,
bool silent,
const std::string& source_name,
const std::string& sponsored,
const std::string& to_pocket,
const std::string& to_series,
bool unembargo,
const std::string& version,
bool auto_approve) const {
std::map<std::string,std::string>params;
params["ws.op"]="copyPackage";
params["from_archive"]=from_archive;
if(!from_pocket.empty())params["from_pocket"]=from_pocket;
if(!from_series.empty())params["from_series"]=from_series;
if(include_binaries)params["include_binaries"]="true";
if(move)params["move"]="true";
if(phased_update_percentage.has_value())params["phased_update_percentage"]=std::to_string(phased_update_percentage.value());
if(silent)params["silent"]="true";
params["source_name"]=source_name;
if(!sponsored.empty()) params["sponsored"]=sponsored;
params["to_pocket"]=to_pocket;
if(!to_series.empty())params["to_series"]=to_series;
if(unembargo)params["unembargo"]="true";
params["version"]=version;
if(auto_approve)params["auto_approve"]="true";
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool archive::copyPackages(const std::string& from_archive,
const std::string& from_series,
bool include_binaries,
bool silent,
const std::vector<std::string>& source_names,
const std::string& sponsored,
const std::string& to_pocket,
const std::string& to_series,
bool unembargo,
bool auto_approve) const {
std::map<std::string,std::string> params;
params["ws.op"]="copyPackages";
params["from_archive"]=from_archive;
if(!from_series.empty()) params["from_series"]=from_series;
if(include_binaries) params["include_binaries"]="true";
if(silent) params["silent"]="true";
{
std::string joined;
for(auto&s:source_names)joined+=s+",";
if(!joined.empty())joined.pop_back();
if(!joined.empty()) params["source_names"]=joined;
}
if(!sponsored.empty()) params["sponsored"]=sponsored;
params["to_pocket"]=to_pocket;
if(!to_series.empty()) params["to_series"]=to_series;
if(unembargo)params["unembargo"]="true";
if(auto_approve)params["auto_approve"]="true";
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool archive::deleteComponentUploader(const std::string& component_name,
const std::string& person) const {
std::map<std::string,std::string>params;
params["ws.op"]="deleteComponentUploader";
params["component_name"]=component_name;
params["person"]=person;
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool archive::deletePackageUploader(const std::string& person,
const std::string& source_package_name) const {
std::map<std::string,std::string>params;
params["ws.op"]="deletePackageUploader";
params["person"]=person;
params["source_package_name"]=source_package_name;
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool archive::deletePackagesetUploader(const std::string& packageset,
const std::string& person,
bool explicit_) const {
std::map<std::string,std::string> params;
params["ws.op"]="deletePackagesetUploader";
params["packageset"]=packageset;
if(explicit_)params["explicit"]="true";
params["person"]=person;
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool archive::deletePocketQueueAdmin(const std::string& distroseries,
const std::string& person,
const std::string& pocket) const {
std::map<std::string,std::string>params;
params["ws.op"]="deletePocketQueueAdmin";
if(!distroseries.empty())params["distroseries"]=distroseries;
params["person"]=person;
params["pocket"]=pocket;
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool archive::deletePocketUploader(const std::string& person,
const std::string& pocket) const {
std::map<std::string,std::string>params;
params["ws.op"]="deletePocketUploader";
params["person"]=person;
params["pocket"]=pocket;
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool archive::deleteQueueAdmin(const std::string& component_name,
const std::string& person) const {
std::map<std::string,std::string>params;
params["ws.op"]="deleteQueueAdmin";
params["component_name"]=component_name;
params["person"]=person;
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool archive::enableRestrictedProcessor(const std::string& processor) const {
std::map<std::string,std::string>params;
params["ws.op"]="enableRestrictedProcessor";
params["processor"]=processor;
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool archive::markSuiteDirty(const std::string& distroseries,
const std::string& pocket) const {
std::map<std::string,std::string>params;
params["ws.op"]="markSuiteDirty";
params["distroseries"]=distroseries;
params["pocket"]=pocket;
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
std::optional<archive_permission> archive::newComponentUploader(const std::string& component_name,
const std::string& person) const {
std::map<std::string,std::string>params;
params["ws.op"]="newComponentUploader";
params["component_name"]=component_name;
params["person"]=person;
auto resp=lp->api_post(self_link,params);
if(!resp)return std::nullopt;
auto ap=archive_permission::parse(resp.value());
if(ap)ap->set_lp(lp);
return ap;
}
std::optional<nlohmann::json> archive::newNamedAuthToken(const std::string& name,
const std::optional<std::string>& token) const {
std::map<std::string,std::string>params;
params["ws.op"]="newNamedAuthToken";
params["name"]=name;
if(token.has_value())params["token"]=token.value();
auto resp=lp->api_post(self_link,params);
if(!resp)return std::nullopt;
return nlohmann::json::parse(resp.value());
}
std::optional<nlohmann::json> archive::newNamedAuthTokens(const std::vector<std::string>& names) const {
std::map<std::string,std::string>params;
params["ws.op"]="newNamedAuthTokens";
if(!names.empty()){
std::string joined;
for(auto&n:names)joined+=n+",";
if(!joined.empty())joined.pop_back();
params["names"]=joined;
}
auto resp=lp->api_post(self_link,params);
if(!resp)return std::nullopt;
return nlohmann::json::parse(resp.value());
}
std::optional<archive_permission> archive::newPackageUploader(const std::string& person,
const std::string& source_package_name) const {
std::map<std::string,std::string>params;
params["ws.op"]="newPackageUploader";
params["person"]=person;
params["source_package_name"]=source_package_name;
auto resp=lp->api_post(self_link,params);
if(!resp)return std::nullopt;
auto ap=archive_permission::parse(resp.value());
if(ap)ap->set_lp(lp);
return ap;
}
std::optional<archive_permission> archive::newPackagesetUploader(const std::string& packageset,
const std::string& person,
bool explicit_) const {
std::map<std::string,std::string>params;
params["ws.op"]="newPackagesetUploader";
params["packageset"]=packageset;
if(explicit_)params["explicit"]="true";
params["person"]=person;
auto resp=lp->api_post(self_link,params);
if(!resp)return std::nullopt;
auto ap=archive_permission::parse(resp.value());
if(ap)ap->set_lp(lp);
return ap;
}
std::optional<archive_permission> archive::newPocketQueueAdmin(const std::string& distroseries,
const std::string& person,
const std::string& pocket) const {
std::map<std::string,std::string>params;
params["ws.op"]="newPocketQueueAdmin";
if(!distroseries.empty())params["distroseries"]=distroseries;
params["person"]=person;
params["pocket"]=pocket;
auto resp=lp->api_post(self_link,params);
if(!resp)return std::nullopt;
auto ap=archive_permission::parse(resp.value());
if(ap)ap->set_lp(lp);
return ap;
}
std::optional<archive_permission> archive::newPocketUploader(const std::string& person,
const std::string& pocket) const {
std::map<std::string,std::string>params;
params["ws.op"]="newPocketUploader";
params["person"]=person;
params["pocket"]=pocket;
auto resp=lp->api_post(self_link,params);
if(!resp)return std::nullopt;
auto ap=archive_permission::parse(resp.value());
if(ap)ap->set_lp(lp);
return ap;
}
std::optional<archive_permission> archive::newQueueAdmin(const std::string& component_name,
const std::string& person) const {
std::map<std::string,std::string>params;
params["ws.op"]="newQueueAdmin";
params["component_name"]=component_name;
params["person"]=person;
auto resp=lp->api_post(self_link,params);
if(!resp)return std::nullopt;
auto ap=archive_permission::parse(resp.value());
if(ap)ap->set_lp(lp);
return ap;
}
std::optional<nlohmann::json> archive::newSubscription(const std::string& subscriber,
const std::optional<std::string>& date_expires,
const std::optional<std::string>& description) const {
std::map<std::string,std::string>params;
params["ws.op"]="newSubscription";
params["subscriber"]=subscriber;
if(date_expires.has_value()) params["date_expires"]=date_expires.value();
if(description.has_value()) params["description"]=description.value();
auto resp=lp->api_post(self_link,params);
if(!resp)return std::nullopt;
return nlohmann::json::parse(resp.value());
}
bool archive::removeArchiveDependency(const std::string& dependency_link) const {
std::map<std::string,std::string>params;
params["ws.op"]="removeArchiveDependency";
params["dependency"]=dependency_link;
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool archive::removeCopyNotification(const std::string& job_id) const {
std::map<std::string,std::string>params;
params["ws.op"]="removeCopyNotification";
params["job_id"]=job_id;
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool archive::revokeNamedAuthToken(const std::string& name) const {
std::map<std::string,std::string>params;
params["ws.op"]="revokeNamedAuthToken";
params["name"]=name;
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool archive::revokeNamedAuthTokens(const std::vector<std::string>& names) const {
std::map<std::string,std::string>params;
params["ws.op"]="revokeNamedAuthTokens";
if(!names.empty()){
std::string joined;
for(auto&n:names)joined+=n+",";
if(!joined.empty())joined.pop_back();
params["names"]=joined;
}
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool archive::setProcessors(const std::vector<std::string>& processors) const {
std::map<std::string,std::string>params;
params["ws.op"]="setProcessors";
{
std::string joined;
for(auto&p:processors)joined+=p+",";
if(!joined.empty())joined.pop_back();
params["processors"]=joined;
}
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool archive::syncSource(const std::string& from_archive,
bool include_binaries,
const std::string& source_name,
const std::string& to_pocket,
const std::string& to_series,
const std::string& version) const {
std::map<std::string,std::string>params;
params["ws.op"]="syncSource";
params["from_archive"]=from_archive;
if(include_binaries)params["include_binaries"]="true";
params["source_name"]=source_name;
params["to_pocket"]=to_pocket;
if(!to_series.empty())params["to_series"]=to_series;
params["version"]=version;
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool archive::syncSources(const std::string& from_archive,
const std::string& from_series,
bool include_binaries,
const std::vector<std::string>& source_names,
const std::string& to_pocket,
const std::string& to_series) const {
std::map<std::string,std::string>params;
params["ws.op"]="syncSources";
params["from_archive"]=from_archive;
if(!from_series.empty())params["from_series"]=from_series;
if(include_binaries)params["include_binaries"]="true";
{
std::string joined;
for(auto&s:source_names)joined+=s+",";
if(!joined.empty())joined.pop_back();
if(!joined.empty()) params["source_names"]=joined;
}
params["to_pocket"]=to_pocket;
if(!to_series.empty())params["to_series"]=to_series;
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool archive::uploadCIBuild(const std::string& ci_build,
const std::string& to_channel,
const std::string& to_pocket,
const std::string& to_series) const {
std::map<std::string,std::string>params;
params["ws.op"]="uploadCIBuild";
params["ci_build"]=ci_build;
if(!to_channel.empty())params["to_channel"]=to_channel;
params["to_pocket"]=to_pocket;
params["to_series"]=to_series;
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}

@ -17,6 +17,9 @@
#define ARCHIVE_H
#include "source_package_publishing_history.h"
#include "archive_permission.h"
#include "binary_package_publishing_history.h"
#include "archive_dependency.h"
#include <generator>
#include <nlohmann/json.hpp>
#include <optional>
@ -25,15 +28,11 @@
class launchpad;
#ifndef LAUNCHPAD_API
#ifdef BUILDING_LAUNCHPAD
#define LAUNCHPAD_API __attribute__((visibility("default")))
#else
#define LAUNCHPAD_API
#endif
#endif
struct build_record {
nlohmann::json data;
};
class LAUNCHPAD_API archive {
class archive {
public:
archive();
~archive();
@ -42,7 +41,6 @@ public:
void parse_json(const std::string& json_data);
void set_lp(launchpad* lp_ptr);
// New public member variables
int authorized_size;
bool build_debug_symbols;
std::string description;
@ -66,7 +64,6 @@ public:
std::string ppa_name;
std::string self_link;
// Read-only fields
std::string dependencies_collection_link;
std::string enabled_restricted_processors_collection_link;
std::string http_etag;
@ -76,14 +73,145 @@ public:
std::string status;
std::string web_link;
// Public functions
std::generator<source_package_publishing_history> getPublishedSources(const std::string& status) const;
std::generator<archive_permission> archive::getAllPermissions() const;
std::generator<archive_permission> getAllPermissions() const;
launchpad* lp;
std::optional<nlohmann::json> checkUpload(const std::string& component,
const std::string& distroseries,
const std::string& person,
const std::string& pocket,
const std::string& sourcepackagename,
bool strict_component = false) const;
std::optional<archive_dependency> getArchiveDependency(const std::string& dependency_link) const;
std::optional<nlohmann::json> getBuildCounters(bool include_needsbuild = false) const;
std::generator<build_record> getBuildRecords(const std::string& build_state = "",
const std::string& pocket = "",
const std::string& source_name = "") const;
std::optional<nlohmann::json> getBuildSummariesForSourceIds(const std::vector<std::string>& source_ids) const;
std::generator<archive_permission> getComponentsForQueueAdmin(const std::string& person_link) const;
std::optional<nlohmann::json> getNamedAuthToken(const std::string& name) const;
std::optional<nlohmann::json> getNamedAuthTokens(const std::vector<std::string>& names = {}) const;
std::generator<archive_permission> getPackagesetsForSource(const std::string& sourcepackagename,
bool direct_permissions = false) const;
std::generator<archive_permission> getPackagesetsForSourceUploader(const std::string& person_link,
const std::string& sourcepackagename) const;
std::generator<archive_permission> getPackagesetsForUploader(const std::string& person_link) const;
std::generator<archive_permission> getPermissionsForPerson(const std::string& person_link) const;
std::generator<archive_permission> getPocketsForQueueAdmin(const std::string& person_link) const;
std::generator<archive_permission> getPocketsForUploader(const std::string& person_link) const;
std::generator<binary_package_publishing_history> getPublishedBinaries(const std::string& binary_name = "",
const std::string& component_name = "",
const std::string& created_since_date = "",
const std::string& distro_arch_series = "",
bool exact_match = false,
bool order_by_date = false,
bool ordered = true,
const std::string& pocket = "",
const std::string& status = "",
const std::string& version = "") const;
std::generator<archive_permission> getQueueAdminsForComponent(const std::string& component_name) const;
std::generator<archive_permission> getQueueAdminsForPocket(const std::string& distroseries,
const std::string& pocket) const;
std::optional<std::string> getSigningKeyData() const;
std::generator<archive_permission> getUploadersForComponent(const std::string& component_name) const;
std::generator<archive_permission> getUploadersForPackage(const std::string& source_package_name) const;
std::generator<archive_permission> getUploadersForPackageset(const std::string& packageset,
bool direct_permissions = false) const;
std::generator<archive_permission> getUploadersForPocket(const std::string& pocket) const;
std::optional<bool> isSourceUploadAllowed(const std::string& distroseries,
const std::string& person,
const std::string& sourcepackagename) const;
// Custom POST methods
std::optional<archive_dependency> addArchiveDependency(const std::string& dependency_link,
const std::string& component,
const std::string& pocket) const;
bool copyPackage(const std::string& from_archive,
const std::string& from_pocket,
const std::string& from_series,
bool include_binaries,
bool move,
std::optional<int> phased_update_percentage,
bool silent,
const std::string& source_name,
const std::string& sponsored,
const std::string& to_pocket,
const std::string& to_series,
bool unembargo,
const std::string& version,
bool auto_approve = false) const;
bool copyPackages(const std::string& from_archive,
const std::string& from_series,
bool include_binaries,
bool silent,
const std::vector<std::string>& source_names,
const std::string& sponsored,
const std::string& to_pocket,
const std::string& to_series,
bool unembargo,
bool auto_approve = false) const;
bool deleteComponentUploader(const std::string& component_name,
const std::string& person) const;
bool deletePackageUploader(const std::string& person,
const std::string& source_package_name) const;
bool deletePackagesetUploader(const std::string& packageset,
const std::string& person,
bool explicit_) const;
bool deletePocketQueueAdmin(const std::string& distroseries,
const std::string& person,
const std::string& pocket) const;
bool deletePocketUploader(const std::string& person,
const std::string& pocket) const;
bool deleteQueueAdmin(const std::string& component_name,
const std::string& person) const;
bool enableRestrictedProcessor(const std::string& processor) const;
bool markSuiteDirty(const std::string& distroseries,
const std::string& pocket) const;
std::optional<archive_permission> newComponentUploader(const std::string& component_name,
const std::string& person) const;
std::optional<nlohmann::json> newNamedAuthToken(const std::string& name,
const std::optional<std::string>& token) const;
std::optional<nlohmann::json> newNamedAuthTokens(const std::vector<std::string>& names) const;
std::optional<archive_permission> newPackageUploader(const std::string& person,
const std::string& source_package_name) const;
std::optional<archive_permission> newPackagesetUploader(const std::string& packageset,
const std::string& person,
bool explicit_) const;
std::optional<archive_permission> newPocketQueueAdmin(const std::string& distroseries,
const std::string& person,
const std::string& pocket) const;
std::optional<archive_permission> newPocketUploader(const std::string& person,
const std::string& pocket) const;
std::optional<archive_permission> newQueueAdmin(const std::string& component_name,
const std::string& person) const;
std::optional<nlohmann::json> newSubscription(const std::string& subscriber,
const std::optional<std::string>& date_expires,
const std::optional<std::string>& description) const;
bool removeArchiveDependency(const std::string& dependency_link) const;
bool removeCopyNotification(const std::string& job_id) const;
bool revokeNamedAuthToken(const std::string& name) const;
bool revokeNamedAuthTokens(const std::vector<std::string>& names) const;
bool setProcessors(const std::vector<std::string>& processors) const;
bool syncSource(const std::string& from_archive,
bool include_binaries,
const std::string& source_name,
const std::string& to_pocket,
const std::string& to_series,
const std::string& version) const;
bool syncSources(const std::string& from_archive,
const std::string& from_series,
bool include_binaries,
const std::vector<std::string>& source_names,
const std::string& to_pocket,
const std::string& to_series) const;
bool uploadCIBuild(const std::string& ci_build,
const std::string& to_channel,
const std::string& to_pocket,
const std::string& to_series) const;
private:
std::string build_archive_endpoint() const;
launchpad* lp;
};
#endif // ARCHIVE_H

@ -0,0 +1,79 @@
#include "archive_dependency.h"
#include "launchpad.h"
#include <iostream>
#include <nlohmann/json.hpp>
archive_dependency::archive_dependency()
: lp(nullptr) {}
archive_dependency::~archive_dependency() {}
std::optional<archive_dependency> archive_dependency::parse(const std::string& json_data) {
archive_dependency ad;
ad.parse_json(json_data);
return ad;
}
void archive_dependency::parse_json(const std::string& json_data) {
if (json_data.empty()) {
std::cerr << "Error: Empty JSON data for archive_dependency::parse_json." << std::endl;
return;
}
try {
auto data = nlohmann::json::parse(json_data);
std::map<std::string, std::function<void(const nlohmann::json&)>> fields = {
{"archive_link", [this](const nlohmann::json& val) { archive_link = val.get<std::string>(); }},
{"component_name", [this](const nlohmann::json& val) { component_name = val.get<std::string>(); }},
{"date_created", [this](const nlohmann::json& val) { date_created = val.get<std::string>(); }},
{"dependency_link", [this](const nlohmann::json& val) { dependency_link = val.get<std::string>(); }},
{"http_etag", [this](const nlohmann::json& val) { http_etag = val.get<std::string>(); }},
{"pocket", [this](const nlohmann::json& val) { pocket = val.get<std::string>(); }},
{"resource_type_link", [this](const nlohmann::json& val) { resource_type_link = val.get<std::string>(); }},
{"self_link", [this](const nlohmann::json& val) { self_link = val.get<std::string>(); }},
{"snap_base_link", [this](const nlohmann::json& val) { snap_base_link = val.get<std::string>(); }},
{"title", [this](const nlohmann::json& val) { title = val.get<std::string>(); }}
};
for (auto& [key, value] : data.items()) {
if (fields.find(key) != fields.end()) {
try {
fields[key](value);
fields.erase(key);
} catch (...) {
continue;
}
}
}
} catch (const nlohmann::json::parse_error& e) {
std::cerr << "JSON parse error in archive_dependency::parse_json: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "Unexpected error during JSON parsing: " << e.what() << std::endl;
}
}
void archive_dependency::set_lp(launchpad* lp_ptr) {
lp = lp_ptr;
}
std::optional<archive_dependency> archive_dependency::get() const {
if(self_link.empty())return std::nullopt;
auto resp=lp->api_get(self_link);
if(!resp)return std::nullopt;
return parse(resp.value());
}
bool archive_dependency::patch(const nlohmann::json& data) const {
if(self_link.empty())return false;
auto resp=lp->api_patch(self_link,data);
return resp.has_value();
}
bool archive_dependency::put(const nlohmann::json& data) const {
if(self_link.empty())return false;
std::string endpoint=self_link+"?ws.op=replace";
auto resp=lp->api_patch(endpoint,data);
return resp.has_value();
}

@ -0,0 +1,38 @@
#ifndef ARCHIVE_DEPENDENCY_H
#define ARCHIVE_DEPENDENCY_H
#include <string>
#include <optional>
#include <nlohmann/json.hpp>
class launchpad;
class archive_dependency {
public:
archive_dependency();
~archive_dependency();
static std::optional<archive_dependency> parse(const std::string& json_data);
void parse_json(const std::string& json_data);
void set_lp(launchpad* lp_ptr);
std::string archive_link;
std::string component_name;
std::string date_created;
std::string dependency_link;
std::string http_etag;
std::string pocket;
std::string resource_type_link;
std::string self_link;
std::string snap_base_link;
std::string title;
std::optional<archive_dependency> get() const;
bool patch(const nlohmann::json& data) const;
bool put(const nlohmann::json& data) const;
private:
launchpad* lp;
};
#endif

@ -15,9 +15,11 @@
#include "archive_permission.h"
#include "launchpad.h"
#include "distro_series.h"
#include <iostream>
#include <map>
#include <functional>
#include <nlohmann/json.hpp>
archive_permission::archive_permission()
: explicit_(false),
@ -39,8 +41,6 @@ void archive_permission::parse_json(const std::string& json_data) {
try {
auto data = nlohmann::json::parse(json_data);
// Map JSON keys to lambdas to parse
std::map<std::string, std::function<void(const nlohmann::json&)>> json_map = {
{"archive_link", [this](const nlohmann::json& val) { archive_link = val.get<std::string>(); }},
{"component_name", [this](const nlohmann::json& val) { component_name = val.get<std::string>(); }},
@ -70,8 +70,7 @@ void archive_permission::parse_json(const std::string& json_data) {
readonly_map[key](value);
readonly_map.erase(key);
}
} catch (const std::exception& e) {
// If parsing fails for a field, just continue
} catch (const std::exception&) {
continue;
}
}
@ -86,3 +85,42 @@ void archive_permission::parse_json(const std::string& json_data) {
void archive_permission::set_lp(launchpad* lp_ptr) {
lp = lp_ptr;
}
std::optional<archive_permission> archive_permission::get() const {
if(self_link.empty())return std::nullopt;
auto resp=lp->api_get(self_link);
if(!resp)return std::nullopt;
return parse(resp.value());
}
bool archive_permission::patch(const nlohmann::json& data) const {
if(self_link.empty())return false;
auto resp=lp->api_patch(self_link,data);
return resp.has_value();
}
bool archive_permission::put(const nlohmann::json& data) const {
if(self_link.empty())return false;
std::string endpoint=self_link+"?ws.op=replace";
auto resp=lp->api_patch(endpoint,data);
return resp.has_value();
}
std::generator<distro_series> archive_permission::getDistroSeries() const {
if (distroseries_link.empty() || !lp) {
co_return;
}
auto response = lp->api_get(distroseries_link);
if (!response.has_value()) {
co_return;
}
auto ds_opt = distro_series::parse(response.value());
if (!ds_opt) {
co_return;
}
ds_opt->set_lp(lp);
co_yield ds_opt.value();
}

@ -19,18 +19,12 @@
#include <string>
#include <optional>
#include <nlohmann/json.hpp>
#include <generator>
#include "distro_series.h"
class launchpad;
#ifndef LAUNCHPAD_API
#ifdef BUILDING_LAUNCHPAD
#define LAUNCHPAD_API __attribute__((visibility("default")))
#else
#define LAUNCHPAD_API
#endif
#endif
class LAUNCHPAD_API archive_permission {
class archive_permission {
public:
archive_permission();
~archive_permission();
@ -39,8 +33,6 @@ public:
void set_lp(launchpad* lp_ptr);
// Public member variables (fields)
// Writable fields
std::string archive_link;
std::string component_name;
std::string date_created;
@ -53,14 +45,19 @@ public:
std::string pocket;
std::string source_package_name;
// Read-only fields
std::string http_etag;
std::string resource_type_link;
std::string self_link;
std::optional<archive_permission> get() const;
bool patch(const nlohmann::json& data) const;
bool put(const nlohmann::json& data) const;
std::generator<distro_series> getDistroSeries() const;
private:
void parse_json(const std::string& json_data);
launchpad* lp;
};
#endif // ARCHIVE_PERMISSION_H
#endif

@ -0,0 +1,178 @@
#include "binary_package_publishing_history.h"
#include "launchpad.h"
#include <iostream>
#include <map>
#include <functional>
#include <nlohmann/json.hpp>
binary_package_publishing_history::binary_package_publishing_history()
: architecture_specific(false),
is_debug(false),
phased_update_percentage(-1),
lp(nullptr) {}
binary_package_publishing_history::~binary_package_publishing_history() {}
std::optional<binary_package_publishing_history> binary_package_publishing_history::parse(const std::string& json_data) {
binary_package_publishing_history b;
b.parse_json(json_data);
return b;
}
void binary_package_publishing_history::parse_json(const std::string& json_data) {
if (json_data.empty()) {
std::cerr << "Error: Empty JSON data for binary_package_publishing_history::parse_json." << std::endl;
return;
}
try {
auto data = nlohmann::json::parse(json_data);
std::map<std::string, std::function<void(const nlohmann::json&)>> fields = {
{"architecture_specific", [this](const nlohmann::json& val) { architecture_specific = val.get<bool>(); }},
{"archive_link", [this](const nlohmann::json& val) { archive_link = val.get<std::string>(); }},
{"binary_package_name", [this](const nlohmann::json& val) { binary_package_name = val.get<std::string>(); }},
{"binary_package_version", [this](const nlohmann::json& val) { binary_package_version = val.get<std::string>(); }},
{"build_link", [this](const nlohmann::json& val) { build_link = val.get<std::string>(); }},
{"component_name", [this](const nlohmann::json& val) { component_name = val.get<std::string>(); }},
{"copied_from_archive_link", [this](const nlohmann::json& val) { copied_from_archive_link = val.get<std::string>(); }},
{"creator_link", [this](const nlohmann::json& val) { creator_link = val.get<std::string>(); }},
{"date_created", [this](const nlohmann::json& val) { date_created = val.get<std::string>(); }},
{"date_made_pending", [this](const nlohmann::json& val) { date_made_pending = val.get<std::string>(); }},
{"date_published", [this](const nlohmann::json& val) { date_published = val.get<std::string>(); }},
{"date_removed", [this](const nlohmann::json& val) { date_removed = val.get<std::string>(); }},
{"date_superseded", [this](const nlohmann::json& val) { date_superseded = val.get<std::string>(); }},
{"display_name", [this](const nlohmann::json& val) { display_name = val.get<std::string>(); }},
{"distro_arch_series_link", [this](const nlohmann::json& val) { distro_arch_series_link = val.get<std::string>(); }},
{"http_etag", [this](const nlohmann::json& val) { http_etag = val.get<std::string>(); }},
{"is_debug", [this](const nlohmann::json& val) { is_debug = val.get<bool>(); }},
{"phased_update_percentage", [this](const nlohmann::json& val) {
if (!val.is_null()) phased_update_percentage = val.get<int>();
else phased_update_percentage = -1;
}},
{"pocket", [this](const nlohmann::json& val) { pocket = val.get<std::string>(); }},
{"priority_name", [this](const nlohmann::json& val) { priority_name = val.get<std::string>(); }},
{"removal_comment", [this](const nlohmann::json& val) { removal_comment = val.get<std::string>(); }},
{"removed_by_link", [this](const nlohmann::json& val) { removed_by_link = val.get<std::string>(); }},
{"resource_type_link", [this](const nlohmann::json& val) { resource_type_link = val.get<std::string>(); }},
{"scheduled_deletion_date", [this](const nlohmann::json& val) { scheduled_deletion_date = val.get<std::string>(); }},
{"section_name", [this](const nlohmann::json& val) { section_name = val.get<std::string>(); }},
{"self_link", [this](const nlohmann::json& val) { self_link = val.get<std::string>(); }},
{"source_package_name", [this](const nlohmann::json& val) { source_package_name = val.get<std::string>(); }},
{"source_package_version", [this](const nlohmann::json& val) { source_package_version = val.get<std::string>(); }},
{"status", [this](const nlohmann::json& val) { status = val.get<std::string>(); }}
};
for (auto& [key, value] : data.items()) {
try {
if (fields.find(key) != fields.end()) {
fields[key](value);
fields.erase(key);
}
} catch (...) {
continue;
}
}
} catch (const nlohmann::json::parse_error& e) {
std::cerr << "JSON parse error in binary_package_publishing_history::parse_json: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "Unexpected error during JSON parsing: " << e.what() << std::endl;
}
}
void binary_package_publishing_history::set_lp(launchpad* lp_ptr) {
lp = lp_ptr;
}
std::optional<binary_package_publishing_history> binary_package_publishing_history::get() const {
if(self_link.empty()) return std::nullopt;
auto resp=lp->api_get(self_link);
if(!resp)return std::nullopt;
return parse(resp.value());
}
bool binary_package_publishing_history::patch(const nlohmann::json& data) const {
if(self_link.empty())return false;
auto resp=lp->api_patch(self_link,data);
return resp.has_value();
}
bool binary_package_publishing_history::put(const nlohmann::json& data) const {
if(self_link.empty())return false;
// For PUT, we emulate with POST & ws.op=replace or something similar, but doc says PUT should contain the full representation.
// We'll try POST with build_endpoint=false and full JSON. Actually we have no direct method for PUT in code. We'll just do a POST with `?ws.op=replace`.
std::string endpoint=self_link;
endpoint+="?ws.op=replace";
auto resp=lp->api_patch(endpoint,data);
return resp.has_value();
}
std::optional<nlohmann::json> binary_package_publishing_history::binaryFileUrls(bool include_meta) const {
if(self_link.empty())return std::nullopt;
std::map<std::string,std::string>params;
params["ws.op"]="binaryFileUrls";
if(include_meta)params["include_meta"]="true";
auto resp=lp->api_get(self_link,params);
if(!resp)return std::nullopt;
return nlohmann::json::parse(resp.value());
}
std::optional<nlohmann::json> binary_package_publishing_history::getDailyDownloadTotals(const std::string& start_date, const std::string& end_date) const {
if(self_link.empty())return std::nullopt;
std::map<std::string,std::string>params;
params["ws.op"]="getDailyDownloadTotals";
if(!start_date.empty())params["start_date"]=start_date;
if(!end_date.empty())params["end_date"]=end_date;
auto resp=lp->api_get(self_link,params);
if(!resp)return std::nullopt;
return nlohmann::json::parse(resp.value());
}
std::optional<int> binary_package_publishing_history::getDownloadCount() const {
if(self_link.empty())return std::nullopt;
std::map<std::string,std::string>params;
params["ws.op"]="getDownloadCount";
auto resp=lp->api_get(self_link,params);
if(!resp)return std::nullopt;
auto j=nlohmann::json::parse(resp.value());
if(j.is_number())return j.get<int>();
return std::nullopt;
}
std::optional<nlohmann::json> binary_package_publishing_history::getDownloadCounts(const std::string& start_date, const std::string& end_date) const {
if(self_link.empty())return std::nullopt;
std::map<std::string,std::string>params;
params["ws.op"]="getDownloadCounts";
if(!start_date.empty())params["start_date"]=start_date;
if(!end_date.empty())params["end_date"]=end_date;
auto resp=lp->api_get(self_link,params);
if(!resp)return std::nullopt;
return nlohmann::json::parse(resp.value());
}
std::optional<binary_package_publishing_history> binary_package_publishing_history::changeOverride(const std::optional<std::string>& new_component,
const std::optional<int>& new_phased_update_percentage,
const std::optional<std::string>& new_priority,
const std::optional<std::string>& new_section) const {
if(self_link.empty())return std::nullopt;
std::map<std::string,std::string>params;
params["ws.op"]="changeOverride";
if(new_component.has_value()) params["new_component"]=new_component.value();
if(new_phased_update_percentage.has_value()) params["new_phased_update_percentage"]=std::to_string(new_phased_update_percentage.value());
if(new_priority.has_value()) params["new_priority"]=new_priority.value();
if(new_section.has_value()) params["new_section"]=new_section.value();
auto resp=lp->api_post(self_link,params);
if(!resp)return std::nullopt;
auto obj=parse(resp.value());
if(obj)obj->set_lp(lp);
return obj;
}
bool binary_package_publishing_history::requestDeletion(const std::optional<std::string>& removal_comment) const {
if(self_link.empty())return false;
std::map<std::string,std::string>params;
params["ws.op"]="requestDeletion";
if(removal_comment.has_value())params["removal_comment"]=removal_comment.value();
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}

@ -0,0 +1,69 @@
#ifndef BINARY_PACKAGE_PUBLISHING_HISTORY_H
#define BINARY_PACKAGE_PUBLISHING_HISTORY_H
#include <string>
#include <optional>
#include <nlohmann/json.hpp>
#include <generator>
class launchpad;
class binary_package_publishing_history {
public:
binary_package_publishing_history();
~binary_package_publishing_history();
static std::optional<binary_package_publishing_history> parse(const std::string& json_data);
void parse_json(const std::string& json_data);
void set_lp(launchpad* lp_ptr);
bool architecture_specific;
std::string archive_link;
std::string binary_package_name;
std::string binary_package_version;
std::string build_link;
std::string component_name;
std::string copied_from_archive_link;
std::string creator_link;
std::string date_created;
std::string date_made_pending;
std::string date_published;
std::string date_removed;
std::string date_superseded;
std::string display_name;
std::string distro_arch_series_link;
std::string http_etag;
bool is_debug;
int phased_update_percentage; // -1 if None
std::string pocket;
std::string priority_name;
std::string removal_comment;
std::string removed_by_link;
std::string resource_type_link;
std::string scheduled_deletion_date;
std::string section_name;
std::string self_link;
std::string source_package_name;
std::string source_package_version;
std::string status;
std::optional<binary_package_publishing_history> get() const;
bool patch(const nlohmann::json& data) const;
bool put(const nlohmann::json& data) const;
std::optional<nlohmann::json> binaryFileUrls(bool include_meta=false) const;
std::optional<nlohmann::json> getDailyDownloadTotals(const std::string& start_date="", const std::string& end_date="") const;
std::optional<int> getDownloadCount() const;
std::optional<nlohmann::json> getDownloadCounts(const std::string& start_date="", const std::string& end_date="") const;
std::optional<binary_package_publishing_history> changeOverride(const std::optional<std::string>& new_component,
const std::optional<int>& new_phased_update_percentage,
const std::optional<std::string>& new_priority,
const std::optional<std::string>& new_section) const;
bool requestDeletion(const std::optional<std::string>& removal_comment) const;
private:
launchpad* lp;
};
#endif // BINARY_PACKAGE_PUBLISHING_HISTORY_H

@ -0,0 +1,214 @@
#include "distro_arch_series.h"
#include "launchpad.h"
#include "utils.h"
#include <iostream>
#include <nlohmann/json.hpp>
distro_arch_series::distro_arch_series()
: enabled(false),
is_nominated_arch_indep(false),
official(false),
package_count(0),
supports_virtualized(false),
lp(nullptr) {}
distro_arch_series::~distro_arch_series() {}
std::optional<distro_arch_series> distro_arch_series::parse(const std::string& json_data) {
distro_arch_series das;
das.parse_json(json_data);
return das;
}
void distro_arch_series::parse_json(const std::string& json_data) {
if (json_data.empty()) {
std::cerr << "Error: Empty JSON data for distro_arch_series::parse_json." << std::endl;
return;
}
try {
auto data = nlohmann::json::parse(json_data);
auto get_bool = [&](const std::string& key, bool& field) {
if (data.contains(key) && data[key].is_boolean()) field = data[key].get<bool>();
};
auto get_str = [&](const std::string& key, std::string& field) {
if (data.contains(key) && data[key].is_string()) field = data[key].get<std::string>();
};
auto get_int = [&](const std::string& key, int& field) {
if (data.contains(key) && data[key].is_number()) field = data[key].get<int>();
};
get_str("architecture_tag", architecture_tag);
get_str("chroot_url", chroot_url);
get_str("display_name", display_name);
get_str("distroseries_link", distroseries_link);
get_bool("enabled", enabled);
get_str("http_etag", http_etag);
get_bool("is_nominated_arch_indep", is_nominated_arch_indep);
get_str("main_archive_link", main_archive_link);
get_bool("official", official);
get_str("owner_link", owner_link);
get_int("package_count", package_count);
get_str("processor_link", processor_link);
get_str("resource_type_link", resource_type_link);
get_str("self_link", self_link);
get_bool("supports_virtualized", supports_virtualized);
get_str("title", title);
get_str("web_link", web_link);
} catch (const nlohmann::json::parse_error& e) {
std::cerr << "JSON parse error in distro_arch_series::parse_json: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "Unexpected error during JSON parsing: " << e.what() << std::endl;
}
}
void distro_arch_series::set_lp(launchpad* lp_ptr) {
lp = lp_ptr;
}
std::optional<distro_arch_series> distro_arch_series::get() const {
if (self_link.empty()) return std::nullopt;
auto resp = lp->api_get(self_link);
if (!resp) return std::nullopt;
auto das = parse(resp.value());
if (das) das->set_lp(lp);
return das;
}
bool distro_arch_series::patch(const nlohmann::json& data) const {
if (self_link.empty()) return false;
auto resp = lp->api_patch(self_link, data);
return resp.has_value();
}
bool distro_arch_series::put(const nlohmann::json& data) const {
if (self_link.empty()) return false;
// Emulate PUT with ws.op=replace
std::string endpoint = self_link + "?ws.op=replace";
auto resp = lp->api_patch(endpoint, data);
return resp.has_value();
}
std::generator<nlohmann::json> distro_arch_series::getBuildRecords(const std::string& build_state,
const std::string& pocket,
const std::string& source_name) const {
if (self_link.empty()) co_return;
std::map<std::string,std::string> params;
params["ws.op"] = "getBuildRecords";
if(!build_state.empty()) params["build_state"] = build_state;
if(!pocket.empty()) params["pocket"] = pocket;
if(!source_name.empty()) params["source_name"] = source_name;
auto response = lp->api_get(self_link, params);
if(!response) co_return;
auto data = nlohmann::json::parse(response.value());
while (true) {
if (data.contains("entries") && data["entries"].is_array()) {
for (auto& e : data["entries"]) {
co_yield e;
}
}
if(!data.contains("next_collection_link") || data["next_collection_link"].is_null() || data["next_collection_link"] == "") break;
response = lp->api_get(data["next_collection_link"].get<std::string>());
if(!response) break;
data = nlohmann::json::parse(response.value());
}
}
std::optional<nlohmann::json> distro_arch_series::getChrootHash(const std::string& image_type,
const std::string& pocket) const {
if (self_link.empty()) return std::nullopt;
std::map<std::string,std::string> params;
params["ws.op"] = "getChrootHash";
if(!image_type.empty()) params["image_type"] = image_type;
if(!pocket.empty()) params["pocket"] = pocket;
auto resp = lp->api_get(self_link, params);
if(!resp)return std::nullopt;
return nlohmann::json::parse(resp.value());
}
std::optional<std::string> distro_arch_series::getChrootURL(const std::optional<std::string>& image_type,
const std::optional<std::string>& pocket) const {
if (self_link.empty()) return std::nullopt;
std::map<std::string,std::string> params;
params["ws.op"] = "getChrootURL";
if(image_type.has_value()) params["image_type"] = image_type.value();
if(pocket.has_value()) params["pocket"] = pocket.value();
auto resp = lp->api_get(self_link, params);
if(!resp)return std::nullopt;
auto j = nlohmann::json::parse(resp.value());
if(j.is_string()) return j.get<std::string>();
return std::nullopt;
}
std::optional<nlohmann::json> distro_arch_series::getSourceFilter() const {
if (self_link.empty()) return std::nullopt;
std::map<std::string,std::string> params;
params["ws.op"] = "getSourceFilter";
auto resp=lp->api_get(self_link,params);
if(!resp)return std::nullopt;
return nlohmann::json::parse(resp.value());
}
bool distro_arch_series::removeChroot(const std::optional<std::string>& image_type,
const std::optional<std::string>& pocket) const {
if(self_link.empty())return false;
std::map<std::string,std::string>params;
params["ws.op"]="removeChroot";
if(image_type.has_value()) params["image_type"]=image_type.value();
if(pocket.has_value()) params["pocket"]=pocket.value();
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool distro_arch_series::removeSourceFilter() const {
if(self_link.empty())return false;
std::map<std::string,std::string>params;
params["ws.op"]="removeSourceFilter";
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool distro_arch_series::setChroot(const std::string& data_link,
const std::optional<std::string>& image_type,
const std::optional<std::string>& pocket,
const std::string& sha1sum) const {
if(self_link.empty())return false;
std::map<std::string,std::string>params;
params["ws.op"]="setChroot";
params["data"]=data_link;
if(image_type.has_value()) params["image_type"]=image_type.value();
if(pocket.has_value()) params["pocket"]=pocket.value();
params["sha1sum"]=sha1sum;
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool distro_arch_series::setChrootFromBuild(const std::string& filename,
const std::string& livefsbuild_link,
const std::optional<std::string>& image_type,
const std::optional<std::string>& pocket) const {
if(self_link.empty())return false;
std::map<std::string,std::string>params;
params["ws.op"]="setChrootFromBuild";
params["filename"]=filename;
params["livefsbuild"]=livefsbuild_link;
if(image_type.has_value()) params["image_type"]=image_type.value();
if(pocket.has_value()) params["pocket"]=pocket.value();
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool distro_arch_series::setSourceFilter(const std::string& packageset,
const std::string& sense) const {
if(self_link.empty())return false;
std::map<std::string,std::string>params;
params["ws.op"]="setSourceFilter";
params["packageset"]=packageset;
params["sense"]=sense; // must be "Include" or "Exclude"
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}

@ -0,0 +1,74 @@
#ifndef DISTRO_ARCH_SERIES_H
#define DISTRO_ARCH_SERIES_H
#include <string>
#include <optional>
#include <nlohmann/json.hpp>
#include <generator>
class launchpad;
class distro_arch_series {
public:
distro_arch_series();
~distro_arch_series();
static std::optional<distro_arch_series> parse(const std::string& json_data);
void parse_json(const std::string& json_data);
void set_lp(launchpad* lp_ptr);
// Writable fields
std::string architecture_tag;
std::string display_name;
std::string distroseries_link;
bool enabled;
bool is_nominated_arch_indep;
std::string main_archive_link;
bool official;
std::string owner_link;
int package_count;
std::string title;
// Read-only fields
std::string chroot_url;
std::string http_etag;
std::string processor_link;
std::string resource_type_link;
std::string self_link;
bool supports_virtualized;
std::string web_link;
std::optional<distro_arch_series> get() const;
bool patch(const nlohmann::json& data) const;
bool put(const nlohmann::json& data) const;
// Custom GET methods
std::generator<nlohmann::json> getBuildRecords(const std::string& build_state="",
const std::string& pocket="",
const std::string& source_name="") const;
std::optional<nlohmann::json> getChrootHash(const std::string& image_type="Chroot tarball",
const std::string& pocket="Release") const;
std::optional<std::string> getChrootURL(const std::optional<std::string>& image_type=std::nullopt,
const std::optional<std::string>& pocket=std::nullopt) const;
std::optional<nlohmann::json> getSourceFilter() const;
// Custom POST methods
bool removeChroot(const std::optional<std::string>& image_type=std::nullopt,
const std::optional<std::string>& pocket=std::nullopt) const;
bool removeSourceFilter() const;
bool setChroot(const std::string& data_link,
const std::optional<std::string>& image_type=std::nullopt,
const std::optional<std::string>& pocket=std::nullopt,
const std::string& sha1sum="") const;
bool setChrootFromBuild(const std::string& filename,
const std::string& livefsbuild_link,
const std::optional<std::string>& image_type=std::nullopt,
const std::optional<std::string>& pocket=std::nullopt) const;
bool setSourceFilter(const std::string& packageset,
const std::string& sense) const;
private:
launchpad* lp;
};
#endif // DISTRO_ARCH_SERIES_H

@ -0,0 +1,74 @@
#include "distro_arch_series_filter.h"
#include "launchpad.h"
#include <iostream>
#include <nlohmann/json.hpp>
distro_arch_series_filter::distro_arch_series_filter()
: lp(nullptr) {}
distro_arch_series_filter::~distro_arch_series_filter() {}
std::optional<distro_arch_series_filter> distro_arch_series_filter::parse(const std::string& json_data) {
distro_arch_series_filter f;
f.parse_json(json_data);
return f;
}
void distro_arch_series_filter::parse_json(const std::string& json_data) {
if (json_data.empty()) {
std::cerr << "Error: Empty JSON data for distro_arch_series_filter::parse_json." << std::endl;
return;
}
try {
auto data = nlohmann::json::parse(json_data);
auto get_str = [&](const std::string& key, std::string& field) {
if (data.contains(key) && data[key].is_string())
field = data[key].get<std::string>();
};
get_str("creator_link", creator_link);
get_str("date_created", date_created);
get_str("date_last_modified", date_last_modified);
get_str("distroarchseries_link", distroarchseries_link);
get_str("http_etag", http_etag);
get_str("packageset_link", packageset_link);
get_str("resource_type_link", resource_type_link);
get_str("self_link", self_link);
get_str("sense", sense);
get_str("web_link", web_link);
} catch (const nlohmann::json::parse_error& e) {
std::cerr << "JSON parse error in distro_arch_series_filter::parse_json: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "Unexpected error during JSON parsing: " << e.what() << std::endl;
}
}
void distro_arch_series_filter::set_lp(launchpad* lp_ptr) {
lp = lp_ptr;
}
std::optional<distro_arch_series_filter> distro_arch_series_filter::get() const {
if (self_link.empty()) return std::nullopt;
auto resp = lp->api_get(self_link);
if (!resp) return std::nullopt;
auto f = parse(resp.value());
if (f) f->set_lp(lp);
return f;
}
bool distro_arch_series_filter::patch(const nlohmann::json& data) const {
if (self_link.empty()) return false;
auto resp = lp->api_patch(self_link, data);
return resp.has_value();
}
bool distro_arch_series_filter::put(const nlohmann::json& data) const {
if (self_link.empty()) return false;
// Emulate PUT with ws.op=replace
std::string endpoint = self_link + "?ws.op=replace";
auto resp = lp->api_patch(endpoint, data);
return resp.has_value();
}

@ -0,0 +1,39 @@
#ifndef DISTRO_ARCH_SERIES_FILTER_H
#define DISTRO_ARCH_SERIES_FILTER_H
#include <string>
#include <optional>
#include <nlohmann/json.hpp>
class launchpad;
class distro_arch_series_filter {
public:
distro_arch_series_filter();
~distro_arch_series_filter();
static std::optional<distro_arch_series_filter> parse(const std::string& json_data);
void parse_json(const std::string& json_data);
void set_lp(launchpad* lp_ptr);
// Read-only fields
std::string creator_link;
std::string date_created;
std::string date_last_modified;
std::string distroarchseries_link;
std::string http_etag;
std::string packageset_link;
std::string resource_type_link;
std::string self_link;
std::string sense; // "Include" or "Exclude"
std::string web_link;
std::optional<distro_arch_series_filter> get() const;
bool patch(const nlohmann::json& data) const;
bool put(const nlohmann::json& data) const;
private:
launchpad* lp;
};
#endif

@ -0,0 +1,521 @@
#include "distro_series.h"
#include "launchpad.h"
#include "utils.h"
#include <iostream>
#include <nlohmann/json.hpp>
distro_series::distro_series()
: active(false),
advertise_by_hash(false),
backports_not_automatic(false),
include_long_descriptions(false),
language_pack_full_export_requested(false),
proposed_not_automatic(false),
publish_by_hash(false),
publish_i18n_index(false),
strict_supported_component_dependencies(false),
supported(false),
lp(nullptr) {}
distro_series::~distro_series() {}
std::optional<distro_series> distro_series::parse(const std::string& json_data) {
distro_series ds;
ds.parse_json(json_data);
return ds;
}
void distro_series::parse_json(const std::string& json_data) {
if (json_data.empty()) {
std::cerr << "Error: Empty JSON data for distro_series::parse_json." << std::endl;
return;
}
try {
auto data = nlohmann::json::parse(json_data);
auto get_bool = [&](const std::string& key, bool& field) {
if (data.contains(key) && data[key].is_boolean()) field = data[key].get<bool>();
};
auto get_str = [&](const std::string& key, std::string& field) {
if (data.contains(key) && data[key].is_string()) field = data[key].get<std::string>();
};
auto get_str_vec = [&](const std::string& key, std::vector<std::string>& field) {
if (data.contains(key) && data[key].is_array()) {
field.clear();
for (auto& e : data[key]) {
if (e.is_string()) field.push_back(e.get<std::string>());
}
}
};
get_bool("active", active);
get_bool("advertise_by_hash", advertise_by_hash);
get_str("all_milestones_collection_link", all_milestones_collection_link);
get_str("all_specifications_collection_link", all_specifications_collection_link);
get_str("architectures_collection_link", architectures_collection_link);
get_bool("backports_not_automatic", backports_not_automatic);
get_str("bug_reported_acknowledgement", bug_reported_acknowledgement);
get_str("bug_reporting_guidelines", bug_reporting_guidelines);
get_str("changeslist", changeslist);
get_str_vec("component_names", component_names);
get_str("date_created", date_created);
get_str("datereleased", datereleased);
get_str("description", description);
get_str("displayname", displayname);
get_str("distribution_link", distribution_link);
get_str("driver_link", driver_link);
get_str("drivers_collection_link", drivers_collection_link);
get_str("fullseriesname", fullseriesname);
get_str("http_etag", http_etag);
get_bool("include_long_descriptions", include_long_descriptions);
get_str_vec("index_compressors", index_compressors);
get_bool("language_pack_full_export_requested", language_pack_full_export_requested);
get_str("main_archive_link", main_archive_link);
get_str("name", name);
get_str("nominatedarchindep_link", nominatedarchindep_link);
get_str_vec("official_bug_tags", official_bug_tags);
get_str("owner_link", owner_link);
get_str("previous_series_link", previous_series_link);
get_bool("proposed_not_automatic", proposed_not_automatic);
get_bool("publish_by_hash", publish_by_hash);
get_bool("publish_i18n_index", publish_i18n_index);
get_str("registrant_link", registrant_link);
get_str("resource_type_link", resource_type_link);
get_str("self_link", self_link);
get_str("status", status);
get_bool("strict_supported_component_dependencies", strict_supported_component_dependencies);
get_str_vec("suite_names", suite_names);
get_str("summary", summary);
get_bool("supported", supported);
get_str("title", title);
get_str("translations_usage", translations_usage);
get_str("valid_specifications_collection_link", valid_specifications_collection_link);
get_str("version", version);
get_str("web_link", web_link);
// These additional fields might not be present in the JSON, but we define them if they exist
// They are listed as additional read-only fields in the header, but were not found in the JSON keys above
// We'll just attempt to parse them similarly if they exist
get_str("active_milestones_collection_link", active_milestones_collection_link);
get_str("archive_mirrors_collection_link", archive_mirrors_collection_link);
get_str("archives_collection_link", archives_collection_link);
get_str("series_collection_link", series_collection_link);
get_str("vulnerabilities_collection_link", vulnerabilities_collection_link);
get_str("webhooks_collection_link", webhooks_collection_link);
} catch (const nlohmann::json::parse_error& e) {
std::cerr << "JSON parse error in distro_series::parse_json: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "Unexpected error during JSON parsing: " << e.what() << std::endl;
}
}
void distro_series::set_lp(launchpad* lp_ptr) {
lp = lp_ptr;
}
std::optional<distro_series> distro_series::get() const {
if (self_link.empty()) return std::nullopt;
auto resp = lp->api_get(self_link);
if(!resp)return std::nullopt;
return parse(resp.value());
}
bool distro_series::patch(const nlohmann::json& data) const {
if (self_link.empty()) return false;
auto resp = lp->api_patch(self_link, data);
return resp.has_value();
}
bool distro_series::put(const nlohmann::json& data) const {
if (self_link.empty()) return false;
std::string endpoint = self_link + "?ws.op=replace";
auto resp = lp->api_patch(endpoint, data);
return resp.has_value();
}
std::generator<nlohmann::json> distro_series::getBuildRecords(const std::string& build_state, const std::string& pocket, const std::string& source_name) const {
if (!lp) co_return;
if (self_link.empty()) co_return;
std::map<std::string,std::string> params;
params["ws.op"] = "getBuildRecords";
if(!build_state.empty()) params["build_state"]=build_state;
if(!pocket.empty()) params["pocket"]=pocket;
if(!source_name.empty()) params["source_name"]=source_name;
auto response=lp->api_get(self_link, params);
if(!response) co_return;
auto data=nlohmann::json::parse(response.value());
while(true) {
if(data.contains("entries") && data["entries"].is_array()) {
for(auto&e:data["entries"]){
co_yield e;
}
}
if(!data.contains("next_collection_link") || data["next_collection_link"].is_null() || data["next_collection_link"]=="") break;
response=lp->api_get(data["next_collection_link"].get<std::string>());
if(!response)break;
data=nlohmann::json::parse(response.value());
}
}
std::generator<distro_series> distro_series::getDerivedSeries() const {
if(!lp || self_link.empty()) co_return;
std::map<std::string,std::string> params;
params["ws.op"]="getDerivedSeries";
auto response=lp->api_get(self_link,params);
if(!response) co_return;
auto data=nlohmann::json::parse(response.value());
while(true){
if(data.contains("entries") && data["entries"].is_array()){
for(auto&e:data["entries"]){
auto ds_opt = parse(e.dump());
if(ds_opt){
ds_opt->set_lp(lp);
co_yield ds_opt.value();
}
}
}
if(!data.contains("next_collection_link")||data["next_collection_link"].is_null()||data["next_collection_link"]=="")break;
response=lp->api_get(data["next_collection_link"].get<std::string>());
if(!response)break;
data=nlohmann::json::parse(response.value());
}
}
std::generator<nlohmann::json> distro_series::getDifferenceComments(const std::string& since, const std::string& source_package_name) const {
if(!lp||self_link.empty()) co_return;
std::map<std::string,std::string> params;
params["ws.op"]="getDifferenceComments";
if(!since.empty()) params["since"]=since;
if(!source_package_name.empty()) params["source_package_name"]=source_package_name;
auto response=lp->api_get(self_link,params);
if(!response) co_return;
auto data=nlohmann::json::parse(response.value());
while(true){
if(data.contains("entries") && data["entries"].is_array()){
for(auto&e:data["entries"]){
co_yield e;
}
}
if(!data.contains("next_collection_link")||data["next_collection_link"].is_null()||data["next_collection_link"]=="")break;
response=lp->api_get(data["next_collection_link"].get<std::string>());
if(!response)break;
data=nlohmann::json::parse(response.value());
}
}
std::generator<nlohmann::json> distro_series::getDifferencesTo(const std::optional<std::string>& parent_series,
const std::optional<std::string>& difference_type,
const std::optional<std::string>& source_package_name_filter,
const std::optional<std::string>& status,
bool child_version_higher) const {
if(!lp||self_link.empty()) co_return;
std::map<std::string,std::string> params;
params["ws.op"]="getDifferencesTo";
if(parent_series.has_value()) params["parent_series"]=parent_series.value();
if(difference_type.has_value()) params["difference_type"]=difference_type.value();
if(source_package_name_filter.has_value()) params["source_package_name_filter"]=source_package_name_filter.value();
if(status.has_value()) params["status"]=status.value();
if(child_version_higher) params["child_version_higher"]="true";
auto response=lp->api_get(self_link,params);
if(!response) co_return;
auto data=nlohmann::json::parse(response.value());
// Usually differences might have entries too
while(true){
if(data.contains("entries")&&data["entries"].is_array()){
for(auto&e:data["entries"]){
co_yield e;
}
}
if(!data.contains("next_collection_link")||data["next_collection_link"].is_null()||data["next_collection_link"]=="")break;
response=lp->api_get(data["next_collection_link"].get<std::string>());
if(!response)break;
data=nlohmann::json::parse(response.value());
}
}
std::optional<nlohmann::json> distro_series::getDistroArchSeries(const std::string& archtag) const {
if(!lp||self_link.empty())return std::nullopt;
std::map<std::string,std::string> params;
params["ws.op"]="getDistroArchSeries";
params["archtag"]=archtag;
auto response=lp->api_get(self_link,params);
if(!response)return std::nullopt;
return nlohmann::json::parse(response.value());
}
std::generator<nlohmann::json> distro_series::getPackageUploads(const std::optional<std::string>& archive,
const std::optional<std::string>& created_since_date,
const std::optional<std::string>& custom_type,
bool exact_match,
const std::optional<std::string>& name,
const std::optional<std::string>& pocket,
const std::optional<std::string>& status,
const std::optional<std::string>& version) const {
if(!lp||self_link.empty()) co_return;
std::map<std::string,std::string> params;
params["ws.op"]="getPackageUploads";
if(archive.has_value()) params["archive"]=archive.value();
if(created_since_date.has_value()) params["created_since_date"]=created_since_date.value();
if(custom_type.has_value()) params["custom_type"]=custom_type.value();
if(exact_match) params["exact_match"]="true";
if(name.has_value()) params["name"]=name.value();
if(pocket.has_value()) params["pocket"]=pocket.value();
if(status.has_value()) params["status"]=status.value();
if(version.has_value()) params["version"]=version.value();
auto response=lp->api_get(self_link,params);
if(!response) co_return;
auto data=nlohmann::json::parse(response.value());
while(true){
if(data.contains("entries") && data["entries"].is_array()){
for(auto&e:data["entries"]) {
co_yield e;
}
}
if(!data.contains("next_collection_link")||data["next_collection_link"].is_null()||data["next_collection_link"]=="") break;
response=lp->api_get(data["next_collection_link"].get<std::string>());
if(!response)break;
data=nlohmann::json::parse(response.value());
}
}
std::generator<distro_series> distro_series::getParentSeries() const {
if(!lp||self_link.empty()) co_return;
std::map<std::string,std::string> params;
params["ws.op"]="getParentSeries";
auto response=lp->api_get(self_link,params);
if(!response) co_return;
auto data=nlohmann::json::parse(response.value());
while(true) {
if(data.contains("entries")&&data["entries"].is_array()){
for(auto&e:data["entries"]){
auto ds_opt=parse(e.dump());
if(ds_opt){
ds_opt->set_lp(lp);
co_yield ds_opt.value();
}
}
}
if(!data.contains("next_collection_link")||data["next_collection_link"].is_null()||data["next_collection_link"]=="")break;
response=lp->api_get(data["next_collection_link"].get<std::string>());
if(!response)break;
data=nlohmann::json::parse(response.value());
}
}
std::optional<nlohmann::json> distro_series::getSourcePackage(const std::string& name_) const {
if(!lp||self_link.empty())return std::nullopt;
std::map<std::string,std::string> params;
params["ws.op"]="getSourcePackage";
params["name"]=name_;
auto resp=lp->api_get(self_link,params);
if(!resp)return std::nullopt;
return nlohmann::json::parse(resp.value());
}
std::optional<nlohmann::json> distro_series::getSpecification(const std::string& name_) const {
if(!lp||self_link.empty())return std::nullopt;
std::map<std::string,std::string> params;
params["ws.op"]="getSpecification";
params["name"]=name_;
auto resp=lp->api_get(self_link,params);
if(!resp)return std::nullopt;
return nlohmann::json::parse(resp.value());
}
std::optional<nlohmann::json> distro_series::getSubscription(const std::string& person) const {
if(!lp||self_link.empty())return std::nullopt;
std::map<std::string,std::string> params;
params["ws.op"]="getSubscription";
params["person"]=person;
auto resp=lp->api_get(self_link,params);
if(!resp)return std::nullopt;
return nlohmann::json::parse(resp.value());
}
std::generator<nlohmann::json> distro_series::getSubscriptions() const {
if(!lp||self_link.empty()) co_return;
std::map<std::string,std::string> params;
params["ws.op"]="getSubscriptions";
auto response=lp->api_get(self_link,params);
if(!response) co_return;
auto data=nlohmann::json::parse(response.value());
while(true) {
if(data.contains("entries")&&data["entries"].is_array()){
for(auto&e:data["entries"]){
co_yield e;
}
}
if(!data.contains("next_collection_link")||data["next_collection_link"].is_null()||data["next_collection_link"]=="")break;
response=lp->api_get(data["next_collection_link"].get<std::string>());
if(!response)break;
data=nlohmann::json::parse(response.value());
}
}
std::generator<nlohmann::json> distro_series::getTranslationImportQueueEntries(const std::optional<std::string>& import_status,
const std::optional<std::string>& file_extension) const {
if(!lp||self_link.empty()) co_return;
std::map<std::string,std::string>params;
params["ws.op"]="getTranslationImportQueueEntries";
if(import_status.has_value()) params["import_status"]=import_status.value();
if(file_extension.has_value()) params["file_extension"]=file_extension.value();
auto response=lp->api_get(self_link,params);
if(!response)co_return;
auto data=nlohmann::json::parse(response.value());
while(true){
if(data.contains("entries")&&data["entries"].is_array()){
for(auto&e:data["entries"]){
co_yield e;
}
}
if(!data.contains("next_collection_link")||data["next_collection_link"].is_null()||data["next_collection_link"]=="")break;
response=lp->api_get(data["next_collection_link"].get<std::string>());
if(!response)break;
data=nlohmann::json::parse(response.value());
}
}
std::optional<nlohmann::json> distro_series::getTranslationTemplateStatistics() const {
if(!lp||self_link.empty())return std::nullopt;
std::map<std::string,std::string>params;
params["ws.op"]="getTranslationTemplateStatistics";
auto resp=lp->api_get(self_link,params);
if(!resp)return std::nullopt;
return nlohmann::json::parse(resp.value());
}
std::generator<nlohmann::json> distro_series::getTranslationTemplates() const {
if(!lp||self_link.empty()) co_return;
std::map<std::string,std::string>params;
params["ws.op"]="getTranslationTemplates";
auto response=lp->api_get(self_link,params);
if(!response)co_return;
auto data=nlohmann::json::parse(response.value());
while(true){
if(data.contains("entries")&&data["entries"].is_array()){
for(auto&e:data["entries"]){
co_yield e;
}
}
if(!data.contains("next_collection_link")||data["next_collection_link"].is_null()||data["next_collection_link"]=="") break;
response=lp->api_get(data["next_collection_link"].get<std::string>());
if(!response) break;
data=nlohmann::json::parse(response.value());
}
}
std::generator<nlohmann::json> distro_series::searchTasks(const std::map<std::string,std::string>& params_in) const {
if(!lp||self_link.empty()) co_return;
std::map<std::string,std::string> params = params_in;
params["ws.op"]="searchTasks";
auto response=lp->api_get(self_link,params);
if(!response) co_return;
auto data=nlohmann::json::parse(response.value());
while(true){
if(data.contains("entries")&&data["entries"].is_array()){
for(auto&e:data["entries"]){
co_yield e;
}
}
if(!data.contains("next_collection_link")||data["next_collection_link"].is_null()||data["next_collection_link"]=="")break;
response=lp->api_get(data["next_collection_link"].get<std::string>());
if(!response)break;
data=nlohmann::json::parse(response.value());
}
}
std::optional<nlohmann::json> distro_series::userHasBugSubscriptions() const {
if(!lp||self_link.empty())return std::nullopt;
std::map<std::string,std::string>params;
params["ws.op"]="userHasBugSubscriptions";
auto resp=lp->api_get(self_link,params);
if(!resp)return std::nullopt;
return nlohmann::json::parse(resp.value());
}
bool distro_series::addBugSubscription(const std::optional<std::string>& subscriber) const {
if(!lp||self_link.empty())return false;
std::map<std::string,std::string>params;
params["ws.op"]="addBugSubscription";
if(subscriber.has_value()) params["subscriber"]=subscriber.value();
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool distro_series::addBugSubscriptionFilter(const std::optional<std::string>& subscriber) const {
if(!lp||self_link.empty())return false;
std::map<std::string,std::string>params;
params["ws.op"]="addBugSubscriptionFilter";
if(subscriber.has_value())params["subscriber"]=subscriber.value();
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
bool distro_series::initDerivedDistroSeries(const std::optional<std::string>& archindep_archtag,
const std::vector<std::string>& architectures,
const std::vector<std::string>& overlay_components,
const std::vector<std::string>& overlay_pockets,
const std::vector<std::string>& overlays,
const std::vector<std::string>& packagesets,
const std::vector<std::string>& parents,
bool rebuild) const {
if(!lp||self_link.empty())return false;
std::map<std::string,std::string>params;
params["ws.op"]="initDerivedDistroSeries";
if(archindep_archtag.has_value()) params["archindep_archtag"]=archindep_archtag.value();
auto join_list = [](const std::vector<std::string>& vec){
std::string joined;
for (auto& v:vec) {
joined+=v+",";
}
if(!joined.empty()) joined.pop_back();
return joined;
};
if(!architectures.empty()) params["architectures"]=join_list(architectures);
if(!overlay_components.empty()) params["overlay_components"]=join_list(overlay_components);
if(!overlay_pockets.empty()) params["overlay_pockets"]=join_list(overlay_pockets);
if(!overlays.empty()) params["overlays"]=join_list(overlays);
if(!packagesets.empty()) params["packagesets"]=join_list(packagesets);
if(!parents.empty()) params["parents"]=join_list(parents);
if(rebuild) params["rebuild"]="true";
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}
std::optional<nlohmann::json> distro_series::newMilestone(const std::string& name,
const std::optional<std::string>& code_name,
const std::optional<std::string>& date_targeted,
const std::optional<std::string>& summary) const {
if(!lp||self_link.empty())return std::nullopt;
std::map<std::string,std::string>params;
params["ws.op"]="newMilestone";
params["name"]=name;
if(code_name.has_value()) params["code_name"]=code_name.value();
if(date_targeted.has_value()) params["date_targeted"]=date_targeted.value();
if(summary.has_value()) params["summary"]=summary.value();
auto resp=lp->api_post(self_link,params);
if(!resp)return std::nullopt;
return nlohmann::json::parse(resp.value());
}
bool distro_series::removeBugSubscription(const std::optional<std::string>& subscriber) const {
if(!lp||self_link.empty())return false;
std::map<std::string,std::string>params;
params["ws.op"]="removeBugSubscription";
if(subscriber.has_value()) params["subscriber"]=subscriber.value();
auto resp=lp->api_post(self_link,params);
return resp.has_value();
}

@ -0,0 +1,127 @@
#ifndef DISTRO_SERIES_H
#define DISTRO_SERIES_H
#include <string>
#include <optional>
#include <nlohmann/json.hpp>
#include <generator>
#include <vector>
class launchpad;
class distro_series {
public:
distro_series();
~distro_series();
static std::optional<distro_series> parse(const std::string& json_data);
void parse_json(const std::string& json_data);
void set_lp(launchpad* lp_ptr);
bool active;
bool advertise_by_hash;
std::string all_milestones_collection_link;
std::string all_specifications_collection_link;
std::string architectures_collection_link;
bool backports_not_automatic;
std::string bug_reported_acknowledgement;
std::string bug_reporting_guidelines;
std::string changeslist;
std::vector<std::string> component_names;
std::string date_created;
std::string datereleased;
std::string description;
std::string displayname;
std::string distribution_link;
std::string driver_link;
std::string drivers_collection_link;
std::string fullseriesname;
std::string http_etag;
bool include_long_descriptions;
std::vector<std::string> index_compressors;
bool language_pack_full_export_requested;
std::string main_archive_link;
std::string name;
std::string nominatedarchindep_link;
std::vector<std::string> official_bug_tags;
std::string owner_link;
std::string previous_series_link;
bool proposed_not_automatic;
bool publish_by_hash;
bool publish_i18n_index;
std::string registrant_link;
std::string resource_type_link;
std::string self_link;
std::string status;
bool strict_supported_component_dependencies;
std::vector<std::string> suite_names;
std::string summary;
bool supported;
std::string title;
std::string translations_usage;
std::string valid_specifications_collection_link;
std::string version;
std::string web_link;
std::string active_milestones_collection_link;
std::string archive_mirrors_collection_link;
std::string archives_collection_link;
std::string series_collection_link;
std::string vulnerabilities_collection_link;
std::string webhooks_collection_link;
std::optional<distro_series> get() const;
bool patch(const nlohmann::json& data) const;
bool put(const nlohmann::json& data) const;
std::generator<nlohmann::json> getBuildRecords(const std::string& build_state="", const std::string& pocket="", const std::string& source_name="") const;
std::generator<distro_series> getDerivedSeries() const;
std::generator<nlohmann::json> getDifferenceComments(const std::string& since="", const std::string& source_package_name="") const;
std::generator<nlohmann::json> getDifferencesTo(const std::optional<std::string>& parent_series,
const std::optional<std::string>& difference_type,
const std::optional<std::string>& source_package_name_filter,
const std::optional<std::string>& status,
bool child_version_higher=false) const;
std::optional<nlohmann::json> getDistroArchSeries(const std::string& archtag) const;
std::generator<nlohmann::json> getPackageUploads(const std::optional<std::string>& archive=std::nullopt,
const std::optional<std::string>& created_since_date=std::nullopt,
const std::optional<std::string>& custom_type=std::nullopt,
bool exact_match=false,
const std::optional<std::string>& name=std::nullopt,
const std::optional<std::string>& pocket=std::nullopt,
const std::optional<std::string>& status=std::nullopt,
const std::optional<std::string>& version=std::nullopt) const;
std::generator<distro_series> getParentSeries() const;
std::optional<nlohmann::json> getSourcePackage(const std::string& name_) const;
std::optional<nlohmann::json> getSpecification(const std::string& name_) const;
std::optional<nlohmann::json> getSubscription(const std::string& person) const;
std::generator<nlohmann::json> getSubscriptions() const;
std::generator<nlohmann::json> getTranslationImportQueueEntries(const std::optional<std::string>& import_status=std::nullopt,
const std::optional<std::string>& file_extension=std::nullopt) const;
std::optional<nlohmann::json> getTranslationTemplateStatistics() const;
std::generator<nlohmann::json> getTranslationTemplates() const;
std::generator<nlohmann::json> searchTasks(const std::map<std::string,std::string>& params) const;
std::optional<nlohmann::json> userHasBugSubscriptions() const;
bool addBugSubscription(const std::optional<std::string>& subscriber=std::nullopt) const;
bool addBugSubscriptionFilter(const std::optional<std::string>& subscriber=std::nullopt) const;
bool initDerivedDistroSeries(const std::optional<std::string>& archindep_archtag,
const std::vector<std::string>& architectures,
const std::vector<std::string>& overlay_components,
const std::vector<std::string>& overlay_pockets,
const std::vector<std::string>& overlays,
const std::vector<std::string>& packagesets,
const std::vector<std::string>& parents,
bool rebuild=false) const;
std::optional<nlohmann::json> newMilestone(const std::string& name,
const std::optional<std::string>& code_name,
const std::optional<std::string>& date_targeted,
const std::optional<std::string>& summary) const;
bool removeBugSubscription(const std::optional<std::string>& subscriber=std::nullopt) const;
private:
launchpad* lp;
};
#endif
Loading…
Cancel
Save