Browse Source

Rewrite moduleWhitelist data structure and merge algorithm.

tags/v2.0.0
Andrew Belt 3 years ago
parent
commit
23334f32a8
3 changed files with 72 additions and 55 deletions
  1. +10
    -2
      include/settings.hpp
  2. +34
    -43
      src/library.cpp
  3. +28
    -10
      src/settings.cpp

+ 10
- 2
include/settings.hpp View File

@@ -96,8 +96,16 @@ extern std::map<std::string, std::map<std::string, ModuleInfo>> moduleInfos;
*/ */
ModuleInfo* getModuleInfo(const std::string& pluginSlug, const std::string& moduleSlug); ModuleInfo* getModuleInfo(const std::string& pluginSlug, const std::string& moduleSlug);


/** pluginSlug -> {moduleSlug} */
extern std::map<std::string, std::set<std::string>> moduleWhitelist;
/** The VCV JSON API returns the data structure
{pluginSlug: [moduleSlugs] or true}
where "true" represents that the user is subscribed to the plugin (all modules and future modules).
C++ isn't weakly typed, so we need the PluginWhitelist data structure to store this information.
*/
struct PluginWhitelist {
bool subscribed = false;
std::set<std::string> moduleSlugs;
};
extern std::map<std::string, PluginWhitelist> moduleWhitelist;


bool isModuleWhitelisted(const std::string& pluginSlug, const std::string& moduleSlug); bool isModuleWhitelisted(const std::string& pluginSlug, const std::string& moduleSlug);




+ 34
- 43
src/library.cpp View File

@@ -187,40 +187,33 @@ void checkUpdates() {
} }
DEFER({json_decref(manifestsResJ);}); DEFER({json_decref(manifestsResJ);});


// Get user's plugins list
std::string pluginsUrl = API_URL + "/plugins";
json_t* pluginsResJ = network::requestJson(network::METHOD_GET, pluginsUrl, NULL, getTokenCookies());
if (!pluginsResJ) {
WARN("Request for user's plugins failed");
updateStatus = "Could not query user's plugins";
return;
}
DEFER({json_decref(pluginsResJ);});

json_t* errorJ = json_object_get(pluginsResJ, "error");
if (errorJ) {
WARN("Request for user's plugins returned an error: %s", json_string_value(errorJ));
updateStatus = "Could not query plugins";
// Get user's modules
std::string modulesUrl = API_URL + "/modules";
json_t* modulesResJ = network::requestJson(network::METHOD_GET, modulesUrl, NULL, getTokenCookies());
if (!modulesResJ) {
WARN("Request for user's modules failed");
updateStatus = "Could not query user's modules";
return; return;
} }
DEFER({json_decref(modulesResJ);});


json_t* manifestsJ = json_object_get(manifestsResJ, "manifests"); json_t* manifestsJ = json_object_get(manifestsResJ, "manifests");
json_t* pluginsJ = json_object_get(pluginsResJ, "plugins");
json_t* pluginsJ = json_object_get(modulesResJ, "modules");


size_t pluginIndex;
json_t* pluginJ;
json_array_foreach(pluginsJ, pluginIndex, pluginJ) {
const char* modulesKey;
json_t* modulesJ;
json_object_foreach(pluginsJ, modulesKey, modulesJ) {
std::string pluginSlug = modulesKey;
// Get plugin manifest // Get plugin manifest
std::string slug = json_string_value(pluginJ);
json_t* manifestJ = json_object_get(manifestsJ, slug.c_str());
json_t* manifestJ = json_object_get(manifestsJ, pluginSlug.c_str());
if (!manifestJ) { if (!manifestJ) {
WARN("VCV account has plugin %s but no manifest was found", slug.c_str());
WARN("VCV account has plugin %s but no manifest was found", pluginSlug.c_str());
continue; continue;
} }


// Don't replace existing UpdateInfo, even if version is newer. // Don't replace existing UpdateInfo, even if version is newer.
// This keeps things sane and ensures that only one version of each plugin is downloaded to `plugins/` at a time. // This keeps things sane and ensures that only one version of each plugin is downloaded to `plugins/` at a time.
auto it = updateInfos.find(slug);
auto it = updateInfos.find(pluginSlug);
if (it != updateInfos.end()) { if (it != updateInfos.end()) {
continue; continue;
} }
@@ -234,7 +227,7 @@ void checkUpdates() {
// Get version // Get version
json_t* versionJ = json_object_get(manifestJ, "version"); json_t* versionJ = json_object_get(manifestJ, "version");
if (!versionJ) { if (!versionJ) {
// WARN("Plugin %s has no version in manifest", slug.c_str());
// WARN("Plugin %s has no version in manifest", pluginSlug.c_str());
continue; continue;
} }
update.version = json_string_value(versionJ); update.version = json_string_value(versionJ);
@@ -244,7 +237,7 @@ void checkUpdates() {
} }


// Check if update is needed // Check if update is needed
plugin::Plugin* p = plugin::getPlugin(slug);
plugin::Plugin* p = plugin::getPlugin(pluginSlug);
if (p && p->version == update.version) if (p && p->version == update.version)
continue; continue;


@@ -259,41 +252,39 @@ void checkUpdates() {
update.changelogUrl = json_string_value(changelogUrlJ); update.changelogUrl = json_string_value(changelogUrlJ);


// Add update to updates map // Add update to updates map
updateInfos[slug] = update;
updateInfos[pluginSlug] = update;
} }


// Get module whitelist
// Merge module whitelist
{ {
std::string whitelistUrl = API_URL + "/modules";
json_t* whitelistResJ = network::requestJson(network::METHOD_GET, whitelistUrl, NULL, getTokenCookies());
if (!whitelistResJ) {
WARN("Request for module whitelist failed");
updateStatus = "Could not query user's modules";
return;
}
DEFER({json_decref(whitelistResJ);});

// Clone plugin slugs from settings to temporary whitelist. // Clone plugin slugs from settings to temporary whitelist.
// This makes plugins entirely hidden if removed.
std::map<std::string, std::set<std::string>> moduleWhitelist;
// This makes existing plugins entirely hidden if removed from user's VCV account.
std::map<std::string, settings::PluginWhitelist> moduleWhitelist;
for (const auto& pluginPair : settings::moduleWhitelist) { for (const auto& pluginPair : settings::moduleWhitelist) {
// Create an empty set
std::string pluginSlug = pluginPair.first; std::string pluginSlug = pluginPair.first;
moduleWhitelist[pluginSlug];
moduleWhitelist[pluginSlug] = settings::PluginWhitelist();
} }


// Iterate plugins // Iterate plugins
json_t* pluginsJ = json_object_get(whitelistResJ, "plugins");
const char* pluginSlug;
const char* modulesKey;
json_t* modulesJ; json_t* modulesJ;
json_object_foreach(pluginsJ, pluginSlug, modulesJ) {
json_object_foreach(pluginsJ, modulesKey, modulesJ) {
std::string pluginSlug = modulesKey;
settings::PluginWhitelist& pw = moduleWhitelist[pluginSlug];

// If value is "true", plugin is subscribed
if (json_is_true(modulesJ)) {
pw.subscribed = true;
continue;
}

// Iterate modules in plugin // Iterate modules in plugin
size_t moduleIndex; size_t moduleIndex;
json_t* moduleSlugJ; json_t* moduleSlugJ;
json_array_foreach(modulesJ, moduleIndex, moduleSlugJ) { json_array_foreach(modulesJ, moduleIndex, moduleSlugJ) {
std::string moduleSlug = json_string_value(moduleSlugJ); std::string moduleSlug = json_string_value(moduleSlugJ);
// Insert module in whitelist // Insert module in whitelist
moduleWhitelist[pluginSlug].insert(moduleSlug);
pw.moduleSlugs.insert(moduleSlug);
} }
} }




+ 28
- 10
src/settings.cpp View File

@@ -62,7 +62,7 @@ bool discordUpdateActivity = true;
BrowserSort browserSort = BROWSER_SORT_UPDATED; BrowserSort browserSort = BROWSER_SORT_UPDATED;
float browserZoom = -1.f; float browserZoom = -1.f;
std::map<std::string, std::map<std::string, ModuleInfo>> moduleInfos; std::map<std::string, std::map<std::string, ModuleInfo>> moduleInfos;
std::map<std::string, std::set<std::string>> moduleWhitelist;
std::map<std::string, PluginWhitelist> moduleWhitelist;




ModuleInfo* getModuleInfo(const std::string& pluginSlug, const std::string& moduleSlug) { ModuleInfo* getModuleInfo(const std::string& pluginSlug, const std::string& moduleSlug) {
@@ -81,8 +81,13 @@ bool isModuleWhitelisted(const std::string& pluginSlug, const std::string& modul
// All modules in a plugin are visible if plugin set is empty. // All modules in a plugin are visible if plugin set is empty.
if (pluginIt == moduleWhitelist.end()) if (pluginIt == moduleWhitelist.end())
return true; return true;
auto moduleIt = pluginIt->second.find(moduleSlug);
if (moduleIt == pluginIt->second.end())
// All modules in a plugin are visible if plugin set is subscribed.
const PluginWhitelist& plugin = pluginIt->second;
if (plugin.subscribed)
return true;
// Check if plugin whitelist contains module
auto moduleIt = plugin.moduleSlugs.find(moduleSlug);
if (moduleIt == plugin.moduleSlugs.end())
return false; return false;
return true; return true;
} }
@@ -204,10 +209,20 @@ json_t* toJson() {
// moduleWhitelist // moduleWhitelist
json_t* moduleWhitelistJ = json_object(); json_t* moduleWhitelistJ = json_object();
for (const auto& pluginPair : moduleWhitelist) { for (const auto& pluginPair : moduleWhitelist) {
json_t* pluginJ = json_array();
for (const std::string& moduleSlug : pluginPair.second) {
json_array_append_new(pluginJ, json_stringn(moduleSlug.c_str(), moduleSlug.size()));
const PluginWhitelist& plugin = pluginPair.second;
json_t* pluginJ;

// If plugin is subscribed, set to true, otherwise an array of module slugs.
if (plugin.subscribed) {
pluginJ = json_true();
} }
else {
pluginJ = json_array();
for (const std::string& moduleSlug : plugin.moduleSlugs) {
json_array_append_new(pluginJ, json_stringn(moduleSlug.c_str(), moduleSlug.size()));
}
}

json_object_set_new(moduleWhitelistJ, pluginPair.first.c_str(), pluginJ); json_object_set_new(moduleWhitelistJ, pluginPair.first.c_str(), pluginJ);
} }
json_object_set_new(rootJ, "moduleWhitelist", moduleWhitelistJ); json_object_set_new(rootJ, "moduleWhitelist", moduleWhitelistJ);
@@ -398,15 +413,18 @@ void fromJson(json_t* rootJ) {
const char* pluginSlug; const char* pluginSlug;
json_t* pluginJ; json_t* pluginJ;
json_object_foreach(moduleWhitelistJ, pluginSlug, pluginJ) { json_object_foreach(moduleWhitelistJ, pluginSlug, pluginJ) {
// Create an empty modules set, even if there are no modules in the whitelist.
// An empty set means no modules will be displayed in the Browser.
auto& modules = moduleWhitelist[pluginSlug];
auto& plugin = moduleWhitelist[pluginSlug];

if (json_is_true(pluginJ)) {
plugin.subscribed = true;
continue;
}


size_t moduleIndex; size_t moduleIndex;
json_t* moduleJ; json_t* moduleJ;
json_array_foreach(pluginJ, moduleIndex, moduleJ) { json_array_foreach(pluginJ, moduleIndex, moduleJ) {
std::string moduleSlug = json_string_value(moduleJ); std::string moduleSlug = json_string_value(moduleJ);
modules.insert(moduleSlug);
plugin.moduleSlugs.insert(moduleSlug);
} }
} }
} }


Loading…
Cancel
Save