[netinstall] Improve loader queue API a bit

- use load() to start loading
- the FetchNextUnless class is useful in more spots in
  the loading process
- set status explicitly on success (otherwise, a failure in a
  previous URL would leave a failure message lying around even
  when the module shows something useful)
This commit is contained in:
Adriaan de Groot 2021-03-19 12:30:09 +01:00
parent 03d086a233
commit fdfe52efe2
4 changed files with 69 additions and 35 deletions

View File

@ -54,9 +54,11 @@ Config::status() const
case Status::FailedBadData:
return tr( "Network Installation. (Disabled: Received invalid groups data)" );
case Status::FailedInternalError:
return tr( "Network Installation. (Disabled: internal error)" );
return tr( "Network Installation. (Disabled: Internal error)" );
case Status::FailedNetworkError:
return tr( "Network Installation. (Disabled: Unable to fetch package lists, check your network connection)" );
case Status::FailedNoData:
return tr( "Network Installation. (Disabled: No package list)" );
}
__builtin_unreachable();
}
@ -89,6 +91,11 @@ Config::loadGroupList( const QVariantList& groupData )
if ( m_model->rowCount() < 1 )
{
cWarning() << "NetInstall groups data was empty.";
setStatus( Status::FailedNoData );
}
else
{
setStatus( Status::Ok );
}
emit statusReady();
}
@ -134,7 +141,7 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
m_queue = new LoaderQueue( this );
m_queue->append( SourceItem::makeSourceItem( groupsUrlVariant.toString(), configurationMap ) );
}
else if ( groupsUrlVariant.type() == QVariant::StringList )
else if ( groupsUrlVariant.type() == QVariant::List )
{
m_queue = new LoaderQueue( this );
for ( const auto& s : groupsUrlVariant.toStringList() )
@ -142,10 +149,11 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
m_queue->append( SourceItem::makeSourceItem( s, configurationMap ) );
}
}
if ( m_queue )
if ( m_queue && m_queue->count() > 0 )
{
cDebug() << "Loading netinstall from" << m_queue->count() << "alternate sources.";
connect( m_queue, &LoaderQueue::done, this, &Config::loadingDone );
QMetaObject::invokeMethod( m_queue, "fetchNext", Qt::QueuedConnection );
m_queue->load();
}
}

View File

@ -47,7 +47,9 @@ public:
FailedBadConfiguration,
FailedInternalError,
FailedNetworkError,
FailedBadData
FailedBadData,
FailedNoData
};
QString status() const;

View File

@ -20,6 +20,32 @@
#include <QNetworkReply>
#include <QTimer>
/** @brief Call fetchNext() on the queue if it can
*
* On destruction, a new call to fetchNext() is queued, so that
* the queue continues loading. Calling release() before the
* destructor skips the fetchNext(), ending the queue-loading.
*/
class FetchNextUnless
{
public:
FetchNextUnless( LoaderQueue* q )
: m_q( q )
{
}
~FetchNextUnless()
{
if ( m_q )
{
QMetaObject::invokeMethod( m_q, "fetchNext", Qt::QueuedConnection );
}
}
void release() { m_q = nullptr; }
private:
LoaderQueue* m_q = nullptr;
};
SourceItem
SourceItem::makeSourceItem( const QString& groupsUrl, const QVariantMap& configurationMap )
{
@ -45,6 +71,13 @@ LoaderQueue::append( SourceItem&& i )
m_queue.append( std::move( i ) );
}
void
LoaderQueue::load()
{
QMetaObject::invokeMethod( this, "fetchNext", Qt::QueuedConnection );
}
void
LoaderQueue::fetchNext()
{
@ -67,13 +100,16 @@ LoaderQueue::fetchNext()
}
}
void
LoaderQueue::fetch( const QUrl& url )
{
FetchNextUnless next( this );
if ( !url.isValid() )
{
m_config->setStatus( Config::Status::FailedBadConfiguration );
cDebug() << "Invalid URL" << url;
return;
}
using namespace CalamaresUtils::Network;
@ -85,42 +121,20 @@ LoaderQueue::fetch( const QUrl& url )
if ( !reply )
{
cDebug() << Logger::Continuation << "request failed immediately.";
cDebug() << Logger::SubEntry << "Request failed immediately.";
// If nobody sets a different status, this will remain
m_config->setStatus( Config::Status::FailedBadConfiguration );
}
else
{
// When the network request is done, **then** we might
// do the next item from the queue, so don't call fetchNext() now.
next.release();
m_reply = reply;
connect( reply, &QNetworkReply::finished, this, &LoaderQueue::dataArrived );
}
}
/** @brief Call fetchNext() on the queue if it can
*
* On destruction, a new call to fetchNext() is queued, so that
* the queue continues loading. Calling release() before the
* destructor skips the fetchNext(), ending the queue-loading.
*/
class FetchNextUnless
{
public:
FetchNextUnless( LoaderQueue* q )
: m_q( q )
{
}
~FetchNextUnless()
{
if ( m_q )
{
QMetaObject::invokeMethod( m_q, "fetchNext", Qt::QueuedConnection );
}
}
void release() { m_q = nullptr; }
private:
LoaderQueue* m_q = nullptr;
};
void
LoaderQueue::dataArrived()
{

View File

@ -41,7 +41,14 @@ struct SourceItem
static SourceItem makeSourceItem( const QString& groupsUrl, const QVariantMap& configurationMap );
};
/** @brief Queue of source items to load
*
* Queue things up by calling append() and then kick things off
* by calling load(). This will try to load the items, in order;
* the first one that succeeds will end the loading process.
*
* Signal done() is emitted when done (also when all of the items fail).
*/
class LoaderQueue : public QObject
{
Q_OBJECT
@ -49,9 +56,12 @@ public:
LoaderQueue( Config* parent );
void append( SourceItem&& i );
void fetchNext();
int count() const { return m_queue.count(); }
public Q_SLOTS:
void load();
void fetchNext();
void fetch( const QUrl& url );
void dataArrived();