PackageInfoLookup

This commit is contained in:
Martchus 2015-09-29 21:52:30 +02:00
parent 6d8cf66f99
commit a0d494ca93
12 changed files with 167 additions and 93 deletions

View File

@ -607,6 +607,12 @@ const QJsonArray Manager::packageInfo(const QJsonObject &pkgSelection, PackageIn
const auto pkgName = entryObj.value(QStringLiteral("name")).toString(); const auto pkgName = entryObj.value(QStringLiteral("name")).toString();
if(!pkgName.isEmpty()) { if(!pkgName.isEmpty()) {
QJsonObject res; QJsonObject res;
res.insert(QStringLiteral("name"), pkgName);
res.insert(QStringLiteral("repo"), repo->name());
const auto index = entryObj.value(QStringLiteral("index"));
if(!index.isNull() && !index.isUndefined()) {
res.insert(QStringLiteral("index"), index);
}
if(auto *pkg = repo->packageByName(pkgName)) { if(auto *pkg = repo->packageByName(pkgName)) {
if(part & Basics) { if(part & Basics) {
res.insert(QStringLiteral("basics"), pkg->basicInfo()); res.insert(QStringLiteral("basics"), pkg->basicInfo());
@ -617,12 +623,6 @@ const QJsonArray Manager::packageInfo(const QJsonObject &pkgSelection, PackageIn
} else { } else {
res.insert(QStringLiteral("error"), QStringLiteral("na")); res.insert(QStringLiteral("error"), QStringLiteral("na"));
} }
res.insert(QStringLiteral("name"), pkgName);
res.insert(QStringLiteral("repo"), repo->name());
const auto index = entryObj.value(QStringLiteral("index"));
if(!index.isNull() && !index.isUndefined()) {
res.insert(QStringLiteral("index"), index);
}
results << res; results << res;
} }
} }

View File

@ -1,4 +1,4 @@
#include "./package.h" #include "./package.h"
#include "./alpmdatabase.h" #include "./alpmdatabase.h"
#include "./utilities.h" #include "./utilities.h"
#include "./repository.h" #include "./repository.h"

View File

@ -1,49 +1,101 @@
#include "./packageinfolookup.h" #include "./packageinfolookup.h"
#include "./repository.h" #include "./repository.h"
#include <cassert>
using namespace std;
namespace RepoIndex { namespace RepoIndex {
PackageInfoLookup::PackageInfoLookup(Manager &manager, const QJsonObject &packageSelection, Manager::PackageInfoPart part, QObject *parent) : PackageInfoLookup::PackageInfoLookup(Manager &manager, const QJsonObject &request, QObject *parent) :
PackageLookup(parent) PackageLookup(parent),
m_what(request.value(QStringLiteral("what")).toString()),
m_part(Manager::None)
{ {
if(part != Manager::None) { m_id = request.value(QStringLiteral("id"));
for(auto i = packageSelection.constBegin(), end = packageSelection.constEnd(); i != end; ++i) { if(m_what == QLatin1String("basicpkginfo")) {
if(auto *repo = manager.repositoryByName(i.key())) { m_part = Manager::Basics;
QStringList packagesToBeRequested; } else if(m_what == QLatin1String("pkgdetails")) {
for(const auto &entry : i.value().toArray()) { m_part = Manager::Details;
const auto entryObj = entry.toObject(); } else if(m_what == QLatin1String("fullpkginfo")) {
const auto pkgName = entryObj.value(QStringLiteral("name")).toString(); m_part = Manager::Basics | Manager::Details;
if(!pkgName.isEmpty()) { } else {
packagesToBeRequested << pkgName; QJsonObject errorObj;
} errorObj.insert(QStringLiteral("error"), QStringLiteral("invalid_request"));
m_results << errorObj;
return;
}
m_packageSelection = request.value(QStringLiteral("sel")).toObject();
for(auto i = m_packageSelection.constBegin(), end = m_packageSelection.constEnd(); i != end; ++i) {
if(auto *repo = manager.repositoryByName(i.key())) {
QStringList packagesToBeRequested;
for(const auto &entry : i.value().toArray()) {
const auto entryObj = entry.toObject();
const auto pkgName = entryObj.value(QStringLiteral("name")).toString();
if(!pkgName.isEmpty()) {
packagesToBeRequested << pkgName;
} }
if(!packagesToBeRequested.isEmpty()) {
if(const auto *reply = (part & Manager::Details ? repo->requestFullPackageInfo(packagesToBeRequested) : repo->requestPackageInfo(packagesToBeRequested))) {
connect(reply, &PackageReply::resultsAvailable, this, &PackageInfoLookup::addResults);
++m_remainingReplies;
} else {
// no need to request any of the packages
}
}
} else {
// specified repository can not be found
QJsonObject errorObj;
errorObj.insert(QStringLiteral("repo"), i.key());
errorObj.insert(QStringLiteral("error"), QStringLiteral("na"));
m_results << errorObj;
} }
if(!packagesToBeRequested.isEmpty()) {
if(const auto *reply = (m_part & Manager::Details ? repo->requestFullPackageInfo(packagesToBeRequested) : repo->requestPackageInfo(packagesToBeRequested))) {
connect(reply, &PackageReply::resultsAvailable, this, &PackageInfoLookup::addResultsFromReply);
++m_remainingReplies;
} else {
// no need to request any of the packages
addResultsDirectly(packagesToBeRequested, repo);
}
}
} else {
// specified repository can not be found
QJsonObject errorObj;
errorObj.insert(QStringLiteral("repo"), i.key());
errorObj.insert(QStringLiteral("error"), QStringLiteral("na"));
m_results << errorObj;
} }
} }
} }
void PackageInfoLookup::addResults() void PackageInfoLookup::addResultsDirectly(const QStringList &packageNames, const Repository *repo)
{
const auto &packages = repo->packages();
for(const auto &packageName : packageNames) {
QJsonObject res;
res.insert(QStringLiteral("name"), packageName);
res.insert(QStringLiteral("repo"), repo->name());
// TODO: remember index
const auto index = m_packageSelection.value(repo->name()).toObject().value(packageName).toObject().value(QStringLiteral("index"));
if(!index.isNull() && !index.isUndefined()) {
res.insert(QStringLiteral("index"), index);
}
bool avail = false;
try {
if(const auto &pkg = packages.at(packageName)) {
avail = true;
if(m_part & Manager::Basics) {
res.insert(QStringLiteral("basics"), pkg->basicInfo());
}
if(m_part & Manager::Details) {
res.insert(QStringLiteral("details"), pkg->detailedInfo());
}
}
} catch(const out_of_range &) {
}
if(!avail) {
res.insert(QStringLiteral("error"), QStringLiteral("na"));
}
m_results << res;
}
}
void PackageInfoLookup::addResultsFromReply()
{ {
assert(m_remainingReplies); assert(m_remainingReplies);
auto *reply = static_cast<PackageReply *>(sender()); auto *reply = static_cast<PackageReply *>(sender());
m_results << reply->;
reply->deleteLater(); reply->deleteLater();
const auto *repo = reply->repository();
addResultsDirectly(reply->requestedPackages(), repo);
if(!--m_remainingReplies) { if(!--m_remainingReplies) {
emit resultsAvailable(QStringLiteral("pkginfo"), m_id, m_results); emit resultsAvailable(m_what, m_id, m_results);
deleteLater(); deleteLater();
} }
} }

View File

@ -10,11 +10,16 @@ class PackageInfoLookup : public PackageLookup
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit PackageInfoLookup(Manager &manager, const QJsonObject &packageSelection, Manager::PackageInfoPart part, QObject *parent = nullptr); explicit PackageInfoLookup(Manager &manager, const QJsonObject &request, QObject *parent = nullptr);
private slots:
void addResultsDirectly(const QStringList &packageNames, const Repository *repo);
void addResultsFromReply();
private: private:
void addResults(); const QString m_what;
Manager::PackageInfoParts m_part;
QJsonObject m_packageSelection;
QList<Package *> m_packages; QList<Package *> m_packages;
}; };

View File

@ -1,4 +1 @@
#include "./packagelookup.h" #include "./packagelookup.h"

View File

@ -267,7 +267,7 @@ QJsonObject Repository::suggestions(const QString &term) const
// } // }
// } // }
for(const auto &pkgEntry : packages()) { for(const auto &pkgEntry : packages()) {
if(pkgEntry.first.compare(term, Qt::CaseInsensitive) == 0) { if(pkgEntry.first.contains(term, Qt::CaseInsensitive)) {
suggestions << pkgEntry.first; suggestions << pkgEntry.first;
} }
} }

View File

@ -45,27 +45,36 @@ class PackageReply : public Reply
{ {
Q_OBJECT Q_OBJECT
public: public:
PackageReply(QNetworkReply *networkReply, std::map<QString, std::unique_ptr<Package> > &packages); PackageReply(QNetworkReply *networkReply, const QStringList &requestedPackages, Repository *repo);
PackageReply(const QList<QNetworkReply *> &networkReplies, std::map<QString, std::unique_ptr<Package> > &packages); PackageReply(const QList<QNetworkReply *> &networkReplies, const QStringList &requestedPackages, Repository *repo);
const std::map<QString, std::unique_ptr<Package> > &packages() const; const QStringList &requestedPackages() const;
const Repository *repository() const;
protected: protected:
std::map<QString, std::unique_ptr<Package> > &m_packages; QStringList m_requestedPackages;
Repository *m_repo;
}; };
inline PackageReply::PackageReply(QNetworkReply *networkReply, std::map<QString, std::unique_ptr<Package> > &packages) : inline PackageReply::PackageReply(QNetworkReply *networkReply, const QStringList &requestedPackages, Repository *repo) :
Reply(networkReply), Reply(networkReply),
m_packages(packages) m_requestedPackages(requestedPackages),
m_repo(repo)
{} {}
inline PackageReply::PackageReply(const QList<QNetworkReply *> &networkReplies, std::map<QString, std::unique_ptr<Package> > &packages) : inline PackageReply::PackageReply(const QList<QNetworkReply *> &networkReplies, const QStringList &requestedPackages, Repository *repo) :
Reply(networkReplies), Reply(networkReplies),
m_packages(packages) m_requestedPackages(requestedPackages),
m_repo(repo)
{} {}
inline const std::map<QString, std::unique_ptr<Package> > &PackageReply::packages() const inline const QStringList &PackageReply::requestedPackages() const
{ {
return m_packages; return m_requestedPackages;
}
inline const Repository *PackageReply::repository() const
{
return m_repo;
} }
class SuggestionsReply : public Reply class SuggestionsReply : public Reply

View File

@ -116,6 +116,7 @@ private slots:
protected: protected:
explicit UpgradeLookup(QObject *parent = nullptr); explicit UpgradeLookup(QObject *parent = nullptr);
Repository *m_toCheck; Repository *m_toCheck;
unsigned int m_remainingProcesses; unsigned int m_remainingProcesses;
bool m_firstFinished; bool m_firstFinished;
@ -136,6 +137,8 @@ class UpgradeLookupJson : public UpgradeLookup
public: public:
explicit UpgradeLookupJson(Manager &manager, const QJsonObject &request, QObject *parent = nullptr); explicit UpgradeLookupJson(Manager &manager, const QJsonObject &request, QObject *parent = nullptr);
const QJsonArray &errors() const; const QJsonArray &errors() const;
QJsonArray results() const;
bool finished() const;
signals: signals:
void resultsAvailable(const QJsonValue &what, const QJsonValue &id, const QJsonValue &value); void resultsAvailable(const QJsonValue &what, const QJsonValue &id, const QJsonValue &value);
@ -158,6 +161,16 @@ inline const QJsonArray &UpgradeLookupJson::errors() const
return m_errorsArray; return m_errorsArray;
} }
inline QJsonArray UpgradeLookupJson::results() const
{
return QJsonArray(); // results are currently only returned by emitting resultsAvailable()
}
inline bool UpgradeLookupJson::finished() const
{
return !m_remainingProcesses && m_errorsArray.isEmpty();
}
class UpgradeLookupCli : public UpgradeLookup class UpgradeLookupCli : public UpgradeLookup
{ {
Q_OBJECT Q_OBJECT

View File

@ -71,6 +71,28 @@ void Connection::sendResults(const QJsonValue &what, const QJsonValue &id, const
sendJson(response); sendJson(response);
} }
template<class Lookup, typename... Args>
void Connection::performLookup(const QJsonObject &request, Args &&...args)
{
auto *lookup = new Lookup(forward<Args>(args)...);
if(lookup->finished()) {
// the lookup has already finished when constructing the lookup object
// -> send the results immidiately
sendResult(request.value(QStringLiteral("what")), request.value(QStringLiteral("id")), lookup->results());
} else if(!lookup->errors().isEmpty()) {
// error occured during the construction of the lookup object
// -> send the errors immidiately
QJsonObject results;
results.insert(QStringLiteral("errors"), lookup->errors());
sendResult(request.value(QStringLiteral("what")), request.value(QStringLiteral("id")), results);
} else {
// the lookup object has been created without errors but the lookup is not done yet
// -> send results when available
connect(lookup, &Lookup::resultsAvailable, this, &Connection::sendResult);
}
// in any case: the lookup object destroys itself
}
void Connection::handleQuery(const QJsonObject &obj) void Connection::handleQuery(const QJsonObject &obj)
{ {
const auto what = obj.value(QStringLiteral("what")).toString(); const auto what = obj.value(QStringLiteral("what")).toString();
@ -79,39 +101,14 @@ void Connection::handleQuery(const QJsonObject &obj)
m_repoInfoUpdatesRequested = obj.value(QStringLiteral("updates")).toBool(m_repoInfoUpdatesRequested); m_repoInfoUpdatesRequested = obj.value(QStringLiteral("updates")).toBool(m_repoInfoUpdatesRequested);
sendResult(what, id, m_manager.basicRepoInfo()); sendResult(what, id, m_manager.basicRepoInfo());
} else if(what == QLatin1String("basicpkginfo") || what == QLatin1String("pkgdetails") || what == QLatin1String("fullpkginfo")) { } else if(what == QLatin1String("basicpkginfo") || what == QLatin1String("pkgdetails") || what == QLatin1String("fullpkginfo")) {
auto *packageInfoLookup = new PackageInfoLookup(m_manager, obj); performLookup<PackageInfoLookup>(obj, m_manager, obj);
if(packageInfoLookup->finished()) {
sendResult(what, id, packageInfoLookup->results());
} else if(!packageInfoLookup->errors().isEmpty()) {
QJsonObject results;
results.insert(QStringLiteral("errors"), packageInfoLookup->errors());
sendResult(what, id, results);
} else {
connect(packageInfoLookup, &SuggestionsLookup::resultsAvailable, this, &Connection::sendResult);
}
} else if(what == QLatin1String("groupinfo")) { } else if(what == QLatin1String("groupinfo")) {
m_groupInfoUpdatesRequested = obj.value(QStringLiteral("updates")).toBool(m_groupInfoUpdatesRequested); m_groupInfoUpdatesRequested = obj.value(QStringLiteral("updates")).toBool(m_groupInfoUpdatesRequested);
sendResults(what, id, m_manager.groupInfo()); sendResults(what, id, m_manager.groupInfo());
} else if(what == QLatin1String("suggestions")) { } else if(what == QLatin1String("suggestions")) {
auto *suggestionsLookup = new SuggestionsLookup(m_manager, obj); performLookup<SuggestionsLookup>(obj, m_manager, obj);
if(suggestionsLookup->finished()) {
sendResult(what, id, suggestionsLookup->results());
} else if(!suggestionsLookup->errors().isEmpty()) {
QJsonObject results;
results.insert(QStringLiteral("errors"), suggestionsLookup->errors());
sendResult(what, id, results);
} else {
connect(suggestionsLookup, &SuggestionsLookup::resultsAvailable, this, &Connection::sendResult);
}
} else if(what == QLatin1String("upgradelookup")) { } else if(what == QLatin1String("upgradelookup")) {
auto *upgradeLookup = new UpgradeLookupJson(m_manager, obj); performLookup<UpgradeLookupJson>(obj, m_manager, obj);
if(upgradeLookup->errors().isEmpty()) {
connect(upgradeLookup, &UpgradeLookupJson::resultsAvailable, this, &Connection::sendResult);
} else {
QJsonObject results;
results.insert(QStringLiteral("errors"), upgradeLookup->errors());
sendResult(what, id, results);
}
} else if(what == QLatin1String("ping")) { } else if(what == QLatin1String("ping")) {
sendResult(what, id, QStringLiteral("pong")); sendResult(what, id, QStringLiteral("pong"));
} else { } else {

View File

@ -31,12 +31,12 @@ private:
void sendError(const QString &msg, const QJsonValue &id = QJsonValue()); void sendError(const QString &msg, const QJsonValue &id = QJsonValue());
void handleQuery(const QJsonObject &obj); void handleQuery(const QJsonObject &obj);
void handleCmd(const QJsonObject &obj); void handleCmd(const QJsonObject &obj);
template<class Lookup, typename... Args> void performLookup(const QJsonObject &request, Args &&...args);
QWebSocket *m_socket; QWebSocket *m_socket;
RepoIndex::Manager &m_manager; RepoIndex::Manager &m_manager;
bool m_repoInfoUpdatesRequested; bool m_repoInfoUpdatesRequested;
bool m_groupInfoUpdatesRequested; bool m_groupInfoUpdatesRequested;
}; };
} }

View File

@ -33,8 +33,8 @@ QUrl UserRepository::m_aurPkgbuildUrl = QUrl(QStringLiteral("https://aur.archlin
QUrl UserRepository::m_aurSrcInfoUrl = QUrl(QStringLiteral("https://aur.archlinux.org/cgit/aur.git/plain/.SRCINFO")); QUrl UserRepository::m_aurSrcInfoUrl = QUrl(QStringLiteral("https://aur.archlinux.org/cgit/aur.git/plain/.SRCINFO"));
QString UserRepository::m_aurSnapshotPath = QStringLiteral("https://aur.archlinux.org/cgit/aur.git/snapshot/%1.tar.gz"); QString UserRepository::m_aurSnapshotPath = QStringLiteral("https://aur.archlinux.org/cgit/aur.git/snapshot/%1.tar.gz");
AurPackageReply::AurPackageReply(QNetworkReply *networkReply, UserRepository *userRepo) : AurPackageReply::AurPackageReply(QNetworkReply *networkReply, const QStringList &requestedPackages, UserRepository *userRepo) :
PackageReply(networkReply, userRepo->packages()), PackageReply(networkReply, requestedPackages, userRepo),
m_userRepo(userRepo) m_userRepo(userRepo)
{} {}
@ -46,13 +46,14 @@ void AurPackageReply::processData()
//QByteArray data = m_networkReply->readAll(); //QByteArray data = m_networkReply->readAll();
//cerr << shchar << "AUR reply: " << data.data() << endl; //cerr << shchar << "AUR reply: " << data.data() << endl;
//const QJsonDocument doc = QJsonDocument::fromJson(data, &error); //const QJsonDocument doc = QJsonDocument::fromJson(data, &error);
const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll(), &error); const auto doc = QJsonDocument::fromJson(reply->readAll(), &error);
auto &packages = m_repo->packages();
if(error.error == QJsonParseError::NoError) { if(error.error == QJsonParseError::NoError) {
for(const auto &result : doc.object().value(QStringLiteral("results")).toArray()) { for(const auto &result : doc.object().value(QStringLiteral("results")).toArray()) {
QJsonObject obj = result.toObject(); QJsonObject obj = result.toObject();
QString packageName = obj.value(QStringLiteral("Name")).toString(); QString packageName = obj.value(QStringLiteral("Name")).toString();
if(!packageName.isEmpty()) { if(!packageName.isEmpty()) {
auto &package = m_packages[packageName]; auto &package = packages[packageName];
if(package) { if(package) {
static_cast<AurPackage *>(package.get())->putJson(obj); static_cast<AurPackage *>(package.get())->putJson(obj);
} else { } else {
@ -69,8 +70,8 @@ void AurPackageReply::processData()
emit resultsAvailable(); emit resultsAvailable();
} }
AurFullPackageReply::AurFullPackageReply(const QList<QNetworkReply *> &networkReplies, UserRepository *userRepo) : AurFullPackageReply::AurFullPackageReply(const QList<QNetworkReply *> &networkReplies, const QStringList &requestedPackages, UserRepository *userRepo) :
PackageReply(networkReplies, userRepo->packages()), PackageReply(networkReplies, requestedPackages, userRepo),
m_userRepo(userRepo) m_userRepo(userRepo)
{} {}
@ -122,7 +123,7 @@ void AurSuggestionsReply::processData()
//QByteArray data = m_networkReply->readAll(); //QByteArray data = m_networkReply->readAll();
//cerr << shchar << "AUR reply: " << data.data() << endl; //cerr << shchar << "AUR reply: " << data.data() << endl;
//const QJsonDocument doc = QJsonDocument::fromJson(data, &error); //const QJsonDocument doc = QJsonDocument::fromJson(data, &error);
const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll(), &error); const auto doc = QJsonDocument::fromJson(reply->readAll(), &error);
if(error.error == QJsonParseError::NoError) { if(error.error == QJsonParseError::NoError) {
auto &packages = m_repo->packages(); auto &packages = m_repo->packages();
if(doc.isObject()) { if(doc.isObject()) {
@ -226,7 +227,7 @@ AurPackageReply *UserRepository::requestPackageInfo(const QStringList &packageNa
auto url = m_aurRpcUrl; auto url = m_aurRpcUrl;
query.addQueryItem(rpcRequestTypeKey, rpcRequestTypeMultiInfo); query.addQueryItem(rpcRequestTypeKey, rpcRequestTypeMultiInfo);
url.setQuery(query); url.setQuery(query);
return new AurPackageReply(m_networkAccessManager.get(QNetworkRequest(url)), this); return new AurPackageReply(m_networkAccessManager.get(QNetworkRequest(url)), packageNames, this);
} }
} }
@ -252,7 +253,7 @@ AurFullPackageReply *UserRepository::requestFullPackageInfo(const QStringList &p
if(replies.isEmpty()) { if(replies.isEmpty()) {
return nullptr; return nullptr;
} else { } else {
return new AurFullPackageReply(replies, this); return new AurFullPackageReply(replies, packageNames, this);
} }
} }

View File

@ -21,7 +21,7 @@ class AurPackageReply : public PackageReply
{ {
Q_OBJECT Q_OBJECT
public: public:
AurPackageReply(QNetworkReply *networkReply, UserRepository *userRepo); AurPackageReply(QNetworkReply *networkReply, const QStringList &requestedPackages, UserRepository *userRepo);
private slots: private slots:
void processData(); void processData();
@ -34,7 +34,7 @@ class AurFullPackageReply : public PackageReply
{ {
Q_OBJECT Q_OBJECT
public: public:
AurFullPackageReply(const QList<QNetworkReply *> &networkReplies, UserRepository *userRepo); AurFullPackageReply(const QList<QNetworkReply *> &networkReplies, const QStringList &requestedPackages, UserRepository *userRepo);
private slots: private slots:
void processData(); void processData();