Move AWSClient out of the Engine, make it a singleton for now

This commit is contained in:
Michael Zanetti 2018-09-26 12:05:21 +02:00
parent fd0f702916
commit d9f9f30257
13 changed files with 124 additions and 91 deletions

View File

@ -10,6 +10,7 @@
#include "sigv4utils.h"
AWSClient* AWSClient::s_instance = nullptr;
// This is Symantec's root CA certificate and most platforms should
// have this in their certificate storage already, but as we can't
@ -116,6 +117,14 @@ AWSClient::AWSClient(QObject *parent) : QObject(parent),
m_sessionTokenExpiry = settings.value("sessionTokenExpiry").toDateTime();
}
AWSClient *AWSClient::instance()
{
if (!s_instance) {
s_instance = new AWSClient();
}
return s_instance;
}
bool AWSClient::isLoggedIn() const
{
return !m_userId.isEmpty() && !m_username.isEmpty() && !m_password.isEmpty();

View File

@ -96,7 +96,7 @@ public:
};
Q_ENUM(LoginError)
explicit AWSClient(QObject *parent = nullptr);
static AWSClient* instance();
bool isLoggedIn() const;
QString username() const;
@ -145,6 +145,9 @@ signals:
void configChanged();
private:
explicit AWSClient(QObject *parent = nullptr);
static AWSClient* s_instance;
void refreshAccessToken();
void getCredentialsForIdentity(const QString &identityId);
void connectMQTT();

View File

@ -105,15 +105,14 @@ void CloudTransport::ignoreSslErrors(const QList<QSslError> &errors)
m_remoteproxyConnection->ignoreSslErrors(errors);
}
CloudTransportFactory::CloudTransportFactory(AWSClient *awsClient):
m_awsClient(awsClient)
CloudTransportFactory::CloudTransportFactory()
{
}
NymeaTransportInterface *CloudTransportFactory::createTransport(QObject *parent) const
{
return new CloudTransport(m_awsClient, parent);
{
return new CloudTransport(AWSClient::instance(), parent);
}
QStringList CloudTransportFactory::supportedSchemes() const

View File

@ -13,11 +13,9 @@ class RemoteProxyConnection;
class CloudTransportFactory: public NymeaTransportInterfaceFactory
{
public:
CloudTransportFactory(AWSClient *awsClient);
CloudTransportFactory();
NymeaTransportInterface* createTransport(QObject *parent = nullptr) const override;
QStringList supportedSchemes() const override;
private:
AWSClient *m_awsClient = nullptr;
};
class CloudTransport : public NymeaTransportInterface

View File

@ -39,30 +39,29 @@ Engine::Engine(QObject *parent) :
m_ruleManager(new RuleManager(m_jsonRpcClient, this)),
m_logManager(new LogManager(m_jsonRpcClient, this)),
m_tagsManager(new TagsManager(m_jsonRpcClient, this)),
m_basicConfiguration(new BasicConfiguration(m_jsonRpcClient, this)),
m_aws(new AWSClient(this))
m_basicConfiguration(new BasicConfiguration(m_jsonRpcClient, this))
{
m_connection->registerTransport(new TcpSocketTransportFactory());
m_connection->registerTransport(new WebsocketTransportFactory());
m_connection->registerTransport(new BluetoothTransportFactoy());
m_connection->registerTransport(new CloudTransportFactory(m_aws));
m_connection->registerTransport(new CloudTransportFactory());
connect(m_jsonRpcClient, &JsonRpcClient::connectedChanged, this, &Engine::onConnectedChanged);
connect(m_jsonRpcClient, &JsonRpcClient::authenticationRequiredChanged, this, &Engine::onConnectedChanged);
connect(m_deviceManager, &DeviceManager::fetchingDataChanged, this, &Engine::onDeviceManagerFetchingChanged);
connect(m_aws, &AWSClient::devicesFetched, this, [this]() {
connect(AWSClient::instance(), &AWSClient::devicesFetched, this, [this]() {
if (m_jsonRpcClient->connected() && m_jsonRpcClient->cloudConnectionState() == JsonRpcClient::CloudConnectionStateConnected) {
if (m_aws->awsDevices()->getDevice(m_jsonRpcClient->serverUuid()) == nullptr) {
m_jsonRpcClient->setupRemoteAccess(m_aws->idToken(), m_aws->userId());
if (AWSClient::instance()->awsDevices()->getDevice(m_jsonRpcClient->serverUuid()) == nullptr) {
m_jsonRpcClient->setupRemoteAccess(AWSClient::instance()->idToken(), AWSClient::instance()->userId());
}
}
});
connect(m_jsonRpcClient, &JsonRpcClient::connectedChanged, this, [this]() {
if (m_jsonRpcClient->connected() && m_jsonRpcClient->cloudConnectionState() == JsonRpcClient::CloudConnectionStateConnected) {
if (m_aws->awsDevices()->getDevice(m_jsonRpcClient->serverUuid()) == nullptr) {
m_jsonRpcClient->setupRemoteAccess(m_aws->idToken(), m_aws->userId());
if (AWSClient::instance()->awsDevices()->getDevice(m_jsonRpcClient->serverUuid()) == nullptr) {
m_jsonRpcClient->setupRemoteAccess(AWSClient::instance()->idToken(), AWSClient::instance()->userId());
}
}
});
@ -99,10 +98,10 @@ BasicConfiguration *Engine::basicConfiguration() const
return m_basicConfiguration;
}
AWSClient *Engine::awsClient() const
{
return m_aws;
}
//AWSClient *Engine::awsClient() const
//{
// return m_aws;
//}
void Engine::deployCertificate()
{
@ -110,11 +109,11 @@ void Engine::deployCertificate()
qWarning() << "JSONRPC not connected. Cannot deploy certificate";
return;
}
if (!m_aws->isLoggedIn()) {
if (!AWSClient::instance()->isLoggedIn()) {
qWarning() << "Not logged in at AWS. Cannot deploy certificate";
return;
}
m_aws->fetchCertificate(m_jsonRpcClient->serverUuid(), [this](const QByteArray &rootCA, const QByteArray &certificate, const QByteArray &publicKey, const QByteArray &privateKey, const QString &endpoint){
AWSClient::instance()->fetchCertificate(m_jsonRpcClient->serverUuid(), [this](const QByteArray &rootCA, const QByteArray &certificate, const QByteArray &publicKey, const QByteArray &privateKey, const QString &endpoint){
qDebug() << "Certificate received" << certificate << publicKey << privateKey;
m_jsonRpcClient->deployCertificate(rootCA, certificate, publicKey, privateKey, endpoint);
});

View File

@ -43,7 +43,7 @@ class Engine : public QObject
Q_PROPERTY(TagsManager* tagsManager READ tagsManager CONSTANT)
Q_PROPERTY(JsonRpcClient* jsonRpcClient READ jsonRpcClient CONSTANT)
Q_PROPERTY(BasicConfiguration* basicConfiguration READ basicConfiguration CONSTANT)
Q_PROPERTY(AWSClient* awsClient READ awsClient CONSTANT)
// Q_PROPERTY(AWSClient* awsClient READ awsClient CONSTANT)
public:
explicit Engine(QObject *parent = nullptr);
@ -58,7 +58,7 @@ public:
JsonRpcClient *jsonRpcClient() const;
LogManager *logManager() const;
BasicConfiguration *basicConfiguration() const;
AWSClient* awsClient() const;
// AWSClient* awsClient() const;
Q_INVOKABLE void deployCertificate();
@ -70,7 +70,7 @@ private:
LogManager *m_logManager;
TagsManager *m_tagsManager;
BasicConfiguration *m_basicConfiguration;
AWSClient *m_aws;
// AWSClient *m_aws;
private slots:
void onConnectedChanged();

View File

@ -61,6 +61,14 @@ static QObject* interfacesModel_provider(QQmlEngine *engine, QJSEngine *scriptEn
return new Interfaces();
}
static QObject* awsClientProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
return AWSClient::instance();
}
void registerQmlTypes() {
const char uri[] = "Nymea";
@ -161,7 +169,7 @@ void registerQmlTypes() {
qmlRegisterUncreatableType<WirelessAccessPoints>(uri, 1, 0, "WirelessAccessPoints", "Can't create this in QML. Get it from the Engine instance.");
qmlRegisterUncreatableType<WirelessAccessPointsProxy>(uri, 1, 0, "WirelessAccessPoints", "Can't create this in QML. Get it from the Engine instance.");
qmlRegisterUncreatableType<AWSClient>(uri, 1, 0, "AWSClient", "Can't create this in QML. Get it from Engine");
qmlRegisterSingletonType<AWSClient>(uri, 1, 0, "AWSClient", awsClientProvider);
qmlRegisterUncreatableType<AWSDevice>(uri, 1, 0, "AWSDevice", "Can't create this in QML. Get it from AWSClient");
qmlRegisterType<RuleTemplates>(uri, 1, 0, "RuleTemplates");

View File

@ -32,7 +32,6 @@ ApplicationWindow {
readonly property bool landscape: app.width > app.height
readonly property var settings: Settings {
property string lastConnectedHost: ""
property alias viewMode: app.visibility
property bool returnToHome: false
property bool darkTheme: false
@ -42,6 +41,7 @@ ApplicationWindow {
property bool showHiddenOptions: false
property int cloudEnvironment: 0
property bool showConnectionTabs: app.hasOwnProperty("industrialSetup") && app.industrialSetup
property int tabCount: 1
}
RootItem {

View File

@ -2,6 +2,7 @@ import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.2
import QtQuick.Layouts 1.3
import Qt.labs.settings 1.0
import Nymea 1.0
import "components"
@ -16,17 +17,26 @@ Item {
ListModel {
id: tabModel
Component.onCompleted: {
append({})
for (var i = 0; i < settings.tabCount; i++) {
tabModel.append({})
}
}
function addTab() {
settings.lastConnectedHost = ""
tabModel.append({})
settings.tabCount++;
}
function removeTab(index) {
remove(index);
settings.tabCount--;
}
}
ColumnLayout {
anchors.fill: parent
spacing: 0
SwipeView {
id: swipeView
@ -45,13 +55,21 @@ Item {
objectName: "pageStack"
initialItem: Page {}
Settings {
id: tabSettings
category: "tabSettings" + index
property string lastConnectedHost
}
Engine {
id: engine
}
property alias _engine: engine
property int connectionTabIndex: index
onConnectionTabIndexChanged: tabSettings.lastConnectedHost = engine.connection.url
Binding {
target: engine.awsClient
target: AWSClient
property: "config"
value: settings.cloudEnvironment
}
@ -74,14 +92,14 @@ Item {
print("opening push button auth")
var page = pageStack.push(Qt.resolvedUrl("PushButtonAuthPage.qml"))
page.backPressed.connect(function() {
settings.lastConnectedHost = "";
tabSettings.lastConnectedHost = "";
engine.connection.disconnect();
init();
})
} else {
var page = pageStack.push(Qt.resolvedUrl("LoginPage.qml"));
page.backPressed.connect(function() {
settings.lastConnectedHost = "";
tabSettings.lastConnectedHost = "";
engine.connection.disconnect()
init();
})
@ -110,7 +128,7 @@ Item {
askForPermissions = true;
}
if (!engine.awsClient.isLoggedIn) {
if (!AWSClient.isLoggedIn) {
print("AWS not logged in. Cannot register for push");
return;
}
@ -125,7 +143,7 @@ Item {
PlatformHelper.requestPermissions();
}
} else {
engine.awsClient.registerPushNotificationEndpoint(PushNotifications.token, PlatformHelper.deviceManufacturer + " " + PlatformHelper.deviceModel, PlatformHelper.deviceSerial + "+io.guh.nymeaapp");
AWSClient.registerPushNotificationEndpoint(PushNotifications.token, PlatformHelper.deviceManufacturer + " " + PlatformHelper.deviceModel, PlatformHelper.deviceSerial + "+io.guh.nymeaapp");
}
}
@ -134,7 +152,7 @@ Item {
onConnectedChanged: {
print("json client connected changed", engine.jsonRpcClient.connected)
if (engine.jsonRpcClient.connected) {
settings.lastConnectedHost = engine.connection.url
tabSettings.lastConnectedHost = engine.connection.url
}
init();
}
@ -153,7 +171,7 @@ Item {
popup.actualVersion = actualVersion;
popup.minimumVersion = minimumVersion
popup.open()
settings.lastConnectedHost = ""
tabSettings.lastConnectedHost = ""
}
}
@ -183,7 +201,7 @@ Item {
}
Connections {
target: engine.awsClient
target: AWSClient
onIsLoggedInChanged: {
setupPushNotifications()
}
@ -233,6 +251,7 @@ Item {
RowLayout {
visible: settings.showConnectionTabs
spacing: 0
TabBar {
id: tabbar
@ -246,6 +265,7 @@ Item {
id: hostTabButton
property var engine: mainRepeater.itemAt(index)._engine
property string serverName: engine.basicConfiguration.serverName
Material.elevation: index
contentItem: RowLayout {
Label {
@ -260,7 +280,7 @@ Item {
name: "../images/close.svg"
MouseArea {
anchors.fill: parent
onClicked: tabModel.remove(index)
onClicked: tabModel.removeTab(index)
}
}
}
@ -272,17 +292,25 @@ Item {
}
}
TabButton {
Pane {
Layout.preferredHeight: tabbar.height
Layout.preferredWidth: height
contentItem: ColorIcon {
height: parent.height
width: parent.width
name: "../images/add.svg"
}
onClicked: {
tabModel.addTab()
Material.elevation: 2
padding: 0
TabButton {
anchors.fill: parent
contentItem: ColorIcon {
height: parent.height
width: parent.width
name: "../images/add.svg"
}
onClicked: {
tabModel.addTab()
}
}
}
}
}
}

View File

@ -36,7 +36,7 @@ Page {
Button {
text: qsTr("Disconnect")
onClicked: {
settings.lastConnectedHost = "";
tabSettings.lastConnectedHost = "";
engine.connection.disconnect();
}
}

View File

@ -12,17 +12,17 @@ Page {
}
Component.onCompleted: {
if (engine.awsClient.isLoggedIn) {
engine.awsClient.fetchDevices();
if (AWSClient.isLoggedIn) {
AWSClient.fetchDevices();
}
}
Connections {
target: engine.awsClient
target: AWSClient
onLoginResult: {
busyOverlay.shown = false;
if (error === AWSClient.LoginErrorNoError) {
engine.awsClient.fetchDevices();
AWSClient.fetchDevices();
}
}
onDeleteAccountResult: {
@ -39,14 +39,14 @@ Page {
ColumnLayout {
anchors.fill: parent
visible: engine.awsClient.isLoggedIn
visible: AWSClient.isLoggedIn
Label {
Layout.fillWidth: true
Layout.topMargin: app.margins
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
wrapMode: Text.WordWrap
text: qsTr("Logged in as %1").arg(engine.awsClient.username)
text: qsTr("Logged in as %1").arg(AWSClient.username)
}
Button {
@ -66,15 +66,15 @@ Page {
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
wrapMode: Text.WordWrap
text: engine.awsClient.awsDevices.count === 0 ?
text: AWSClient.awsDevices.count === 0 ?
qsTr("There are no boxes connected to your cloud yet.") :
qsTr("There are %n boxes connected to your cloud", "", engine.awsClient.awsDevices.count)
qsTr("There are %n boxes connected to your cloud", "", AWSClient.awsDevices.count)
}
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
model: engine.awsClient.awsDevices
model: AWSClient.awsDevices
delegate: MeaListItemDelegate {
width: parent.width
text: model.name
@ -98,13 +98,13 @@ Page {
}
onDeleteClicked: {
engine.awsClient.unpairDevice(model.id);
AWSClient.unpairDevice(model.id);
}
}
BusyIndicator {
anchors.centerIn: parent
visible: engine.awsClient.awsDevices.busy
visible: AWSClient.awsDevices.busy
}
}
}
@ -132,16 +132,16 @@ Page {
onAccepted: {
if (deleteCheckbox.checked) {
busyOverlay.shown = true;
engine.awsClient.deleteAccount()
AWSClient.deleteAccount()
} else {
engine.awsClient.logout()
AWSClient.logout()
}
}
}
ColumnLayout {
anchors { left: parent.left; right: parent.right; top: parent.top }
visible: !engine.awsClient.isLoggedIn
visible: !AWSClient.isLoggedIn
Label {
Layout.fillWidth: true
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins
@ -197,12 +197,12 @@ Page {
enabled: usernameTextField.acceptableInput
onClicked: {
busyOverlay.shown = true
engine.awsClient.login(usernameTextField.text, passwordTextField.text);
AWSClient.login(usernameTextField.text, passwordTextField.text);
}
}
Connections {
target: engine.awsClient
target: AWSClient
onLoginResult: {
errorLabel.visible = (error !== AWSClient.LoginErrorNoError)
}
@ -310,14 +310,14 @@ Page {
enabled: usernameTextField.acceptableInput && passwordTextField.isValidPassword
onClicked: {
busyOverlay.shown = true;
engine.awsClient.signup(usernameTextField.text, passwordTextField.password)
AWSClient.signup(usernameTextField.text, passwordTextField.password)
}
}
}
Connections {
target: engine.awsClient
target: AWSClient
onSignupResult: {
busyOverlay.shown = false;
var text;
@ -371,12 +371,12 @@ Page {
text: qsTr("OK")
onClicked: {
busyOverlay.shown = true;
engine.awsClient.confirmRegistration(confirmationCodeTextField.text)
AWSClient.confirmRegistration(confirmationCodeTextField.text)
}
}
Connections {
target: engine.awsClient
target: AWSClient
onConfirmationResult: {
busyOverlay.shown = false;
var text
@ -418,7 +418,7 @@ Page {
}
Connections {
target: engine.awsClient
target: AWSClient
onForgotPasswordResult: {
busyOverlay.shown = false
if (error !== AWSClient.LoginErrorNoError) {
@ -459,7 +459,7 @@ Page {
Layout.fillWidth: true; Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
text: qsTr("Reset password")
onClicked: {
engine.awsClient.forgotPassword(emailTextField.text)
AWSClient.forgotPassword(emailTextField.text)
busyOverlay.shown = true
}
}
@ -478,7 +478,7 @@ Page {
id: confirmResetPasswordPage
Connections {
target: engine.awsClient
target: AWSClient
onConfirmForgotPasswordResult: {
busyOverlay.shown = false
if (error !== AWSClient.LoginErrorNoError) {
@ -545,7 +545,7 @@ Page {
enabled: passwordTextField.isValidPassword && codeTextField.text.length > 0
onClicked: {
busyOverlay.shown = true
engine.awsClient.confirmForgotPassword(confirmResetPasswordPage.email, codeTextField.text, passwordTextField.password)
AWSClient.confirmForgotPassword(confirmResetPasswordPage.email, codeTextField.text, passwordTextField.password)
}
}
BusyOverlay {

View File

@ -11,8 +11,8 @@ Page {
readonly property bool haveHosts: discovery.discoveryModel.count > 0
Component.onCompleted: {
print("completed connectPage. last connected host:", settings.lastConnectedHost)
if (settings.lastConnectedHost.length > 0 && engine.connection.connect(settings.lastConnectedHost)) {
print("completed connectPage for tab", connectionTabIndex, "last connected host:", tabSettings.lastConnectedHost)
if (tabSettings.lastConnectedHost.length > 0 && engine.connection.connect(tabSettings.lastConnectedHost)) {
var page = pageStack.push(Qt.resolvedUrl("ConnectingPage.qml"))
page.cancel.connect(function() {
engine.connection.disconnect();
@ -37,7 +37,7 @@ Page {
NymeaDiscovery {
id: discovery
objectName: "discovery"
awsClient: engine.awsClient
awsClient: AWSClient
discovering: pageStack.currentItem.objectName === "discoveryPage"
}
@ -285,7 +285,7 @@ Page {
Layout.leftMargin: app.margins
Layout.rightMargin: app.margins
text: qsTr("Cloud login")
visible: !engine.awsClient.isLoggedIn
visible: !AWSClient.isLoggedIn
onClicked: pageStack.push(Qt.resolvedUrl("../appsettings/CloudLoginPage.qml"))
}

View File

@ -21,8 +21,8 @@ Page {
print("cloud connection state changed", engine.jsonRpcClient.cloudConnectionState)
if (engine.jsonRpcClient.cloudConnectionState === JsonRpcClient.CloudConnectionStateConnected) {
d.deploymentStarted = false;
if (engine.awsClient.awsDevices.getDevice(engine.jsonRpcClient.serverUuid) === null) {
engine.jsonRpcClient.setupRemoteAccess(engine.awsClient.idToken, engine.awsClient.userId)
if (AWSClient.awsDevices.getDevice(engine.jsonRpcClient.serverUuid) === null) {
engine.jsonRpcClient.setupRemoteAccess(AWSClient.idToken, AWSClient.userId)
}
}
}
@ -43,7 +43,7 @@ Page {
// Button {
// text: "pair"
// onClicked: engine.jsonRpcClient.setupRemoteAccess(engine.awsClient.idToken, engine.awsClient.userId)
// onClicked: engine.jsonRpcClient.setupRemoteAccess(AWSClient.idToken, AWSClient.userId)
// }
SwitchDelegate {
@ -111,9 +111,9 @@ Page {
Layout.fillWidth: true
Layout.leftMargin: app.margins; Layout.rightMargin: app.margins
visible: engine.jsonRpcClient.cloudConnectionState === JsonRpcClient.CloudConnectionStateUnconfigured && !d.deploymentStarted
text: engine.awsClient.isLoggedIn ? qsTr("Register box") : qsTr("Log in to cloud")
text: AWSClient.isLoggedIn ? qsTr("Register box") : qsTr("Log in to cloud")
onClicked: {
if (engine.awsClient.isLoggedIn) {
if (AWSClient.isLoggedIn) {
d.deploymentStarted = true
engine.deployCertificate();
} else {
@ -121,16 +121,5 @@ Page {
}
}
}
// Label {
// Layout.fillWidth: true
// Layout.leftMargin: app.margins
// Layout.rightMargin: app.margins
// visible: engine.basicConfiguration.cloudEnabled && engine.awsClient.isLoggedIn
// text: engine.awsClient.awsDevices.getDevice(engine.jsonRpcClient.serverUuid) !== null ?
// qsTr("This box is connected to a nymea:cloud.") :
// qsTr("Connecting to nymea:cloud...")
// }
}
}