diff --git a/src/slic3r/GUI/Auth.cpp b/src/slic3r/GUI/Auth.cpp index a3ca21a956..7b2cd54e5b 100644 --- a/src/slic3r/GUI/Auth.cpp +++ b/src/slic3r/GUI/Auth.cpp @@ -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(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 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 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(); } diff --git a/src/slic3r/GUI/Auth.hpp b/src/slic3r/GUI/Auth.hpp index 21a5571a00..b536ea6b4b 100644 --- a/src/slic3r/GUI/Auth.hpp +++ b/src/slic3r/GUI/Auth.hpp @@ -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(); diff --git a/src/slic3r/GUI/AuthSession.cpp b/src/slic3r/GUI/AuthSession.cpp index ad97778449..6f0026038b 100644 --- a/src/slic3r/GUI/AuthSession.cpp +++ b/src/slic3r/GUI/AuthSession.cpp @@ -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("access_token"); - const auto refresh_token_optional = ptree.get_optional("refresh_token"); - const auto shared_session_key_optional = ptree.get_optional("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("access_token"); + const auto refresh_token_optional = ptree.get_optional("refresh_token"); + const auto shared_session_key_optional = ptree.get_optional("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("access_token"); - const auto refresh_token_optional = ptree.get_optional("refresh_token"); - const auto shared_session_key_optional = ptree.get_optional("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()) { diff --git a/src/slic3r/GUI/AuthSession.hpp b/src/slic3r/GUI/AuthSession.hpp index 2995ed5230..4f098511d6 100644 --- a/src/slic3r/GUI/AuthSession.hpp +++ b/src/slic3r/GUI/AuthSession.hpp @@ -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 UserActionSuccessFn; typedef std::function 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(); - m_actions[UserActionID::REFRESH_TOKEN] = std::make_unique("EXCHANGE_TOKENS", "https://test-account.prusa3d.com/o/token/"); - m_actions[UserActionID::CODE_FOR_TOKEN] = std::make_unique("EXCHANGE_TOKENS", "https://test-account.prusa3d.com/o/token/"); - m_actions[UserActionID::TEST_CONNECTION] = std::make_unique("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("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("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("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("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(); + m_actions[UserActionID::AUTH_ACTION_REFRESH_TOKEN] = std::make_unique("EXCHANGE_TOKENS", "https://test-account.prusa3d.com/o/token/"); + m_actions[UserActionID::AUTH_ACTION_CODE_FOR_TOKEN] = std::make_unique("EXCHANGE_TOKENS", "https://test-account.prusa3d.com/o/token/"); + m_actions[UserActionID::AUTH_ACTION_USER_ID] = std::make_unique("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("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("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("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("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("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 m_action_queue; std::queue m_priority_action_queue; std::map> m_actions; + + wxEvtHandler* p_evt_handler; }; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7663cee9df..465fdfb015 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -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) { diff --git a/src/slic3r/GUI/UserAccount.cpp b/src/slic3r/GUI/UserAccount.cpp index 2549ecb65b..8105b25f85 100644 --- a/src/slic3r/GUI/UserAccount.cpp +++ b/src/slic3r/GUI/UserAccount.cpp @@ -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(ConnectPrinterState::CONNECT_PRINTER_OFFLINE)]) - , std::to_string(it.second[static_cast(ConnectPrinterState::CONNECT_PRINTER_IDLE)]) - , std::to_string(it.second[static_cast(ConnectPrinterState::CONNECT_PRINTER_PRINTING)]) - , std::to_string(it.second[static_cast(ConnectPrinterState::CONNECT_PRINTER_FINISHED)])); - } return true; } diff --git a/src/slic3r/GUI/UserAccount.hpp b/src/slic3r/GUI/UserAccount.hpp index 27f73a38f9..69fb47ea89 100644 --- a/src/slic3r/GUI/UserAccount.hpp +++ b/src/slic3r/GUI/UserAccount.hpp @@ -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 m_auth_communication; ConnectPrinterStateMap m_printer_map; std::map 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 printer_type_and_name_table = {