Prusa Account communication changes after testing all possible error states.

This commit is contained in:
David Kocik 2024-01-24 17:23:36 +01:00
parent 5804d5aa71
commit c129340712
7 changed files with 217 additions and 281 deletions

View File

@ -146,12 +146,11 @@ PrusaAuthCommunication::PrusaAuthCommunication(wxEvtHandler* evt_handler, AppCon
refresh_token = m_app_config->get("refresh_token");
shared_session_key = m_app_config->get("shared_session_key");
}
if (!access_token.empty() || !refresh_token.empty())
m_remember_session = true;
bool has_token = !access_token.empty() && !refresh_token.empty();
m_session = std::make_unique<AuthSession>(evt_handler, access_token, refresh_token, shared_session_key, m_app_config->get_bool("connect_polling"));
init_session_thread();
// perform login at the start
if (m_remember_session)
// perform login at the start, but only with tokens
if (has_token)
do_login();
}
@ -234,8 +233,9 @@ void PrusaAuthCommunication::do_login()
std::lock_guard<std::mutex> lock(m_session_mutex);
if (!m_session->is_initialized()) {
login_redirect();
} else {
m_session->enqueue_test_with_refresh();
}
m_session->enqueue_action(UserActionID::LOGIN_USER_ID, nullptr, nullptr, {});
}
wakeup_session_thread();
}
@ -289,7 +289,7 @@ void PrusaAuthCommunication::enqueue_connect_dummy_action()
}
wakeup_session_thread();
}
#endif
#endif 0
void PrusaAuthCommunication::enqueue_connect_printers_action()
{
@ -299,7 +299,19 @@ void PrusaAuthCommunication::enqueue_connect_printers_action()
BOOST_LOG_TRIVIAL(error) << "Connect Printers endpoint connection failed - Not Logged in.";
return;
}
m_session->enqueue_action(UserActionID::CONNECT_PRINTERS, nullptr, nullptr, {});
m_session->enqueue_action(UserActionID::AUTH_ACTION_CONNECT_PRINTERS, nullptr, nullptr, {});
}
wakeup_session_thread();
}
void PrusaAuthCommunication::enqueue_test_connection()
{
{
std::lock_guard<std::mutex> lock(m_session_mutex);
if (!m_session->is_initialized()) {
BOOST_LOG_TRIVIAL(error) << "Connect Printers endpoint connection failed - Not Logged in.";
return;
}
m_session->enqueue_action(UserActionID::AUTH_ACTION_TEST_CONNECTION, nullptr, nullptr, {});
}
wakeup_session_thread();
}
@ -312,7 +324,7 @@ void PrusaAuthCommunication::enqueue_avatar_action(const std::string url)
BOOST_LOG_TRIVIAL(error) << "Connect Printers endpoint connection failed - Not Logged in.";
return;
}
m_session->enqueue_action(UserActionID::AVATAR, nullptr, nullptr, url);
m_session->enqueue_action(UserActionID::AUTH_ACTION_AVATAR, nullptr, nullptr, url);
}
wakeup_session_thread();
}

View File

@ -44,6 +44,7 @@ public:
#endif
void enqueue_connect_printers_action();
void enqueue_avatar_action(const std::string url);
void enqueue_test_connection();
// Callbacks - called from UI after receiving Event from Session thread. Some might use Session thread.
//
@ -73,7 +74,7 @@ private:
AppConfig* m_app_config;
// if not empty - user is logged in
std::string m_username;
bool m_remember_session {true};
bool m_remember_session { true }; // if default is true, on every login Remember me will be checked.
void wakeup_session_thread();
void init_session_thread();

View File

@ -26,75 +26,47 @@ wxDEFINE_EVENT(EVT_PRUSAAUTH_SUCCESS, PrusaAuthSuccessEvent);
wxDEFINE_EVENT(EVT_PRUSACONNECT_PRINTERS_SUCCESS, PrusaAuthSuccessEvent);
wxDEFINE_EVENT(EVT_PA_AVATAR_SUCCESS, PrusaAuthSuccessEvent);
wxDEFINE_EVENT(EVT_PRUSAAUTH_FAIL, PrusaAuthFailEvent);
wxDEFINE_EVENT(EVT_PA_AVATAR_FAIL, PrusaAuthFailEvent);
wxDEFINE_EVENT(EVT_PRUSAAUTH_RESET, PrusaAuthFailEvent);
void UserActionPost::perform( /*UNUSED*/ const std::string& access_token, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, const std::string& input)
void UserActionPost::perform(/*UNUSED*/ wxEvtHandler* evt_handler, /*UNUSED*/ const std::string& access_token, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, const std::string& input)
{
std::string url = m_url;
//BOOST_LOG_TRIVIAL(info) << m_action_name <<" POST " << url << " body: " << input;
auto http = Http::post(std::move(url));
if (!input.empty())
http.set_post_body(input);
http.header("Content-type", "application/x-www-form-urlencoded");
http.on_error([&](std::string body, std::string error, unsigned status) {
//BOOST_LOG_TRIVIAL(error) << m_action_name << " action failed. status: " << status << " Body: " << body;
http.on_error([fail_callback](std::string body, std::string error, unsigned status) {
if (fail_callback)
fail_callback(body);
});
http.on_complete([&, this](std::string body, unsigned status) {
//BOOST_LOG_TRIVIAL(info) << m_action_name << "action success. Status: " << status << " Body: " << body;
http.on_complete([success_callback](std::string body, unsigned status) {
if (success_callback)
success_callback(body);
});
http.perform_sync();
}
void UserActionGetWithEvent::perform(const std::string& access_token, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, /*UNUSED*/ const std::string& input)
{
std::string url = m_url;
//BOOST_LOG_TRIVIAL(info) << m_action_name << " GET " << url;
auto http = Http::get(url);
http.header("Authorization", "Bearer " + access_token);
http.on_error([&](std::string body, std::string error, unsigned status) {
//BOOST_LOG_TRIVIAL(error) << m_action_name << " action failed. status: " << status << " Body: " << body;
if (fail_callback)
fail_callback(body);
std::string message = GUI::format("%1% action failed (%2%): %3%", m_action_name, std::to_string(status), body);
if (m_succ_evt_type != wxEVT_NULL)
wxQueueEvent(m_evt_handler, new PrusaAuthFailEvent(m_fail_evt_type, std::move(message)));
});
http.on_complete([&, this](std::string body, unsigned status) {
//BOOST_LOG_TRIVIAL(info) << m_action_name << " action success. Status: " << status << " Body: " << body;
if (success_callback)
success_callback(body);
if (m_succ_evt_type != wxEVT_NULL)
wxQueueEvent(m_evt_handler, new PrusaAuthSuccessEvent(m_succ_evt_type, body));
});
http.perform_sync();
}
void UserActionNoAuthGetWithEvent::perform(/*UNUSED*/ const std::string& access_token, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, const std::string& input)
void UserActionGetWithEvent::perform(wxEvtHandler* evt_handler, const std::string& access_token, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, const std::string& input)
{
std::string url = m_url + input;
//BOOST_LOG_TRIVIAL(info) << m_action_name << " GET " << url;
auto http = Http::get(url);
http.on_error([&](std::string body, std::string error, unsigned status) {
//BOOST_LOG_TRIVIAL(error) << m_action_name << " action failed. status: " << status << " Body: " << body;
auto http = Http::get(std::move(url));
if (!access_token.empty())
http.header("Authorization", "Bearer " + access_token);
http.on_error([evt_handler, fail_callback, action_name = &m_action_name, fail_evt_type = m_fail_evt_type](std::string body, std::string error, unsigned status) {
if (fail_callback)
fail_callback(body);
std::string message = GUI::format("%1% action failed (%2%): %3%", m_action_name, std::to_string(status), body);
if (m_succ_evt_type != wxEVT_NULL)
wxQueueEvent(m_evt_handler, new PrusaAuthFailEvent(m_fail_evt_type, std::move(message)));
});
http.on_complete([&, this](std::string body, unsigned status) {
//BOOST_LOG_TRIVIAL(info) << m_action_name << " action success. Status: " << status << " Body: " << body;
std::string message = GUI::format("%1% action failed (%2%): %3%", action_name, std::to_string(status), body);
if (fail_evt_type != wxEVT_NULL)
wxQueueEvent(evt_handler, new PrusaAuthFailEvent(fail_evt_type, std::move(message)));
});
http.on_complete([evt_handler, success_callback, succ_evt_type = m_succ_evt_type](std::string body, unsigned status) {
if (success_callback)
success_callback(body);
if (m_succ_evt_type != wxEVT_NULL)
wxQueueEvent(m_evt_handler, new PrusaAuthSuccessEvent(m_succ_evt_type, body));
});
if (succ_evt_type != wxEVT_NULL)
wxQueueEvent(evt_handler, new PrusaAuthSuccessEvent(succ_evt_type, body));
});
http.perform_sync();
}
@ -103,47 +75,36 @@ void AuthSession::process_action_queue()
{
if (!m_proccessing_enabled)
return;
//BOOST_LOG_TRIVIAL(debug) << "process_action_queue start";
if (m_priority_action_queue.empty() && m_action_queue.empty()) {
//BOOST_LOG_TRIVIAL(debug) << "process_action_queue queues empty";
// update printers on every periodic wakeup call
if (m_polling_enabled)
enqueue_action(UserActionID::CONNECT_PRINTERS, nullptr, nullptr, {});
enqueue_action(UserActionID::AUTH_ACTION_CONNECT_PRINTERS, nullptr, nullptr, {});
else
return;
}
if (this->is_initialized()) {
// if priority queue already has some action f.e. to exchange tokens, the test should not be neccessary but also shouldn't be problem
enqueue_test_with_refresh();
}
// priority queue works even when tokens are empty or broken
while (!m_priority_action_queue.empty()) {
m_actions[m_priority_action_queue.front().action_id]->perform(m_access_token, m_priority_action_queue.front().success_callback, m_priority_action_queue.front().fail_callback, m_priority_action_queue.front().input);
m_actions[m_priority_action_queue.front().action_id]->perform(p_evt_handler, m_access_token, m_priority_action_queue.front().success_callback, m_priority_action_queue.front().fail_callback, m_priority_action_queue.front().input);
if (!m_priority_action_queue.empty())
m_priority_action_queue.pop();
}
if (!this->is_initialized()) {
//BOOST_LOG_TRIVIAL(debug) << "process_action_queue not initialized";
// regular queue has to wait until priority fills tokens
if (!this->is_initialized())
return;
}
while (!m_action_queue.empty()) {
m_actions[m_action_queue.front().action_id]->perform(m_access_token, m_action_queue.front().success_callback, m_action_queue.front().fail_callback, m_action_queue.front().input);
m_actions[m_action_queue.front().action_id]->perform(p_evt_handler, m_access_token, m_action_queue.front().success_callback, m_action_queue.front().fail_callback, m_action_queue.front().input);
if (!m_action_queue.empty())
m_action_queue.pop();
}
//BOOST_LOG_TRIVIAL(debug) << "process_action_queue end";
}
void AuthSession::enqueue_action(UserActionID id, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, const std::string& input)
{
//BOOST_LOG_TRIVIAL(info) << "enqueue_action " << (int)id;
m_proccessing_enabled = true;
m_action_queue.push({ id, success_callback, fail_callback, input });
}
void AuthSession::init_with_code(const std::string& code, const std::string& code_verifier)
{
// Data we have
@ -154,115 +115,99 @@ void AuthSession::init_with_code(const std::string& code, const std::string& cod
"&redirect_uri=" + REDIRECT_URI +
"&code_verifier="+ code_verifier;
auto succ_fn = [&](const std::string& body){
// Data we need
std::string access_token, refresh_token, shared_session_key;
try {
std::stringstream ss(body);
pt::ptree ptree;
pt::read_json(ss, ptree);
const auto access_token_optional = ptree.get_optional<std::string>("access_token");
const auto refresh_token_optional = ptree.get_optional<std::string>("refresh_token");
const auto shared_session_key_optional = ptree.get_optional<std::string>("shared_session_key");
if (access_token_optional)
access_token = *access_token_optional;
if (refresh_token_optional)
refresh_token = *refresh_token_optional;
if (shared_session_key_optional)
shared_session_key = *shared_session_key_optional;
}
catch (const std::exception&) {
BOOST_LOG_TRIVIAL(error) << "Auth::http_access Could not parse server response.";
}
assert(!access_token.empty() && !refresh_token.empty() && !shared_session_key.empty());
if (access_token.empty() || refresh_token.empty() || shared_session_key.empty()) {
BOOST_LOG_TRIVIAL(error) << "Refreshing token has failed.";
m_access_token = std::string();
m_refresh_token = std::string();
m_shared_session_key = std::string();
return;
}
BOOST_LOG_TRIVIAL(info) << "access_token: " << access_token;
BOOST_LOG_TRIVIAL(info) << "refresh_token: " << refresh_token;
BOOST_LOG_TRIVIAL(info) << "shared_session_key: " << shared_session_key;
m_access_token = access_token;
m_refresh_token = refresh_token;
m_shared_session_key = shared_session_key;
};
m_proccessing_enabled = true;
// fail fn might be cancel_queue here
m_priority_action_queue.push({ UserActionID::CODE_FOR_TOKEN, succ_fn, std::bind(&AuthSession::enqueue_refresh, this, std::placeholders::_1), post_fields });
m_priority_action_queue.push({ UserActionID::AUTH_ACTION_CODE_FOR_TOKEN
, std::bind(&AuthSession::token_success_callback, this, std::placeholders::_1)
, std::bind(&AuthSession::code_exchange_fail_callback, this, std::placeholders::_1)
, post_fields });
}
void AuthSession::token_success_callback(const std::string& body)
{
// Data we need
std::string access_token, refresh_token, shared_session_key;
try {
std::stringstream ss(body);
pt::ptree ptree;
pt::read_json(ss, ptree);
const auto access_token_optional = ptree.get_optional<std::string>("access_token");
const auto refresh_token_optional = ptree.get_optional<std::string>("refresh_token");
const auto shared_session_key_optional = ptree.get_optional<std::string>("shared_session_key");
if (access_token_optional)
access_token = *access_token_optional;
if (refresh_token_optional)
refresh_token = *refresh_token_optional;
if (shared_session_key_optional)
shared_session_key = *shared_session_key_optional;
}
catch (const std::exception&) {
std::string msg = "Could not parse server response after code exchange.";
wxQueueEvent(p_evt_handler, new PrusaAuthFailEvent(EVT_PRUSAAUTH_RESET, std::move(msg)));
return;
}
if (access_token.empty() || refresh_token.empty() || shared_session_key.empty()) {
// just debug msg, no need to translate
std::string msg = GUI::format("Failed read tokens after POST.\nAccess token: %1%\nRefresh token: %2%\nShared session token: %3%\nbody: %4%", access_token, refresh_token, shared_session_key, body);
m_access_token = std::string();
m_refresh_token = std::string();
m_shared_session_key = std::string();
wxQueueEvent(p_evt_handler, new PrusaAuthFailEvent(EVT_PRUSAAUTH_RESET, std::move(msg)));
return;
}
BOOST_LOG_TRIVIAL(info) << "access_token: " << access_token;
BOOST_LOG_TRIVIAL(info) << "refresh_token: " << refresh_token;
BOOST_LOG_TRIVIAL(info) << "shared_session_key: " << shared_session_key;
m_access_token = access_token;
m_refresh_token = refresh_token;
m_shared_session_key = shared_session_key;
enqueue_action(UserActionID::AUTH_ACTION_USER_ID, nullptr, nullptr, {});
}
void AuthSession::code_exchange_fail_callback(const std::string& body)
{
clear();
cancel_queue();
// Unlike refresh_fail_callback, no event was triggered so far, do it. (AUTH_ACTION_CODE_FOR_TOKEN does not send events)
wxQueueEvent(p_evt_handler, new PrusaAuthFailEvent(EVT_PRUSAAUTH_RESET, std::move(body)));
}
void AuthSession::enqueue_test_with_refresh()
{
// on test fail - try refresh
m_priority_action_queue.push({ UserActionID::TEST_CONNECTION, nullptr, std::bind(&AuthSession::enqueue_refresh, this, std::placeholders::_1), {} });
m_proccessing_enabled = true;
m_priority_action_queue.push({ UserActionID::AUTH_ACTION_TEST_ACCESS_TOKEN, nullptr, std::bind(&AuthSession::enqueue_refresh, this, std::placeholders::_1), {} });
}
void AuthSession::enqueue_refresh(const std::string& body)
{
assert(!m_refresh_token.empty());
std::string post_fields = "grant_type=refresh_token"
"&client_id=" + client_id() +
"&refresh_token=" + m_refresh_token;
auto succ_callback = [&](const std::string& body){
std::string new_access_token;
std::string new_refresh_token;
std::string new_shared_session_key;
try {
std::stringstream ss(body);
pt::ptree ptree;
pt::read_json(ss, ptree);
const auto access_token_optional = ptree.get_optional<std::string>("access_token");
const auto refresh_token_optional = ptree.get_optional<std::string>("refresh_token");
const auto shared_session_key_optional = ptree.get_optional<std::string>("shared_session_key");
if (access_token_optional)
new_access_token = *access_token_optional;
if (refresh_token_optional)
new_refresh_token = *refresh_token_optional;
if (shared_session_key_optional)
new_shared_session_key = *shared_session_key_optional;
}
catch (const std::exception&) {
BOOST_LOG_TRIVIAL(error) << "Could not parse server response.";
}
assert(!new_access_token.empty() && !new_refresh_token.empty() && !new_shared_session_key.empty());
if (new_access_token.empty() || new_refresh_token.empty() || new_shared_session_key.empty()) {
BOOST_LOG_TRIVIAL(error) << "Refreshing token has failed.";
m_access_token = std::string();
m_refresh_token = std::string();
m_shared_session_key = std::string();
// TODO: cancel following queue
}
BOOST_LOG_TRIVIAL(info) << "access_token: " << new_access_token;
BOOST_LOG_TRIVIAL(info) << "refresh_token: " << new_refresh_token;
BOOST_LOG_TRIVIAL(info) << "shared_session_key: " << new_shared_session_key;
m_access_token = new_access_token;
m_refresh_token = new_refresh_token;
m_shared_session_key = new_shared_session_key;
m_priority_action_queue.push({ UserActionID::TEST_CONNECTION, nullptr, std::bind(&AuthSession::refresh_failed_callback, this, std::placeholders::_1), {} });
};
m_priority_action_queue.push({ UserActionID::REFRESH_TOKEN, succ_callback, std::bind(&AuthSession::refresh_failed_callback, this, std::placeholders::_1), post_fields });
m_priority_action_queue.push({ UserActionID::AUTH_ACTION_REFRESH_TOKEN
, std::bind(&AuthSession::token_success_callback, this, std::placeholders::_1)
, std::bind(&AuthSession::refresh_fail_callback, this, std::placeholders::_1)
, post_fields });
}
void AuthSession::refresh_failed_callback(const std::string& body)
void AuthSession::refresh_fail_callback(const std::string& body)
{
clear();
cancel_queue();
// No need to notify UI thread here
// backtrace: load tokens -> TEST_TOKEN fail (access token bad) -> REFRESH_TOKEN fail (refresh token bad)
// AUTH_ACTION_TEST_ACCESS_TOKEN triggers EVT_PRUSAAUTH_FAIL, we need also RESET
wxQueueEvent(p_evt_handler, new PrusaAuthFailEvent(EVT_PRUSAAUTH_RESET, std::move(body)));
}
void AuthSession::cancel_queue()
{
while (!m_priority_action_queue.empty()) {

View File

@ -23,29 +23,31 @@ wxDECLARE_EVENT(EVT_PA_ID_USER_FAIL, PrusaAuthFailEvent);
wxDECLARE_EVENT(EVT_PRUSAAUTH_SUCCESS, PrusaAuthSuccessEvent);
wxDECLARE_EVENT(EVT_PRUSACONNECT_PRINTERS_SUCCESS, PrusaAuthSuccessEvent);
wxDECLARE_EVENT(EVT_PA_AVATAR_SUCCESS, PrusaAuthSuccessEvent);
wxDECLARE_EVENT(EVT_PRUSAAUTH_FAIL, PrusaAuthFailEvent);
wxDECLARE_EVENT(EVT_PRUSAAUTH_RESET, PrusaAuthFailEvent);
wxDECLARE_EVENT(EVT_PRUSAAUTH_FAIL, PrusaAuthFailEvent); // Soft fail - clears only after some number of fails
wxDECLARE_EVENT(EVT_PA_AVATAR_FAIL, PrusaAuthFailEvent); // Soft fail - clears only after some number of fails
wxDECLARE_EVENT(EVT_PRUSAAUTH_RESET, PrusaAuthFailEvent); // Hard fail - clears all
typedef std::function<void(const std::string& body)> UserActionSuccessFn;
typedef std::function<void(const std::string& body)> UserActionFailFn;
// UserActions implements different operations via trigger() method. Stored in m_actions.
enum class UserActionID {
DUMMY_ACTION,
REFRESH_TOKEN,
CODE_FOR_TOKEN,
TEST_CONNECTION,
LOGIN_USER_ID,
CONNECT_DUMMY,
CONNECT_PRINTERS,
AVATAR,
AUTH_ACTION_DUMMY,
AUTH_ACTION_REFRESH_TOKEN,
AUTH_ACTION_CODE_FOR_TOKEN,
AUTH_ACTION_USER_ID,
AUTH_ACTION_TEST_ACCESS_TOKEN,
AUTH_ACTION_TEST_CONNECTION,
AUTH_ACTION_CONNECT_DUMMY,
AUTH_ACTION_CONNECT_PRINTERS,
AUTH_ACTION_AVATAR,
};
class UserAction
{
public:
UserAction(const std::string name, const std::string url) : m_action_name(name), m_url(url){}
~UserAction() {}
virtual void perform(const std::string& access_token, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, const std::string& input) = 0;
virtual void perform(wxEvtHandler* evt_handler, const std::string& access_token, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, const std::string& input) = 0;
protected:
std::string m_action_name;
std::string m_url;
@ -54,35 +56,16 @@ protected:
class UserActionGetWithEvent : public UserAction
{
public:
UserActionGetWithEvent(const std::string name, const std::string url, wxEvtHandler* evt_handler, wxEventType succ_event_type, wxEventType fail_event_type)
UserActionGetWithEvent(const std::string name, const std::string url, wxEventType succ_event_type, wxEventType fail_event_type)
: m_succ_evt_type(succ_event_type)
, m_fail_evt_type(fail_event_type)
, m_evt_handler(evt_handler)
, UserAction(name, url)
{}
~UserActionGetWithEvent() {}
void perform(const std::string& access_token, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, const std::string& input) override;
void perform(wxEvtHandler* evt_handler, const std::string& access_token, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, const std::string& input) override;
private:
wxEventType m_succ_evt_type;
wxEventType m_fail_evt_type;
wxEvtHandler* m_evt_handler;
};
class UserActionNoAuthGetWithEvent : public UserAction
{
public:
UserActionNoAuthGetWithEvent(const std::string name, const std::string url, wxEvtHandler* evt_handler, wxEventType succ_event_type, wxEventType fail_event_type)
: m_succ_evt_type(succ_event_type)
, m_fail_evt_type(fail_event_type)
, m_evt_handler(evt_handler)
, UserAction(name, url)
{}
~UserActionNoAuthGetWithEvent() {}
void perform(const std::string& access_token, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, const std::string& input) override;
private:
wxEventType m_succ_evt_type;
wxEventType m_fail_evt_type;
wxEvtHandler* m_evt_handler;
};
class UserActionPost : public UserAction
@ -90,7 +73,7 @@ class UserActionPost : public UserAction
public:
UserActionPost(const std::string name, const std::string url) : UserAction(name, url) {}
~UserActionPost() {}
void perform(const std::string& access_token, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, const std::string& input) override;
void perform(wxEvtHandler* evt_handler, const std::string& access_token, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, const std::string& input) override;
};
class DummyUserAction : public UserAction
@ -98,7 +81,7 @@ class DummyUserAction : public UserAction
public:
DummyUserAction() : UserAction("Dummy", {}) {}
~DummyUserAction() {}
void perform(const std::string& access_token, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, const std::string& input) override { }
void perform(wxEvtHandler* evt_handler, const std::string& access_token, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, const std::string& input) override { }
};
struct ActionQueueData
@ -113,31 +96,36 @@ class AuthSession
{
public:
AuthSession(wxEvtHandler* evt_handler, const std::string& access_token, const std::string& refresh_token, const std::string& shared_session_key, bool polling_enabled)
: m_access_token(access_token)
: p_evt_handler(evt_handler)
, m_access_token(access_token)
, m_refresh_token(refresh_token)
, m_shared_session_key(shared_session_key)
, m_polling_enabled(polling_enabled)
{
// do not forget to add delete to destructor
m_actions[UserActionID::DUMMY_ACTION] = std::make_unique<DummyUserAction>();
m_actions[UserActionID::REFRESH_TOKEN] = std::make_unique<UserActionPost>("EXCHANGE_TOKENS", "https://test-account.prusa3d.com/o/token/");
m_actions[UserActionID::CODE_FOR_TOKEN] = std::make_unique<UserActionPost>("EXCHANGE_TOKENS", "https://test-account.prusa3d.com/o/token/");
m_actions[UserActionID::TEST_CONNECTION] = std::make_unique<UserActionGetWithEvent>("TEST_CONNECTION", "https://test-account.prusa3d.com/api/v1/me/", evt_handler, wxEVT_NULL, EVT_PRUSAAUTH_FAIL);
m_actions[UserActionID::LOGIN_USER_ID] = std::make_unique<UserActionGetWithEvent>("USER_ID", "https://test-account.prusa3d.com/api/v1/me/", evt_handler, EVT_PA_ID_USER_SUCCESS, EVT_PRUSAAUTH_FAIL);
m_actions[UserActionID::CONNECT_DUMMY] = std::make_unique<UserActionGetWithEvent>("CONNECT_DUMMY", "https://dev.connect.prusa3d.com/slicer/dummy"/*"dev.connect.prusa:8000/slicer/dummy"*/, evt_handler, EVT_PRUSAAUTH_SUCCESS, EVT_PRUSAAUTH_FAIL);
m_actions[UserActionID::CONNECT_PRINTERS] = std::make_unique<UserActionGetWithEvent>("CONNECT_PRINTERS", "https://dev.connect.prusa3d.com/slicer/printers"/*"dev.connect.prusa:8000/slicer/printers"*/, evt_handler, EVT_PRUSACONNECT_PRINTERS_SUCCESS, EVT_PRUSAAUTH_FAIL);
m_actions[UserActionID::AVATAR] = std::make_unique<UserActionNoAuthGetWithEvent>("AVATAR", "https://test-media.printables.com/media/", evt_handler, EVT_PA_AVATAR_SUCCESS, EVT_PRUSAAUTH_FAIL);
m_actions[UserActionID::AUTH_ACTION_DUMMY] = std::make_unique<DummyUserAction>();
m_actions[UserActionID::AUTH_ACTION_REFRESH_TOKEN] = std::make_unique<UserActionPost>("EXCHANGE_TOKENS", "https://test-account.prusa3d.com/o/token/");
m_actions[UserActionID::AUTH_ACTION_CODE_FOR_TOKEN] = std::make_unique<UserActionPost>("EXCHANGE_TOKENS", "https://test-account.prusa3d.com/o/token/");
m_actions[UserActionID::AUTH_ACTION_USER_ID] = std::make_unique<UserActionGetWithEvent>("USER_ID", "https://test-account.prusa3d.com/api/v1/me/", EVT_PA_ID_USER_SUCCESS, EVT_PRUSAAUTH_RESET);
m_actions[UserActionID::AUTH_ACTION_TEST_ACCESS_TOKEN] = std::make_unique<UserActionGetWithEvent>("TEST_ACCESS_TOKEN", "https://test-account.prusa3d.com/api/v1/me/", EVT_PA_ID_USER_SUCCESS, EVT_PRUSAAUTH_FAIL);
m_actions[UserActionID::AUTH_ACTION_TEST_CONNECTION] = std::make_unique<UserActionGetWithEvent>("TEST_CONNECTION", "https://test-account.prusa3d.com/api/v1/me/", wxEVT_NULL, EVT_PRUSAAUTH_RESET);
m_actions[UserActionID::AUTH_ACTION_CONNECT_DUMMY] = std::make_unique<UserActionGetWithEvent>("CONNECT_DUMMY", "https://dev.connect.prusa3d.com/slicer/dummy"/*"dev.connect.prusa:8000/slicer/dummy"*/, EVT_PRUSAAUTH_SUCCESS, EVT_PRUSAAUTH_FAIL);
m_actions[UserActionID::AUTH_ACTION_CONNECT_PRINTERS] = std::make_unique<UserActionGetWithEvent>("CONNECT_PRINTERS", "https://dev.connect.prusa3d.com/slicer/printers"/*"dev.connect.prusa:8000/slicer/printers"*/, EVT_PRUSACONNECT_PRINTERS_SUCCESS, EVT_PRUSAAUTH_FAIL);
m_actions[UserActionID::AUTH_ACTION_AVATAR] = std::make_unique<UserActionGetWithEvent>("AVATAR", "https://test-media.printables.com/media/", EVT_PA_AVATAR_SUCCESS, EVT_PA_AVATAR_FAIL);
}
~AuthSession()
{
m_actions[UserActionID::DUMMY_ACTION].reset(nullptr);
m_actions[UserActionID::REFRESH_TOKEN].reset(nullptr);
m_actions[UserActionID::CODE_FOR_TOKEN].reset(nullptr);
m_actions[UserActionID::TEST_CONNECTION].reset(nullptr);
m_actions[UserActionID::LOGIN_USER_ID].reset(nullptr);
m_actions[UserActionID::CONNECT_DUMMY].reset(nullptr);
m_actions[UserActionID::CONNECT_PRINTERS].reset(nullptr);
m_actions[UserActionID::AVATAR].reset(nullptr);
m_actions[UserActionID::AUTH_ACTION_DUMMY].reset(nullptr);
m_actions[UserActionID::AUTH_ACTION_REFRESH_TOKEN].reset(nullptr);
m_actions[UserActionID::AUTH_ACTION_CODE_FOR_TOKEN].reset(nullptr);
m_actions[UserActionID::AUTH_ACTION_USER_ID].reset(nullptr);
m_actions[UserActionID::AUTH_ACTION_TEST_ACCESS_TOKEN].reset(nullptr);
m_actions[UserActionID::AUTH_ACTION_TEST_CONNECTION].reset(nullptr);
m_actions[UserActionID::AUTH_ACTION_CONNECT_DUMMY].reset(nullptr);
m_actions[UserActionID::AUTH_ACTION_CONNECT_PRINTERS].reset(nullptr);
m_actions[UserActionID::AUTH_ACTION_AVATAR].reset(nullptr);
//assert(m_actions.empty());
}
void clear() {
@ -150,6 +138,8 @@ public:
// Functions that automatically enable action queu processing
void init_with_code(const std::string& code, const std::string& code_verifier);
void enqueue_action(UserActionID id, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, const std::string& input);
// Special enques, that sets callbacks.
void enqueue_test_with_refresh();
void process_action_queue();
bool is_initialized() { return !m_access_token.empty() || !m_refresh_token.empty(); }
@ -159,10 +149,12 @@ public:
void set_polling_enabled(bool enabled) {m_polling_enabled = enabled; }
private:
void enqueue_test_with_refresh();
void enqueue_refresh(const std::string& body);
void refresh_failed_callback(const std::string& body);
void refresh_fail_callback(const std::string& body);
void cancel_queue();
void code_exchange_fail_callback(const std::string& body);
void token_success_callback(const std::string& body);
std::string client_id() const { return "UfTRUm5QjWwaQEGpWQBHGHO3reAyuzgOdBaiqO52"; }
// false prevents action queu to be processed - no communication is done
@ -178,6 +170,8 @@ private:
std::queue<ActionQueueData> m_action_queue;
std::queue<ActionQueueData> m_priority_action_queue;
std::map<UserActionID, std::unique_ptr<UserAction>> m_actions;
wxEvtHandler* p_evt_handler;
};
}

View File

@ -884,42 +884,52 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
});
this->q->Bind(EVT_LOGGEDOUT_PRUSAAUTH, [this](PrusaAuthSuccessEvent& evt) {
user_account->on_logout(wxGetApp().app_config);
this->main_frame->disable_connect_tab();
std::string text = _u8L("Logged out.");
user_account->clear();
std::string text = _u8L("Logged out from Prusa Account.");
this->notification_manager->close_notification_of_type(NotificationType::PrusaAuthUserID);
this->notification_manager->push_notification(NotificationType::PrusaAuthUserID, NotificationManager::NotificationLevel::ImportantNotificationLevel, text);
this->main_frame->disable_connect_tab();
this->main_frame->refresh_auth_menu();
});
this->q->Bind(EVT_PA_ID_USER_SUCCESS, [this](PrusaAuthSuccessEvent& evt) {
std::string username;
bool succ = user_account->on_user_id_success(evt.data, username);
if (succ) {
// show connect tab
this->main_frame->enable_connect_tab();
if (user_account->on_user_id_success(evt.data, username)) {
// login notification
std::string text = format(_u8L("Logged as %1%."), username);
std::string text = format(_u8L("Logged to Prusa Account as %1%."), username);
this->notification_manager->close_notification_of_type(NotificationType::PrusaAuthUserID);
this->notification_manager->push_notification(NotificationType::PrusaAuthUserID, NotificationManager::NotificationLevel::ImportantNotificationLevel, text);
// show connect tab
this->main_frame->enable_connect_tab();
// Update User name in TopBar
this->main_frame->refresh_auth_menu();
} else {
// TODO
// data were corrupt and username was not retrieved
// procced as if EVT_PRUSAAUTH_RESET was recieved
BOOST_LOG_TRIVIAL(error) << "Reseting Prusa Account communication. Recieved data were corrupt.";
user_account->clear();
this->notification_manager->close_notification_of_type(NotificationType::PrusaAuthUserID);
this->notification_manager->push_notification(NotificationType::PrusaAuthUserID, NotificationManager::NotificationLevel::WarningNotificationLevel, _u8L("Failed to connect to Prusa Account."));
// Update User name in TopBar
this->main_frame->refresh_auth_menu();
// Update sidebar printer status
sidebar->update_printer_presets_combobox();
}
});
this->q->Bind(EVT_PRUSAAUTH_RESET, [this](PrusaAuthFailEvent& evt) {
BOOST_LOG_TRIVIAL(error) << "Network error message: " << evt.data;
user_account->on_communication_fail(evt.data, wxGetApp().app_config);
BOOST_LOG_TRIVIAL(error) << "Reseting Prusa Account communication. Error message: " << evt.data;
user_account->clear();
this->notification_manager->close_notification_of_type(NotificationType::PrusaAuthUserID);
this->notification_manager->push_notification(NotificationType::PrusaAuthUserID, NotificationManager::NotificationLevel::WarningNotificationLevel, _u8L("Connection to PrusaConnect has failed."));
this->notification_manager->push_notification(NotificationType::PrusaAuthUserID, NotificationManager::NotificationLevel::WarningNotificationLevel, _u8L("Failed to connect to Prusa Account."));
// Update User name in TopBar
this->main_frame->refresh_auth_menu();
// Update sidebar printer status
sidebar->update_printer_presets_combobox();
});
this->q->Bind(EVT_PRUSAAUTH_FAIL, [this](PrusaAuthFailEvent& evt) {
BOOST_LOG_TRIVIAL(error) << "Network error message: " << evt.data;
user_account->on_communication_fail(evt.data, wxGetApp().app_config);
this->notification_manager->close_notification_of_type(NotificationType::PrusaAuthUserID);
this->notification_manager->push_notification(NotificationType::PrusaAuthUserID, NotificationManager::NotificationLevel::WarningNotificationLevel, _u8L("Connection to PrusaConnect has failed."));
BOOST_LOG_TRIVIAL(error) << "Failed communication with Prusa Account: " << evt.data;
user_account->on_communication_fail();
});
this->q->Bind(EVT_PRUSAAUTH_SUCCESS, [this](PrusaAuthSuccessEvent& evt) {
this->notification_manager->close_notification_of_type(NotificationType::PrusaAuthUserID);
@ -928,19 +938,13 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
this->q->Bind(EVT_PRUSACONNECT_PRINTERS_SUCCESS, [this](PrusaAuthSuccessEvent& evt) {
std::string text;
bool printers_changed = false;
bool succ = user_account->on_connect_printers_success(evt.data, wxGetApp().app_config, printers_changed, text);
if (succ) {
if (printers_changed)
{
//std::string out = GUI::format("Printers in your PrusaConnect team %1%:\n%2%", (printers_changed ? "changed" : "didn't changed"), text);
//std::string out = GUI::format("Printers in your PrusaConnect team:\n%1%", text);
//this->notification_manager->close_notification_of_type(NotificationType::PrusaConnectPrinters);
//this->notification_manager->push_notification(NotificationType::PrusaConnectPrinters, NotificationManager::NotificationLevel::ImportantNotificationLevel, out);
if (user_account->on_connect_printers_success(evt.data, wxGetApp().app_config, printers_changed)) {
if (printers_changed) {
sidebar->update_printer_presets_combobox();
}
} else {
// TODO
// message was corrupt, procceed like EVT_PRUSAAUTH_FAIL
user_account->on_communication_fail();
}
});
this->q->Bind(EVT_PA_AVATAR_SUCCESS, [this](PrusaAuthSuccessEvent& evt) {

View File

@ -25,7 +25,7 @@ void UserAccount::set_username(const std::string& username)
m_auth_communication->set_username(username);
}
void UserAccount::reset()
void UserAccount::clear()
{
m_username = {};
m_user_data.clear();
@ -79,10 +79,9 @@ void UserAccount::enqueue_connect_printers_action()
{
m_auth_communication->enqueue_connect_printers_action();
}
void UserAccount::enqueue_avatar_action(const std::string& url)
void UserAccount::enqueue_avatar_action()
{
m_auth_communication->enqueue_avatar_action(url);
m_auth_communication->enqueue_avatar_action(m_user_data["avatar"]);
}
bool UserAccount::on_login_code_recieved(const std::string& url_message)
@ -111,7 +110,6 @@ bool UserAccount::on_user_id_success(const std::string data, std::string& out_us
}
}
assert(m_user_data.find("public_username") != m_user_data.end());
if (m_user_data.find("public_username") == m_user_data.end()) {
BOOST_LOG_TRIVIAL(error) << "User ID message from PrusaAuth did not contain public_username. Login failed. Message data: " << data;
return false;
@ -121,33 +119,24 @@ bool UserAccount::on_user_id_success(const std::string data, std::string& out_us
out_username = public_username;
// equeue GET with avatar url
if (m_user_data.find("avatar") != m_user_data.end()) {
enqueue_avatar_action(m_user_data["avatar"]);
enqueue_avatar_action();
}
else {
BOOST_LOG_TRIVIAL(warning) << "User ID message from PrusaAcuth did not contain avatar.";
BOOST_LOG_TRIVIAL(error) << "User ID message from PrusaAuth did not contain avatar.";
}
// update printers list
enqueue_connect_printers_action();
return true;
}
bool UserAccount::on_communication_fail(const std::string data, AppConfig* app_config)
void UserAccount::on_communication_fail()
{
// TODO: should we just declare disconnect on every fail?
//set_username({}, app_config);
return true;
}
bool UserAccount::on_communication_reset(const std::string data, AppConfig* app_config)
{
set_username({});
return true;
}
bool UserAccount::on_logout( AppConfig* app_config)
{
set_username({});
return true;
m_fail_counter++;
if (m_fail_counter > 5) // there is no deeper reason why 5
{
m_auth_communication->enqueue_test_connection();
m_fail_counter = 0;
}
}
namespace {
@ -166,7 +155,7 @@ namespace {
}
}
bool UserAccount::on_connect_printers_success(const std::string data, AppConfig* app_config, bool& out_printers_changed, std::string& out_message)
bool UserAccount::on_connect_printers_success(const std::string data, AppConfig* app_config, bool& out_printers_changed)
{
BOOST_LOG_TRIVIAL(debug) << "PrusaConnect printers message: " << data;
pt::ptree ptree;
@ -238,17 +227,6 @@ bool UserAccount::on_connect_printers_success(const std::string data, AppConfig*
}
}
}
std::string out;
for (const auto& it : m_printer_map)
{
out_message += GUI::format("%1%: O%2% I%3% P%4% F%5% \n"
, it.first
, std::to_string(it.second[static_cast<size_t>(ConnectPrinterState::CONNECT_PRINTER_OFFLINE)])
, std::to_string(it.second[static_cast<size_t>(ConnectPrinterState::CONNECT_PRINTER_IDLE)])
, std::to_string(it.second[static_cast<size_t>(ConnectPrinterState::CONNECT_PRINTER_PRINTING)])
, std::to_string(it.second[static_cast<size_t>(ConnectPrinterState::CONNECT_PRINTER_FINISHED)]));
}
return true;
}

View File

@ -42,16 +42,17 @@ public:
void enqueue_connect_dummy_action();
#endif
void enqueue_connect_printers_action();
void enqueue_avatar_action(const std::string& url);
void enqueue_avatar_action();
// Functions called from UI where events emmited from AuthSession are binded
// Returns bool if data were correctly proccessed
bool on_login_code_recieved(const std::string& url_message);
bool on_user_id_success(const std::string data, std::string& out_username);
bool on_communication_fail(const std::string data, AppConfig* app_config);
bool on_communication_reset(const std::string data, AppConfig* app_config);
bool on_logout(AppConfig* app_config);
bool on_connect_printers_success(const std::string data, AppConfig* app_config, bool& out_printers_changed, std::string& out_message);
// Called on EVT_PRUSAAUTH_FAIL, triggers test after several calls
void on_communication_fail();
// Clears all data and connections, called on logout or EVT_PRUSAAUTH_RESET
void clear();
bool on_connect_printers_success(const std::string data, AppConfig* app_config, bool& out_printers_changed);
std::string get_username() const { return m_username; }
std::string get_access_token();
@ -65,13 +66,14 @@ public:
std::string get_apikey_from_json(const std::string& message) const;
private:
void set_username(const std::string& username);
void reset();
std::unique_ptr<Slic3r::GUI::PrusaAuthCommunication> m_auth_communication;
ConnectPrinterStateMap m_printer_map;
std::map<std::string, std::string> m_user_data;
std::string m_username;
size_t m_fail_counter { 0 };
// first string is "printer_type" code from Connect edpoints
const std::map<std::string, std::string> printer_type_and_name_table = {