Added ability to test the connection in the dialog where the connection details are entered.

This commit is contained in:
eelke 2021-07-04 16:45:50 +02:00
parent 45795333f2
commit c00a0452d1
7 changed files with 201 additions and 43 deletions

View file

@ -3,21 +3,27 @@
#include "ConnectionConfig.h" #include "ConnectionConfig.h"
#include "ConnectionController.h" #include "ConnectionController.h"
#include "ConnectionListModel.h" #include "ConnectionListModel.h"
#include "Pgsql_Connection.h"
#include "Pgsql_PgException.h"
#include "util.h" #include "util.h"
#include <QApplication> #include <QApplication>
#include <QCheckBox>
#include <QComboBox>
#include <QDataWidgetMapper> #include <QDataWidgetMapper>
#include <QDebug>
#include <QDialog>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QFormLayout> #include <QFormLayout>
#include <QFuture>
#include <QLabel> #include <QLabel>
#include <QLineEdit> #include <QLineEdit>
#include <QPushButton> #include <QPushButton>
#include <QSpinBox> #include <QSpinBox>
#include <QComboBox> #include <QtConcurrent>
#include <QDialog>
#include <QDebug>
#define SET_OBJECT_NAME(var) var->setObjectName(#var) #define SET_OBJECT_NAME(var) var->setObjectName(#var)
void ConnectionConfigurationWidget::editExistingInWindow(ConnectionController *ctrl, const ConnectionConfig &cfg) void ConnectionConfigurationWidget::editExistingInWindow(ConnectionController *ctrl, const ConnectionConfig &cfg)
{ {
try { try {
@ -25,7 +31,6 @@ void ConnectionConfigurationWidget::editExistingInWindow(ConnectionController *c
w->setData(cfg); w->setData(cfg);
auto btn_hbox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); auto btn_hbox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal);
//auto btn_test = btn_hbox->addButton(tr("Test"), QDialogButtonBox::ActionRole);
auto vbox = new QVBoxLayout; auto vbox = new QVBoxLayout;
vbox->addWidget(w); vbox->addWidget(w);
@ -37,7 +42,8 @@ void ConnectionConfigurationWidget::editExistingInWindow(ConnectionController *c
win->setAttribute( Qt::WA_DeleteOnClose, true ); win->setAttribute( Qt::WA_DeleteOnClose, true );
QObject::connect(btn_hbox, &QDialogButtonBox::accepted, [ctrl, w, win] () { QObject::connect(btn_hbox, &QDialogButtonBox::accepted, [ctrl, w, win] () {
auto [grp, cc] = w->data(); auto cc = w->data();
auto grp = w->group();
ctrl->getConnectionTreeModel()->save(grp, cc); ctrl->getConnectionTreeModel()->save(grp, cc);
win->accept(); win->accept();
}); });
@ -55,7 +61,7 @@ void ConnectionConfigurationWidget::editExistingInWindow(ConnectionController *c
ConnectionConfigurationWidget::ConnectionConfigurationWidget(ConnectionTreeModel *connection_model, QWidget *parent) ConnectionConfigurationWidget::ConnectionConfigurationWidget(ConnectionTreeModel *connection_model, QWidget *parent)
: QWidget(parent) : QWidget(parent)
, m_connectionModel(connection_model) , m_connectionModel(connection_model)
{ {
lblGroup = new QLabel; lblGroup = new QLabel;
cmbbxGroup = new QComboBox; cmbbxGroup = new QComboBox;
@ -88,11 +94,20 @@ ConnectionConfigurationWidget::ConnectionConfigurationWidget(ConnectionTreeModel
SET_OBJECT_NAME(edtUser); SET_OBJECT_NAME(edtUser);
lblUser->setBuddy(edtUser); lblUser->setBuddy(edtUser);
lblPassword = new QLabel;
SET_OBJECT_NAME(lblPassword);
edtPassword = new QLineEdit;
SET_OBJECT_NAME(edtPassword);
edtPassword->setEchoMode(QLineEdit::PasswordEchoOnEdit);
cbSavePassword = new QCheckBox;
SET_OBJECT_NAME(cbSavePassword);
lblDbName = new QLabel; lblDbName = new QLabel;
SET_OBJECT_NAME(lblDbName); SET_OBJECT_NAME(lblDbName);
edtDbname = new QLineEdit; cmbDbname = new QComboBox;
SET_OBJECT_NAME(edtDbname); cmbDbname->setEditable(true); // allows to enter the database name even if test failed.
lblDbName->setBuddy(edtDbname); cmbDbname->setInsertPolicy(QComboBox::InsertAlphabetically);
lblSsl = new QLabel; lblSsl = new QLabel;
SET_OBJECT_NAME(lblSsl); SET_OBJECT_NAME(lblSsl);
@ -127,6 +142,11 @@ ConnectionConfigurationWidget::ConnectionConfigurationWidget(ConnectionTreeModel
SET_OBJECT_NAME(edtCrl); SET_OBJECT_NAME(edtCrl);
lblCrl->setBuddy(edtCrl); lblCrl->setBuddy(edtCrl);
btnTest = new QPushButton;
connect(btnTest, &QPushButton::clicked, this, &ConnectionConfigurationWidget::testConnection);
lblResult = new QLabel;
formLayout = new QFormLayout; formLayout = new QFormLayout;
formLayout->addRow(lblGroup, cmbbxGroup); formLayout->addRow(lblGroup, cmbbxGroup);
@ -134,13 +154,19 @@ ConnectionConfigurationWidget::ConnectionConfigurationWidget(ConnectionTreeModel
formLayout->addRow(lblHost, edtHost); formLayout->addRow(lblHost, edtHost);
formLayout->addRow(lblPort, spinPort); formLayout->addRow(lblPort, spinPort);
formLayout->addRow(lblUser, edtUser); formLayout->addRow(lblUser, edtUser);
formLayout->addRow(lblDbName, edtDbname); formLayout->addRow(lblPassword, edtPassword);
formLayout->addRow(nullptr, cbSavePassword);
formLayout->addRow(lblDbName, cmbDbname);
formLayout->addRow(lblSsl, cmbbxSsl); formLayout->addRow(lblSsl, cmbbxSsl);
formLayout->addRow(lblCert, edtCert); formLayout->addRow(lblCert, edtCert);
formLayout->addRow(lblKey, edtKey); formLayout->addRow(lblKey, edtKey);
formLayout->addRow(lblRootCert, edtRootCert); formLayout->addRow(lblRootCert, edtRootCert);
formLayout->addRow(lblCrl, edtCrl); formLayout->addRow(lblCrl, edtCrl);
setLayout(formLayout); formLayout->addRow(btnTest, lblResult);
setLayout(formLayout);
connect(&TestWatcher, &QFutureWatcher<TestConnectionResult>::finished, this,
&ConnectionConfigurationWidget::handleTestResult);
retranslateUi(); retranslateUi();
} }
@ -152,12 +178,15 @@ void ConnectionConfigurationWidget::retranslateUi()
lblHost->setText(QApplication::translate("ConnectionConfigurationWidget", "&Host", nullptr)); lblHost->setText(QApplication::translate("ConnectionConfigurationWidget", "&Host", nullptr));
lblPort->setText(QApplication::translate("ConnectionConfigurationWidget", "&Port", nullptr)); lblPort->setText(QApplication::translate("ConnectionConfigurationWidget", "&Port", nullptr));
lblUser->setText(QApplication::translate("ConnectionConfigurationWidget", "&Username", nullptr)); lblUser->setText(QApplication::translate("ConnectionConfigurationWidget", "&Username", nullptr));
lblDbName->setText(QApplication::translate("ConnectionConfigurationWidget", "&Database", nullptr)); lblPassword->setText(QApplication::translate("ConnectionConfigurationWidget", "Password", nullptr));
cbSavePassword->setText(QApplication::translate("ConnectionConfigurationWidget", "&Save password", nullptr));
lblDbName->setText(QApplication::translate("ConnectionConfigurationWidget", "Database", nullptr));
lblSsl->setText(QApplication::translate("ConnectionConfigurationWidget", "&SSL mode", nullptr)); lblSsl->setText(QApplication::translate("ConnectionConfigurationWidget", "&SSL mode", nullptr));
lblCert->setText(QApplication::translate("ConnectionConfigurationWidget", "&Certificate", nullptr)); lblCert->setText(QApplication::translate("ConnectionConfigurationWidget", "&Certificate", nullptr));
lblKey->setText(QApplication::translate("ConnectionConfigurationWidget", "&Key", nullptr)); lblKey->setText(QApplication::translate("ConnectionConfigurationWidget", "&Key", nullptr));
lblRootCert->setText(QApplication::translate("ConnectionConfigurationWidget", "&Root cert.", nullptr)); lblRootCert->setText(QApplication::translate("ConnectionConfigurationWidget", "&Root cert.", nullptr));
lblCrl->setText(QApplication::translate("ConnectionConfigurationWidget", "Revocation &list", nullptr)); lblCrl->setText(QApplication::translate("ConnectionConfigurationWidget", "Revocation &list", nullptr));
btnTest->setText(QApplication::translate("ConnectionConfigurationWidget", "Test", nullptr));
} }
void ConnectionConfigurationWidget::setData(const ConnectionConfig &cfg) void ConnectionConfigurationWidget::setData(const ConnectionConfig &cfg)
@ -173,7 +202,7 @@ void ConnectionConfigurationWidget::setData(const ConnectionConfig &cfg)
edtHost->setText(cfg.host()); edtHost->setText(cfg.host());
spinPort->setValue(cfg.port()); spinPort->setValue(cfg.port());
edtUser->setText(cfg.user()); edtUser->setText(cfg.user());
edtDbname->setText(cfg.dbname()); cmbDbname->setCurrentText(cfg.dbname());
cmbbxSsl->setCurrentIndex(static_cast<int>(cfg.sslMode())); cmbbxSsl->setCurrentIndex(static_cast<int>(cfg.sslMode()));
edtCert->setText(cfg.sslCert()); edtCert->setText(cfg.sslCert());
edtKey->setText(cfg.sslKey()); edtKey->setText(cfg.sslKey());
@ -181,23 +210,46 @@ void ConnectionConfigurationWidget::setData(const ConnectionConfig &cfg)
edtCrl->setText(cfg.sslCrl()); edtCrl->setText(cfg.sslCrl());
} }
std::tuple<QString, ConnectionConfig> ConnectionConfigurationWidget::data() const ConnectionConfig ConnectionConfigurationWidget::data() const
{ {
QString group;
group = cmbbxGroup->currentText();
ConnectionConfig cfg; ConnectionConfig cfg;
cfg.setUuid(m_uuid); cfg.setUuid(m_uuid);
cfg.setName(edtName->text()); cfg.setName(edtName->text());
cfg.setHost(edtHost->text()); cfg.setHost(edtHost->text());
cfg.setPort(static_cast<uint16_t>(spinPort->value())); cfg.setPort(static_cast<uint16_t>(spinPort->value()));
cfg.setUser(edtUser->text()); cfg.setUser(edtUser->text());
cfg.setDbname(edtDbname->text()); cfg.setPassword(edtPassword->text());
cfg.setDbname(cmbDbname->currentText());
cfg.setSslMode(static_cast<SslMode>(cmbbxSsl->currentIndex())); cfg.setSslMode(static_cast<SslMode>(cmbbxSsl->currentIndex()));
cfg.setSslCert(edtCert->text()); cfg.setSslCert(edtCert->text());
cfg.setSslKey(edtKey->text()); cfg.setSslKey(edtKey->text());
cfg.setSslRootCert(edtRootCert->text()); cfg.setSslRootCert(edtRootCert->text());
cfg.setSslCrl(edtCrl->text()); cfg.setSslCrl(edtCrl->text());
return { group, cfg }; return cfg;
}
QString ConnectionConfigurationWidget::group() const
{
return cmbbxGroup->currentText();
}
void ConnectionConfigurationWidget::testConnection()
{
auto cc = data();
QFuture<TestConnectionResult> result = QtConcurrent::run([cc] { return TestConnection(cc); });
TestWatcher.setFuture(result);
}
void ConnectionConfigurationWidget::handleTestResult()
{
auto f = TestWatcher.future();
auto result = f.result();
lblResult->setText(result.message());
QString current = cmbDbname->currentText();
cmbDbname->clear();
for (auto &dbn : result.databases())
cmbDbname->addItem(dbn);
cmbDbname->setCurrentText(current);
} }

View file

@ -3,19 +3,24 @@
#include <QWidget> #include <QWidget>
#include <QUuid> #include <QUuid>
#include <QFutureWatcher>
#include <tuple> #include <tuple>
#include "TestConnection.h"
class ConnectionController; class ConnectionController;
class ConnectionConfig; class ConnectionConfig;
class QCheckBox;
class QComboBox;
class QDataWidgetMapper;
class QFormLayout; class QFormLayout;
class QLabel; class QLabel;
class QLineEdit; class QLineEdit;
class QPushButton;
class QSpinBox; class QSpinBox;
class QComboBox;
class QDataWidgetMapper;
class ConnectionTreeModel; class ConnectionTreeModel;
class ConnectionConfigurationWidget : public QWidget class ConnectionConfigurationWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
@ -26,11 +31,15 @@ public:
void retranslateUi(); void retranslateUi();
void setData(const ConnectionConfig &cfg); void setData(const ConnectionConfig &cfg);
std::tuple<QString, ConnectionConfig> data() const; ConnectionConfig data() const;
QString group() const;
signals: signals:
public slots:
void testConnection();
private: private:
ConnectionTreeModel *m_connectionModel; ConnectionTreeModel *m_connectionModel;
QUuid m_uuid; QUuid m_uuid;
@ -44,8 +53,11 @@ private:
QSpinBox *spinPort; QSpinBox *spinPort;
QLabel *lblUser; QLabel *lblUser;
QLineEdit *edtUser; QLineEdit *edtUser;
QLabel *lblDbName; QLabel *lblPassword;
QLineEdit *edtDbname; QLineEdit *edtPassword;
QCheckBox *cbSavePassword;
QLabel *lblDbName;
QComboBox *cmbDbname;
QLabel *lblSsl; QLabel *lblSsl;
QComboBox *cmbbxSsl; QComboBox *cmbbxSsl;
QLabel *lblCert; QLabel *lblCert;
@ -56,9 +68,14 @@ private:
QLineEdit *edtRootCert; QLineEdit *edtRootCert;
QLabel *lblCrl; QLabel *lblCrl;
QLineEdit *edtCrl; QLineEdit *edtCrl;
QFormLayout *formLayout;
public slots: QPushButton *btnTest;
QLabel *lblResult;
QFormLayout *formLayout;
QFutureWatcher<TestConnectionResult> TestWatcher;
private slots:
void handleTestResult();
}; };

View file

@ -173,9 +173,6 @@ void CrudModel::loadData()
Q_ARG(std::shared_ptr<Pgsql::Result>, dbres)); Q_ARG(std::shared_ptr<Pgsql::Result>, dbres));
} }
} }
else {
// emit onQueryError();
}
}); });
} }

View file

@ -51,25 +51,25 @@ public:
/** This version of send uses the signal onQueryResult and onQueryError to report back /** This version of send uses the signal onQueryResult and onQueryError to report back
* the completion of the query. * the completion of the query.
*/ */
bool send(const std::string &command, Pgsql::Params params = Pgsql::Params()) // bool send(const std::string &command, Pgsql::Params params = Pgsql::Params())
{ // {
return send(command, params, [this] (Expected<std::shared_ptr<Pgsql::Result>> res, qint64) { // return send(command, params, [this] (Expected<std::shared_ptr<Pgsql::Result>> res, qint64) {
if (res.valid()) { // if (res.valid()) {
emit onQueryResult(res.get()); // emit onQueryResult(res.get());
} // }
else { // else {
emit onQueryError(); // emit onQueryError();
} // }
}); // });
} // }
bool cancel(); bool cancel();
signals: signals:
void onStateChanged(ASyncDBConnection::State state); void onStateChanged(ASyncDBConnection::State state);
void onNotice(Pgsql::ErrorDetails notice); void onNotice(Pgsql::ErrorDetails notice);
void onQueryResult(std::shared_ptr<Pgsql::Result> result); // void onQueryResult(std::shared_ptr<Pgsql::Result> result);
void onQueryError(); // void onQueryError();
private: private:
Pgsql::Connection m_connection; Pgsql::Connection m_connection;

View file

@ -0,0 +1,58 @@
#include "TestConnection.h"
#include "ConnectionConfig.h"
#include "Pgsql_Connection.h"
#include "Pgsql_PgException.h"
TestConnectionResult TestConnectionResult::Failed(QString msg)
{
return TestConnectionResult(false, msg, {});
}
TestConnectionResult TestConnectionResult::Success(QStringList databases)
{
return TestConnectionResult(true, "Test succesfull", databases);
}
bool TestConnectionResult::ok() const
{
return Ok;
}
QString TestConnectionResult::message() const
{
return Message;
}
const QStringList &TestConnectionResult::databases() const
{
return Databases;
}
TestConnectionResult::TestConnectionResult(bool ok, QString msg, QStringList databases)
: Ok(ok)
, Message(msg)
, Databases(databases)
{}
TestConnectionResult TestConnection(const ConnectionConfig &cc)
{
try {
Pgsql::Connection conn;
conn.connect(cc.connectionString());
auto result = conn.query(
"SELECT datname\n"
" FROM pg_database\n"
" WHERE datallowconn AND has_database_privilege(datname, 'connect')\n"
" ORDER BY 1");
QStringList dbs;
for (auto row : result)
dbs.append(row.get(0));
return TestConnectionResult::Success(dbs);
}
catch (const Pgsql::PgException &ex) {
return TestConnectionResult::Failed(QString::fromUtf8(ex.what()));
}
}

32
pglablib/TestConnection.h Normal file
View file

@ -0,0 +1,32 @@
#pragma once
#include <QString>
#include <QStringList>
class ConnectionConfig;
class TestConnectionResult {
public:
/// Creates an instance signalling failure
static TestConnectionResult Failed(QString msg);
/// Creates a result instance signalling success
static TestConnectionResult Success(QStringList databases);
TestConnectionResult() = default;
bool ok() const;
QString message() const;
const QStringList& databases() const;
private:
bool Ok;
QString Message;
QStringList Databases;
TestConnectionResult(bool ok, QString msg, QStringList databases);
};
/// Test if a connection can be opened using the details in cc
///
/// It returns either the error message from the string on failure
/// or a list of databases the details give access to.
TestConnectionResult TestConnection(const ConnectionConfig &cc);

View file

@ -23,6 +23,7 @@ SOURCES += \
Pglablib.cpp \ Pglablib.cpp \
ASyncDBConnection.cpp \ ASyncDBConnection.cpp \
ConnectionConfig.cpp \ ConnectionConfig.cpp \
TestConnection.cpp \
WaitHandleList.cpp \ WaitHandleList.cpp \
catalog/PgType.cpp \ catalog/PgType.cpp \
catalog/PgTypeContainer.cpp \ catalog/PgTypeContainer.cpp \
@ -89,6 +90,7 @@ HEADERS += \
Pglablib.h \ Pglablib.h \
ASyncDBConnection.h \ ASyncDBConnection.h \
ConnectionConfig.h \ ConnectionConfig.h \
TestConnection.h \
WaitHandleList.h \ WaitHandleList.h \
Win32Event.h \ Win32Event.h \
catalog/PgType.h \ catalog/PgType.h \