diff --git a/libnymea-core/jsonrpc/jsontypes.cpp b/libnymea-core/jsonrpc/jsontypes.cpp index fd3a13b8..c8895873 100644 --- a/libnymea-core/jsonrpc/jsontypes.cpp +++ b/libnymea-core/jsonrpc/jsontypes.cpp @@ -1275,31 +1275,22 @@ QString JsonTypes::basicTypeToString(const QVariant::Type &type) switch (type) { case QVariant::Uuid: return "Uuid"; - break; case QVariant::String: return "String"; - break; case QVariant::Int: return "Int"; - break; case QVariant::UInt: return "Uint"; - break; case QVariant::Double: return "Double"; - break; case QVariant::Bool: return "Bool"; - break; case QVariant::Color: return "Color"; - break; case QVariant::Time: return "Time"; - break; default: - return QVariant::typeToName(type); - break; + return QVariant::typeToName(static_cast(type)); } } @@ -1554,6 +1545,12 @@ LogFilter JsonTypes::unpackLogFilter(const QVariantMap &logFilterMap) filter.addValue(value.toString()); } } + if (logFilterMap.contains("limit")) { + filter.setLimit(logFilterMap.value("limit", -1).toInt()); + } + if (logFilterMap.contains("offset")) { + filter.setOffset(logFilterMap.value("offset").toInt()); + } return filter; } diff --git a/libnymea-core/jsonrpc/logginghandler.cpp b/libnymea-core/jsonrpc/logginghandler.cpp index 01c55f94..c50c6e61 100644 --- a/libnymea-core/jsonrpc/logginghandler.cpp +++ b/libnymea-core/jsonrpc/logginghandler.cpp @@ -60,9 +60,20 @@ LoggingHandler::LoggingHandler(QObject *parent) : QVariantMap timeFilter; params.clear(); returns.clear(); setDescription("GetLogEntries", "Get the LogEntries matching the given filter. " - "Each list element of a given filter will be connected with OR " - "to each other. Each of the given filters will be connected with AND " - "to each other."); + "The result set will contain entries matching all filter rules combined. " + "If multiple options are given for a single filter type, the result set will " + "contain entries matching any of those. The offset starts at the newest entry " + "in the result set. By default all items are returned. Example: If the specified " + "filter returns a total amount of 100 entries:\n" + "- a offset value of 10 would include the oldest 90 entries\n" + "- a offset value of 0 would return all 100 entries\n\n" + "The offset is particularly useful in combination with the maxCount property and " + "can be used for pagination. E.g. A result set of 10000 entries can be fetched in " + " batches of 1000 entries by fetching\n" + "1) offset 0, maxCount 1000: Entries 0 to 9999\n" + "2) offset 10000, maxCount 1000: Entries 10000 - 19999\n" + "3) offset 20000, maxCount 1000: Entries 20000 - 29999\n" + "..."); timeFilter.insert("o:startDate", JsonTypes::basicTypeToString(JsonTypes::Int)); timeFilter.insert("o:endDate", JsonTypes::basicTypeToString(JsonTypes::Int)); params.insert("o:timeFilters", QVariantList() << timeFilter); @@ -72,9 +83,13 @@ LoggingHandler::LoggingHandler(QObject *parent) : params.insert("o:typeIds", QVariantList() << JsonTypes::basicTypeToString(JsonTypes::Uuid)); params.insert("o:deviceIds", QVariantList() << JsonTypes::basicTypeToString(JsonTypes::Uuid)); params.insert("o:values", QVariantList() << JsonTypes::basicTypeToString(JsonTypes::Variant)); + params.insert("o:limit", JsonTypes::basicTypeToString(JsonTypes::Int)); + params.insert("o:offset", JsonTypes::basicTypeToString(JsonTypes::Int)); setParams("GetLogEntries", params); returns.insert("loggingError", JsonTypes::loggingErrorRef()); returns.insert("o:logEntries", QVariantList() << JsonTypes::logEntryRef()); + returns.insert("count", JsonTypes::basicTypeToString(JsonTypes::Int)); + returns.insert("offset", JsonTypes::basicTypeToString(JsonTypes::Int)); setReturns("GetLogEntries", returns); // Notifications @@ -126,7 +141,10 @@ JsonReply* LoggingHandler::GetLogEntries(const QVariantMap ¶ms) const entries.append(JsonTypes::packLogEntry(entry)); } QVariantMap returns = statusToReply(Logging::LoggingErrorNoError); + returns.insert("logEntries", entries); + returns.insert("offset", filter.offset()); + returns.insert("count", entries.count()); return createReply(returns); } diff --git a/libnymea-core/jsonrpc/logginghandler.h b/libnymea-core/jsonrpc/logginghandler.h index 354452d2..595766d2 100644 --- a/libnymea-core/jsonrpc/logginghandler.h +++ b/libnymea-core/jsonrpc/logginghandler.h @@ -31,7 +31,7 @@ class LoggingHandler : public JsonHandler { Q_OBJECT public: - explicit LoggingHandler(QObject *parent = 0); + explicit LoggingHandler(QObject *parent = nullptr); QString name() const override; Q_INVOKABLE JsonReply *GetLogEntries(const QVariantMap ¶ms) const; diff --git a/libnymea-core/logging/logengine.cpp b/libnymea-core/logging/logengine.cpp index 850508ed..f17a9352 100644 --- a/libnymea-core/logging/logengine.cpp +++ b/libnymea-core/logging/logengine.cpp @@ -189,13 +189,21 @@ QList LogEngine::logEntries(const LogFilter &filter) const QList results; QSqlQuery query(m_db); + QString limitString; + if (filter.limit() >= 0) { + limitString.append(QString("LIMIT %1 ").arg(filter.limit())); + } + if (filter.offset() > 0) { + limitString.append(QString("OFFSET %1").arg(QString::number(filter.offset()))); + } + QString queryString; if (filter.isEmpty()) { - queryString = "SELECT * FROM entries ORDER BY timestamp;"; + queryString = QString("SELECT * FROM entries ORDER BY timestamp DESC %1;").arg(limitString); } else { - queryString = QString("SELECT * FROM entries WHERE %1 ORDER BY timestamp;").arg(filter.queryString()); + queryString = QString("SELECT * FROM entries WHERE %1 ORDER BY timestamp DESC %2;").arg(filter.queryString()).arg(limitString); } -// qCDebug(dcLogEngine()) << "Preparing query:" << queryString; + qCDebug(dcLogEngine()) << "Preparing query:" << queryString; query.prepare(queryString); foreach (const QString &value, filter.values()) { diff --git a/libnymea-core/logging/logfilter.cpp b/libnymea-core/logging/logfilter.cpp index 93b60454..f697daf3 100644 --- a/libnymea-core/logging/logfilter.cpp +++ b/libnymea-core/logging/logfilter.cpp @@ -177,6 +177,47 @@ QList LogFilter::values() const return m_values; } +/*! Set the maximum count for the result set. Unless a \l{offset} is specified, + * the newest \a count entries will be returned. \sa{setOffset} + */ +void LogFilter::setLimit(int limit) +{ + m_limit = limit; +} + +/*! Returns the maximum count for the result set. \sa{setOffset} */ +int LogFilter::limit() const +{ + return m_limit; +} + +/*! Set the offset for the result set. + * The offset starts at the newest entry in the result set. + * 0 (default) means "all items" + * Example: If the specified filter returns a total amount of 100 entries: + * - a offset value of 10 would include the oldest 90 entries + * - a offset value of 0 would return all 100 entries + * + * The offset is particularly useful in combination with the \l{limit} property and + * can be used for pagination. + * + * E.g. A result set of 10000 entries can be fetched in batches of 1000 entries by fetching + * 1) offset 0, limit 1000: Entries 0 to 9999 + * 2) offset 10000, limit 1000: Entries 10000 - 19999 + * 3) offset 20000, limit 1000: Entries 20000 - 29999 + * ... + */ +void LogFilter::setOffset(int offset) +{ + m_offset = offset; +} + +/*! Returns the offset for the result set. \sa{setOffset} */ +int LogFilter::offset() const +{ + return m_offset; +} + /*! Returns true if this \l{LogFilter} is empty. */ bool LogFilter::isEmpty() const { diff --git a/libnymea-core/logging/logfilter.h b/libnymea-core/logging/logfilter.h index 67aeb447..5b8a86f2 100644 --- a/libnymea-core/logging/logfilter.h +++ b/libnymea-core/logging/logfilter.h @@ -62,6 +62,12 @@ public: void addValue(const QString &value); QList values() const; + void setLimit(int limit); + int limit() const; + + void setOffset(int offset); + int offset() const; + bool isEmpty() const; private: @@ -72,6 +78,8 @@ private: QList m_typeIds; QList m_deviceIds; QList m_values; + int m_limit = -1; + int m_offset = 0; QString createDateString() const; QString createTimeFilterString(QPair timeFilter) const; diff --git a/nymea.pri b/nymea.pri index 2a4b38b1..88ee6ced 100644 --- a/nymea.pri +++ b/nymea.pri @@ -6,7 +6,7 @@ NYMEA_PLUGINS_PATH=/usr/lib/$$system('dpkg-architecture -q DEB_HOST_MULTIARCH')/ # define protocol versions JSON_PROTOCOL_VERSION_MAJOR=1 -JSON_PROTOCOL_VERSION_MINOR=9 +JSON_PROTOCOL_VERSION_MINOR=10 REST_API_VERSION=1 DEFINES += NYMEA_VERSION_STRING=\\\"$${NYMEA_VERSION_STRING}\\\" \ diff --git a/tests/auto/api.json b/tests/auto/api.json index 18c1908d..3f1aeb1c 100644 --- a/tests/auto/api.json +++ b/tests/auto/api.json @@ -1,4 +1,4 @@ -1.9 +1.10 { "methods": { "Actions.ExecuteAction": { @@ -552,7 +552,7 @@ } }, "Logging.GetLogEntries": { - "description": "Get the LogEntries matching the given filter. Each list element of a given filter will be connected with OR to each other. Each of the given filters will be connected with AND to each other.", + "description": "Get the LogEntries matching the given filter. The result set will contain entries matching all filter rules combined. If multiple options are given for a single filter type, the result set will contain entries matching any of those. The offset starts at the newest entry in the result set. By default all items are returned. Example: If the specified filter returns a total amount of 100 entries:\n- a offset value of 10 would include the oldest 90 entries\n- a offset value of 0 would return all 100 entries\n\nThe offset is particularly useful in combination with the maxCount property and can be used for pagination. E.g. A result set of 10000 entries can be fetched in batches of 1000 entries by fetching\n1) offset 0, maxCount 1000: Entries 0 to 9999\n2) offset 10000, maxCount 1000: Entries 10000 - 19999\n3) offset 20000, maxCount 1000: Entries 20000 - 29999\n...", "params": { "o:deviceIds": [ "Uuid" @@ -560,12 +560,14 @@ "o:eventTypes": [ "$ref:LoggingEventType" ], + "o:limit": "Int", "o:loggingLevels": [ "$ref:LoggingLevel" ], "o:loggingSources": [ "$ref:LoggingSource" ], + "o:offset": "Int", "o:timeFilters": [ { "o:endDate": "Int", @@ -580,10 +582,12 @@ ] }, "returns": { + "count": "Int", "loggingError": "$ref:LoggingError", "o:logEntries": [ "$ref:LogEntry" - ] + ], + "offset": "Int" } }, "NetworkManager.ConnectWifiNetwork": {