//SPDX-FileCopyrightText: 2023 Yury Gubich //SPDX-License-Identifier: GPL-3.0-or-later #include "magpie.h" #include #include "API/api.h" #include "API/helpers.h" #include "API/codes.h" Models::Magpie::Magpie (QObject* parent): QObject(parent), assets(), address(), state(State::Offline), accessToken(), renewToken(), api(), firstPoll(), pollRequestId(API::none) { firstPoll.setSingleShot(true); firstPoll.setInterval(2000); connect(&firstPoll, &QTimer::timeout, this, &Magpie::onFirstPollTimerSuccess); connect(&assets, &Assets::requestAssets, this, &Magpie::requestAssets); } QUrl Models::Magpie::getAddress () const { return address; } Models::Magpie::State Models::Magpie::getState () const { return state; } QString Models::Magpie::getAccessToken () const { return accessToken; } QString Models::Magpie::getRenewToken () const { return renewToken; } void Models::Magpie::installAPI (const std::shared_ptr& api) { Magpie::api = api; } void Models::Magpie::setAddress (const QUrl& address) { if (Magpie::address == address) return; if (state == Authenticated) { //do something } Magpie::address = address; emit addressChanged(address); if (address.isEmpty()) setState(NoServer); else setState(NotAuthenticated); } void Models::Magpie::forceAddress (const QUrl& address) { Magpie::address = ""; setAddress(address); } void Models::Magpie::setTokens (const QString access, const QString& renew, bool notify) { accessToken = access; renewToken = renew; setState(Authenticating); startPolling(); if (notify) emit storeTokens(accessToken, renewToken); } void Models::Magpie::setState (State newState) { if (newState == state) return; state = newState; emit stateChanged(state); } Models::Assets* Models::Magpie::getAssets () { return &assets; } void Models::Magpie::requestAssets () { api->requestAssets( [this] (const QVariantList& list) { std::deque result; bool res = Assets::deserialize(list, result); if (!res) { qDebug() << "Error deserializer received assets"; result.clear(); } else { qDebug() << "Assets successfully received"; } assets.addAssets(result); }, [this] (const QString& error, const std::optional& data) { assets.addAssets({}); } ); } void Models::Magpie::onFirstPollTimerSuccess () { setState(Authenticated); } void Models::Magpie::startPolling () { qDebug() << "Starting polling..."; if (state != Authenticating) throw std::runtime_error("Can not start polling in this state: " + std::to_string(state)); sendPoll(); firstPoll.start(); } void Models::Magpie::sendPoll (bool clear) { if (pollRequestId != API::none) throw std::runtime_error("an attempt to send second poll request while the other one is active"); pollRequestId = api->poll( std::bind(&Magpie::onPollSuccess, this, std::placeholders::_1), std::bind(&Magpie::onPollError, this, std::placeholders::_1, std::placeholders::_2), clear ); } void Models::Magpie::onPollSuccess (const QVariantMap& data) { pollRequestId = API::none; firstPoll.stop(); Codes::Poll code = Codes::convertPoll(data.value("result").toInt()); bool clear = false; switch (code) { case Codes::Poll::success: clear = handleChanges(qast(data.value("data"))); //todo handle the result case Codes::Poll::timeout: setState(Authenticated); return sendPoll(clear); case Codes::Poll::tokenProblem: case Codes::Poll::replace: case Codes::Poll::unknownError: //todo this one doesn't actually mean that we can't work for now, the network may be temporarily down or something return setState(NotAuthenticated); } } void Models::Magpie::onPollError (const QString& err, const std::optional& data) { qDebug() << "Poll error:" << err; pollRequestId = API::none; firstPoll.stop(); setState(NotAuthenticated); } bool Models::Magpie::handleChanges (const QVariantMap& changes) { QVariantMap::ConstIterator itr = changes.constFind("system"); if (itr != changes.constEnd() && itr.value().canConvert()) { const QVariantMap& sys = qast(itr.value()); QVariantMap::ConstIterator invItr = sys.constFind("invalidate"); if (invItr != sys.constEnd()) { const QVariant& vinv = invItr.value(); if (vinv.canConvert() && vinv.toBool()) resetAllModels(); } } itr = changes.constFind("assets"); if (itr != changes.constEnd() && itr.value().canConvert()) { const QVariantMap& assets = qast(itr.value()); QVariantMap::ConstIterator aItr = assets.constFind("invalidate"); if (aItr != assets.constEnd()) { const QVariant& vinv = aItr.value(); if (vinv.canConvert() && vinv.toBool()) Magpie::assets.clear(); } aItr = assets.constFind("added"); if (aItr != assets.constEnd() && aItr.value().canConvert()) { std::deque added; if (!Models::Assets::deserialize(qast(aItr.value()), added)) qDebug() << "Error deserializng added assets"; else Magpie::assets.addAssets(added); } } return true; } void Models::Magpie::resetAllModels () { assets.clear(); }