Fixed #59 (async download of package index and details)

This commit is contained in:
Matthias Koefferlein 2018-01-07 10:19:53 +01:00
parent bf5f932ff1
commit 8eb3f5e6ce
4 changed files with 165 additions and 78 deletions

View File

@ -480,15 +480,14 @@ SaltGrain::from_path (const std::string &path)
return g;
}
SaltGrain
SaltGrain::from_url (const std::string &url_in)
tl::InputStream *
SaltGrain::stream_from_url (std::string &url_in)
{
if (url_in.empty ()) {
throw tl::Exception (tl::to_string (QObject::tr ("No download link available")));
}
std::string url = url_in;
std::auto_ptr<tl::InputStream> stream;
// base relative URL's on the salt mine URL
if (url.find ("http:") != 0 && url.find ("https:") != 0 && url.find ("file:") != 0 && !url.empty() && url[0] != '/' && url[0] != '\\' && lay::SaltController::instance ()) {
@ -501,16 +500,23 @@ SaltGrain::from_url (const std::string &url_in)
}
sami_url.setPath (path_comp.join (QString::fromUtf8 ("/")));
url = tl::to_string (sami_url.toString ());
url_in = tl::to_string (sami_url.toString ());
}
std::string spec_url = SaltGrain::spec_url (url);
if (spec_url.find ("http:") == 0 || spec_url.find ("https:") == 0) {
stream.reset (tl::WebDAVObject::download_item (spec_url));
return tl::WebDAVObject::download_item (spec_url);
} else {
stream.reset (new tl::InputStream (spec_url));
return new tl::InputStream (spec_url);
}
}
SaltGrain
SaltGrain::from_url (const std::string &url_in)
{
std::string url = url_in;
std::auto_ptr<tl::InputStream> stream (stream_from_url (url));
SaltGrain g;
g.load (*stream);

View File

@ -449,6 +449,14 @@ public:
*/
static SaltGrain from_url (const std::string &url);
/**
* @brief Returns a stream prepared for downloading the grain
* The stream is a new'd object and needs to be deleted by the caller.
* "url" is the download URL on input and gets modified to match the
* actual URL if it is a relative one.
*/
static tl::InputStream *stream_from_url (std::string &url);
/**
* @brief Forms the spec file download URL from a given download URL
*/

View File

@ -113,7 +113,8 @@ private:
SaltManagerDialog::SaltManagerDialog (QWidget *parent, lay::Salt *salt, const std::string &salt_mine_url)
: QDialog (parent),
m_salt_mine_url (salt_mine_url),
dm_update_models (this, &SaltManagerDialog::update_models), m_current_tab (-1)
dm_update_models (this, &SaltManagerDialog::update_models), m_current_tab (-1),
mp_downloaded_target (0)
{
Ui::SaltManagerDialog::setupUi (this);
mp_properties_dialog = new lay::SaltGrainPropertiesDialog (this);
@ -126,18 +127,6 @@ SaltManagerDialog::SaltManagerDialog (QWidget *parent, lay::Salt *salt, const st
mp_salt = salt;
QApplication::setOverrideCursor (Qt::WaitCursor);
try {
if (! m_salt_mine_url.empty ()) {
tl::log << tl::to_string (tr ("Downloading package repository from %1").arg (tl::to_qstring (m_salt_mine_url)));
m_salt_mine.load (m_salt_mine_url);
}
m_salt_mine.consolidate ();
} catch (tl::Exception &ex) {
tl::error << ex.msg ();
}
QApplication::restoreOverrideCursor ();
SaltModel *model = new SaltModel (this, mp_salt);
model->set_empty_explanation (tr ("No packages are present on this system"));
salt_view->setModel (model);
@ -234,7 +223,7 @@ SaltManagerDialog::SaltManagerDialog (QWidget *parent, lay::Salt *salt, const st
connect (actionMarkForUpdate, SIGNAL (triggered ()), this, SLOT (mark_clicked ()));
connect (actionUnmarkForUpdate, SIGNAL (triggered ()), this, SLOT (mark_clicked ()));
update_models ();
refresh ();
}
void
@ -245,10 +234,13 @@ SaltManagerDialog::mode_changed ()
QList<int> sizes;
if (m_current_tab == 2) {
selected_changed ();
sizes = splitter->sizes ();
} else if (m_current_tab == 1) {
mine_update_selected_changed ();
sizes = splitter_update->sizes ();
} else if (m_current_tab == 0) {
mine_new_selected_changed ();
sizes = splitter_new->sizes ();
}
@ -268,6 +260,7 @@ SaltManagerDialog::mode_changed ()
}
m_current_tab = mode_tab->currentIndex ();
update_apply_state ();
}
void
@ -437,7 +430,7 @@ SaltManagerDialog::update_apply_state ()
{
SaltModel *model;
model = dynamic_cast <SaltModel *> (salt_mine_view_new->model ());
model = dynamic_cast <SaltModel *> (salt_mine_view_new->model ());
if (model) {
int marked = 0;
@ -665,30 +658,61 @@ SaltManagerDialog::salt_mine_about_to_change ()
void
SaltManagerDialog::refresh ()
{
BEGIN_PROTECTED
if (! m_salt_mine_url.empty ()) {
tl::log << tl::to_string (tr ("Downloading package repository from %1").arg (tl::to_qstring (m_salt_mine_url)));
try {
m_salt_mine_reader.reset (new tl::InputStream (m_salt_mine_url));
salt_mine_download_started ();
QApplication::setOverrideCursor (Qt::WaitCursor);
lay::Salt new_mine;
new_mine.load (m_salt_mine_url);
m_salt_mine = new_mine;
QApplication::restoreOverrideCursor ();
} catch (...) {
QApplication::restoreOverrideCursor ();
throw;
tl::InputHttpStream *http = dynamic_cast<tl::InputHttpStream *> (m_salt_mine_reader->base ());
if (http) {
// async reading on HTTP
http->ready ().add (this, &SaltManagerDialog::salt_mine_data_ready);
http->send ();
} else {
salt_mine_data_ready ();
}
salt_mine_changed ();
}
}
void
SaltManagerDialog::salt_mine_download_started ()
{
QApplication::setOverrideCursor (Qt::WaitCursor);
}
void
SaltManagerDialog::salt_mine_download_finished ()
{
QApplication::restoreOverrideCursor ();
m_salt_mine_reader.reset (0);
}
void
SaltManagerDialog::salt_mine_data_ready ()
{
BEGIN_PROTECTED
try {
if (m_salt_mine_reader.get ()) {
lay::Salt new_mine;
new_mine.load (*m_salt_mine_reader);
m_salt_mine = new_mine;
}
salt_mine_download_finished ();
} catch (...) {
salt_mine_download_finished ();
throw;
}
salt_mine_changed ();
END_PROTECTED
}
@ -824,10 +848,7 @@ SaltManagerDialog::update_models ()
salt_mine_view_new->selectionModel ()->blockSignals (false);
}
mine_new_selected_changed ();
mine_update_selected_changed ();
selected_changed ();
update_apply_state ();
mode_changed ();
}
void
@ -929,13 +950,14 @@ SaltManagerDialog::get_remote_grain_info (lay::SaltGrain *g, SaltGrainDetailsTex
return;
}
std::auto_ptr<lay::SaltGrain> remote_grain;
m_downloaded_grain.reset (0);
m_downloaded_grain_reader.reset (0);
mp_downloaded_target = details;
m_salt_mine_grain.reset (new lay::SaltGrain (*g));
// Download actual grain definition file
try {
QApplication::setOverrideCursor (Qt::WaitCursor);
if (g->url ().empty ()) {
throw tl::Exception (tl::to_string (tr ("No download link available")));
}
@ -954,44 +976,75 @@ SaltManagerDialog::get_remote_grain_info (lay::SaltGrain *g, SaltGrainDetailsTex
details->setHtml (html);
QApplication::processEvents (QEventLoop::ExcludeUserInputEvents);
std::string url = g->url ();
m_downloaded_grain_reader.reset (SaltGrain::stream_from_url (url));
m_downloaded_grain.reset (new SaltGrain ());
m_downloaded_grain->set_url (url);
remote_grain.reset (new SaltGrain (SaltGrain::from_url (g->url ())));
if (g->name () != remote_grain->name ()) {
throw tl::Exception (tl::to_string (tr ("Name mismatch between repository and actual package (repository: %1, package: %2)").arg (tl::to_qstring (g->name ())).arg (tl::to_qstring (remote_grain->name ()))));
tl::InputHttpStream *http = dynamic_cast<tl::InputHttpStream *> (m_downloaded_grain_reader->base ());
if (http) {
// async reading on HTTP
http->ready ().add (this, &SaltManagerDialog::data_ready);
http->send ();
} else {
data_ready ();
}
if (SaltGrain::compare_versions (g->version (), remote_grain->version ()) != 0) {
throw tl::Exception (tl::to_string (tr ("Version mismatch between repository and actual package (repository: %1, package: %2)").arg (tl::to_qstring (g->version ())).arg (tl::to_qstring (remote_grain->version ()))));
}
details->set_grain (remote_grain.get ());
QApplication::restoreOverrideCursor ();
} catch (tl::Exception &ex) {
QApplication::restoreOverrideCursor ();
remote_grain.reset (0);
QString html = tr (
"<html>"
"<body>"
"<font color=\"#ff0000\">"
"<h2>Error Fetching Package Definition</h2>"
"<p><b>URL</b>: %1</p>"
"<p><b>Error</b>: %2</p>"
"</font>"
"</body>"
"</html>"
)
.arg (tl::to_qstring (SaltGrain::spec_url (g->url ())))
.arg (tl::to_qstring (tl::escaped_to_html (ex.msg ())));
details->setHtml (html);
show_error (ex);
}
}
void
SaltManagerDialog::data_ready ()
{
if (! m_salt_mine_grain.get () || ! m_downloaded_grain.get () || ! m_downloaded_grain_reader.get () || ! mp_downloaded_target) {
return;
}
m_downloaded_grain->load (*m_downloaded_grain_reader);
try {
if (m_salt_mine_grain->name () != m_downloaded_grain->name ()) {
throw tl::Exception (tl::to_string (tr ("Name mismatch between repository and actual package (repository: %1, package: %2)").arg (tl::to_qstring (m_salt_mine_grain->name ())).arg (tl::to_qstring (m_downloaded_grain->name ()))));
}
if (SaltGrain::compare_versions (m_salt_mine_grain->version (), m_downloaded_grain->version ()) != 0) {
throw tl::Exception (tl::to_string (tr ("Version mismatch between repository and actual package (repository: %1, package: %2)").arg (tl::to_qstring (m_salt_mine_grain->version ())).arg (tl::to_qstring (m_downloaded_grain->version ()))));
}
mp_downloaded_target->set_grain (m_downloaded_grain.get ());
m_downloaded_grain.reset (0);
m_downloaded_grain_reader.reset (0);
m_salt_mine_grain.reset (0);
} catch (tl::Exception &ex) {
show_error (ex);
}
}
void
SaltManagerDialog::show_error (tl::Exception &ex)
{
QString html = tr (
"<html>"
"<body>"
"<font color=\"#ff0000\">"
"<h2>Error Fetching Package Definition</h2>"
"<p><b>URL</b>: %1</p>"
"<p><b>Error</b>: %2</p>"
"</font>"
"</body>"
"</html>"
)
.arg (tl::to_qstring (m_downloaded_grain->url ()))
.arg (tl::to_qstring (tl::escaped_to_html (ex.msg ())));
mp_downloaded_target->setHtml (html);
m_downloaded_grain.reset (0);
m_downloaded_grain_reader.reset (0);
m_salt_mine_grain.reset (0);
}
}

View File

@ -26,6 +26,8 @@
#include "ui_SaltManagerDialog.h"
#include "laySalt.h"
#include "tlDeferredExecution.h"
#include "tlHttpStream.h"
#include "tlException.h"
#include <QDialog>
#include <memory>
@ -40,7 +42,7 @@ class SaltGrainPropertiesDialog;
* @brief The dialog for managing the Salt ("Packages")
*/
class SaltManagerDialog
: public QDialog, private Ui::SaltManagerDialog
: public QDialog, private Ui::SaltManagerDialog, public tl::Object
{
Q_OBJECT
@ -58,6 +60,17 @@ public:
return m_salt_mine_url;
}
private:
/**
* @brief Called when data is available from the grain downloader
*/
void data_ready ();
/**
* @brief Called when data is available from the salt mine downloader
*/
void salt_mine_data_ready ();
private slots:
/**
* @brief Called when the list of packages (grains) is about to change
@ -171,6 +184,10 @@ private:
SaltGrainPropertiesDialog *mp_properties_dialog;
tl::DeferredMethod<SaltManagerDialog> dm_update_models;
int m_current_tab;
std::auto_ptr<tl::InputStream> m_downloaded_grain_reader;
std::auto_ptr<lay::SaltGrain> m_downloaded_grain, m_salt_mine_grain;
SaltGrainDetailsTextWidget *mp_downloaded_target;
std::auto_ptr<tl::InputStream> m_salt_mine_reader;
SaltGrain *current_grain ();
std::vector<lay::SaltGrain *> current_grains ();
@ -179,6 +196,9 @@ private:
void update_apply_state ();
void get_remote_grain_info (lay::SaltGrain *g, SaltGrainDetailsTextWidget *details);
void consolidate_salt_mine_entries ();
void show_error (tl::Exception &ex);
void salt_mine_download_started ();
void salt_mine_download_finished ();
};
}