diff --git a/src/librssguard/services/feedly/definitions.h b/src/librssguard/services/feedly/definitions.h index 8f2d2ed17..25058d093 100755 --- a/src/librssguard/services/feedly/definitions.h +++ b/src/librssguard/services/feedly/definitions.h @@ -4,7 +4,8 @@ #define FEEDLY_UNLIMITED_BATCH_SIZE -1 #define FEEDLY_DEFAULT_BATCH_SIZE 20 #define FEEDLY_MAX_BATCH_SIZE 500 -#define FEEDLX_MAX_TOTAL_SIZE 5000 +#define FEEDLY_MAX_TOTAL_SIZE 5000 +#define FEEDLY_UNTAG_BATCH_SIZE 100 #define FEEDLY_GENERATE_DAT "https://feedly.com/v3/auth/dev" diff --git a/src/librssguard/services/feedly/feedlynetwork.cpp b/src/librssguard/services/feedly/feedlynetwork.cpp index 626d57106..97ac4570c 100755 --- a/src/librssguard/services/feedly/feedlynetwork.cpp +++ b/src/librssguard/services/feedly/feedlynetwork.cpp @@ -45,6 +45,89 @@ FeedlyNetwork::FeedlyNetwork(QObject* parent) #endif } +void FeedlyNetwork::untagEntries(const QString& tag_id, const QStringList& msg_custom_ids) { + if (msg_custom_ids.isEmpty()) { + return; + } + + QString bear = bearer(); + + if (bear.isEmpty()) { + qCriticalNN << LOGSEC_FEEDLY << "Cannot untag entries, because bearer is empty."; + throw NetworkException(QNetworkReply::NetworkError::AuthenticationRequiredError); + } + + QString target_url = fullUrl(Service::TagEntries) + + QSL("/%1/").arg(QString(QUrl::toPercentEncoding(tag_id))); + int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); + QByteArray output; + int i = 0; + + do { + auto msg_batch = msg_custom_ids.mid(i, FEEDLY_UNTAG_BATCH_SIZE); + + i += FEEDLY_UNTAG_BATCH_SIZE; + + auto ids = boolinq::from(msg_batch).select([](const QString& msg_id) { + return QString(QUrl::toPercentEncoding(msg_id)); + }).toStdList(); + QString final_url = target_url + FROM_STD_LIST(QStringList, ids).join(','); + auto result = NetworkFactory::performNetworkOperation(final_url, + timeout, + {}, + output, + QNetworkAccessManager::Operation::DeleteOperation, + { bearerHeader(bear) }, + false, + {}, + {}, + m_service->networkProxy()); + + if (result.first != QNetworkReply::NetworkError::NoError) { + throw NetworkException(result.first, output); + } + } + while (i < msg_custom_ids.size()); +} + +void FeedlyNetwork::tagEntries(const QString& tag_id, const QStringList& msg_custom_ids) { + if (msg_custom_ids.isEmpty()) { + return; + } + + QString bear = bearer(); + + if (bear.isEmpty()) { + qCriticalNN << LOGSEC_FEEDLY << "Cannot tag entries, because bearer is empty."; + throw NetworkException(QNetworkReply::NetworkError::AuthenticationRequiredError); + } + + QString target_url = fullUrl(Service::TagEntries) + QSL("/%1").arg(QString(QUrl::toPercentEncoding(tag_id))); + int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); + QByteArray output; + QByteArray input_data; + QJsonObject input; + + input["entryIds"] = QJsonArray::fromStringList(msg_custom_ids); + input_data = QJsonDocument(input).toJson(QJsonDocument::JsonFormat::Compact); + + auto result = NetworkFactory::performNetworkOperation(target_url, + timeout, + input_data, + output, + QNetworkAccessManager::Operation::PutOperation, + { bearerHeader(bear), + { "Content-Type", "application/json" } }, + false, + {}, + {}, + m_service->networkProxy()); + + if (result.first != QNetworkReply::NetworkError::NoError) { + throw NetworkException(result.first, output); + } +} + void FeedlyNetwork::markers(const QString& action, const QStringList& msg_custom_ids) { if (msg_custom_ids.isEmpty()) { return; @@ -137,7 +220,7 @@ QList FeedlyNetwork::streamContents(const QString& stream_id) { } while (!continuation.isEmpty() && (m_batchSize <= 0 || messages.size() < m_batchSize) && - messages.size() <= FEEDLX_MAX_TOTAL_SIZE); + messages.size() <= FEEDLY_MAX_TOTAL_SIZE); return messages; } @@ -466,6 +549,7 @@ QString FeedlyNetwork::fullUrl(FeedlyNetwork::Service service) const { return QSL(FEEDLY_API_URL_BASE) + FEEDLY_API_URL_COLLETIONS; case Service::Tags: + case Service::TagEntries: return QSL(FEEDLY_API_URL_BASE) + FEEDLY_API_URL_TAGS; case Service::StreamContents: diff --git a/src/librssguard/services/feedly/feedlynetwork.h b/src/librssguard/services/feedly/feedlynetwork.h index a46470249..a565f4ee2 100755 --- a/src/librssguard/services/feedly/feedlynetwork.h +++ b/src/librssguard/services/feedly/feedlynetwork.h @@ -21,6 +21,8 @@ class FeedlyNetwork : public QObject { explicit FeedlyNetwork(QObject* parent = nullptr); // API operations. + void untagEntries(const QString& tag_id, const QStringList& msg_custom_ids); + void tagEntries(const QString& tag_id, const QStringList& msg_custom_ids); void markers(const QString& action, const QStringList& msg_custom_ids); QList streamContents(const QString& stream_id); QVariantHash profile(const QNetworkProxy& network_proxy); @@ -58,7 +60,8 @@ class FeedlyNetwork : public QObject { Collections, Tags, StreamContents, - Markers + Markers, + TagEntries }; QString fullUrl(Service service) const; diff --git a/src/librssguard/services/feedly/feedlyserviceroot.cpp b/src/librssguard/services/feedly/feedlyserviceroot.cpp index e9de22cbc..bad9405b7 100755 --- a/src/librssguard/services/feedly/feedlyserviceroot.cpp +++ b/src/librssguard/services/feedly/feedlyserviceroot.cpp @@ -137,37 +137,57 @@ void FeedlyServiceRoot::saveAllCachedData(bool ignore_errors) { } } - /* - QMapIterator k(msg_cache.m_cachedLabelAssignments); + QMapIterator k(msg_cache.m_cachedLabelAssignments); - // Assign label for these messages. - while (k.hasNext()) { - k.next(); - auto label_custom_id = k.key(); - QStringList messages = k.value(); + // Assign label for these messages. + while (k.hasNext()) { + k.next(); + auto label_custom_id = k.key(); + QStringList messages = k.value(); - if (!messages.isEmpty()) { - if (network()->editLabels(label_custom_id, true, messages) != QNetworkReply::NetworkError::NoError && !ignore_errors) { - addLabelsAssignmentsToCache(messages, label_custom_id, true); + if (!messages.isEmpty()) { + try { + network()->tagEntries(label_custom_id, messages); } - } - } + catch (const NetworkException& net_ex) { + qCriticalNN << LOGSEC_FEEDLY + << "Failed to synchronize tag assignments with error:" + << QUOTE_W_SPACE(net_ex.message()) + << "and HTTP code" + << QUOTE_W_SPACE_DOT(net_ex.networkError()); - QMapIterator l(msg_cache.m_cachedLabelDeassignments); - - // Remove label from these messages. - while (l.hasNext()) { - l.next(); - auto label_custom_id = l.key(); - QStringList messages = l.value(); - - if (!messages.isEmpty()) { - if (network()->editLabels(label_custom_id, false, messages) != QNetworkReply::NetworkError::NoError && !ignore_errors) { - addLabelsAssignmentsToCache(messages, label_custom_id, false); + if (!ignore_errors) { + addLabelsAssignmentsToCache(messages, label_custom_id, true); + } } - } - } - */ + } + } + + QMapIterator l(msg_cache.m_cachedLabelDeassignments); + + // Remove label from these messages. + while (l.hasNext()) { + l.next(); + auto label_custom_id = l.key(); + QStringList messages = l.value(); + + if (!messages.isEmpty()) { + try { + network()->untagEntries(label_custom_id, messages); + } + catch (const NetworkException& net_ex) { + qCriticalNN << LOGSEC_FEEDLY + << "Failed to synchronize tag DEassignments with error:" + << QUOTE_W_SPACE(net_ex.message()) + << "and HTTP code" + << QUOTE_W_SPACE_DOT(net_ex.networkError()); + + if (!ignore_errors) { + addLabelsAssignmentsToCache(messages, label_custom_id, false); + } + } + } + } } void FeedlyServiceRoot::updateTitle() {