diff --git a/core/idl/wallet/common/wallet.djinni b/core/idl/wallet/common/wallet.djinni index dbbef06355..75dcf5938c 100644 --- a/core/idl/wallet/common/wallet.djinni +++ b/core/idl/wallet/common/wallet.djinni @@ -106,6 +106,13 @@ Block = record { height: i64; } +OperationCount = record { + # Type of operation + type: OperationType; + # Count of operations + count: i64; +} + # Class representing an operation. Operation = interface +c { # Get id's operation. @@ -265,6 +272,9 @@ OperationQuery = interface +c { # Execute query to retrieve operations. # @param callback, if execute method succeed, ListCallback object returning a List of Operation objects execute(callback: ListCallback); + # Count operations by type + # @param callback, if execute method succeed, ListCallback object returning a List of OperationCount objects + count(callback: ListCallback); } # Structure of informations needed for account creation. diff --git a/core/src/api/OperationCount.hpp b/core/src/api/OperationCount.hpp new file mode 100644 index 0000000000..9869e137f2 --- /dev/null +++ b/core/src/api/OperationCount.hpp @@ -0,0 +1,52 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file generated by Djinni from wallet.djinni + +#ifndef DJINNI_GENERATED_OPERATIONCOUNT_HPP +#define DJINNI_GENERATED_OPERATIONCOUNT_HPP + +#include "OperationType.hpp" +#include +#include +#include + +namespace ledger { namespace core { namespace api { + +struct OperationCount final { + /** Type of operation */ + OperationType type; + /** Count of operations */ + int64_t count; + + OperationCount(OperationType type_, + int64_t count_) + : type(std::move(type_)) + , count(std::move(count_)) + {} + + OperationCount(const OperationCount& cpy) { + this->type = cpy.type; + this->count = cpy.count; + } + + OperationCount() = default; + + + OperationCount& operator=(const OperationCount& cpy) { + this->type = cpy.type; + this->count = cpy.count; + return *this; + } + + template + void load(Archive& archive) { + archive(type, count); + } + + template + void save(Archive& archive) const { + archive(type, count); + } +}; + +} } } // namespace ledger::core::api +#endif //DJINNI_GENERATED_OPERATIONCOUNT_HPP diff --git a/core/src/api/OperationCountListCallback.hpp b/core/src/api/OperationCountListCallback.hpp new file mode 100644 index 0000000000..fbf17535c4 --- /dev/null +++ b/core/src/api/OperationCountListCallback.hpp @@ -0,0 +1,36 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file generated by Djinni from callback.djinni + +#ifndef DJINNI_GENERATED_OPERATIONCOUNTLISTCALLBACK_HPP +#define DJINNI_GENERATED_OPERATIONCOUNTLISTCALLBACK_HPP + +#include "../utils/optional.hpp" +#include +#ifndef LIBCORE_EXPORT + #if defined(_MSC_VER) + #include + #else + #define LIBCORE_EXPORT + #endif +#endif + +namespace ledger { namespace core { namespace api { + +struct Error; +struct OperationCount; + +/** Callback triggered by main completed task, returning optional result as list of template type T. */ +class OperationCountListCallback { +public: + virtual ~OperationCountListCallback() {} + + /** + * Method triggered when main task complete. + * @params result optional of type list, non null if main task failed + * @params error optional of type Error, non null if main task succeeded + */ + virtual void onCallback(const std::experimental::optional> & result, const std::experimental::optional & error) = 0; +}; + +} } } // namespace ledger::core::api +#endif //DJINNI_GENERATED_OPERATIONCOUNTLISTCALLBACK_HPP diff --git a/core/src/api/OperationQuery.hpp b/core/src/api/OperationQuery.hpp index 6e7820583b..e0453cb5f5 100644 --- a/core/src/api/OperationQuery.hpp +++ b/core/src/api/OperationQuery.hpp @@ -16,6 +16,7 @@ namespace ledger { namespace core { namespace api { +class OperationCountListCallback; class OperationListCallback; class QueryFilter; enum class OperationOrderKey; @@ -65,6 +66,12 @@ class LIBCORE_EXPORT OperationQuery { * @param callback, if execute method succeed, ListCallback object returning a List of Operation objects */ virtual void execute(const std::shared_ptr & callback) = 0; + + /** + * Count operations by type + * @param callback, if execute method succeed, ListCallback object returning a List of OperationCount objects + */ + virtual void count(const std::shared_ptr & callback) = 0; }; } } } // namespace ledger::core::api diff --git a/core/src/database/query/QueryBuilder.cpp b/core/src/database/query/QueryBuilder.cpp index 58a81f705d..0cfb01b45b 100644 --- a/core/src/database/query/QueryBuilder.cpp +++ b/core/src/database/query/QueryBuilder.cpp @@ -51,6 +51,10 @@ namespace ledger { query << sFilter; } + if (!_group.empty()) { + query << " GROUP BY " << _group; + } + if (!_order.empty()) { query << " ORDER BY "; for (auto it = _order.begin(); it != _order.end(); it++) { @@ -133,5 +137,10 @@ namespace ledger { return *this; } + QueryBuilder &QueryBuilder::groupBy(std::string group) { + _group = std::move(group); + return *this; + } + } } \ No newline at end of file diff --git a/core/src/database/query/QueryBuilder.h b/core/src/database/query/QueryBuilder.h index 8df104648f..9f76acba14 100644 --- a/core/src/database/query/QueryBuilder.h +++ b/core/src/database/query/QueryBuilder.h @@ -55,6 +55,7 @@ namespace ledger { QueryBuilder& order(std::string&& keys, bool&& descending, std::string&& table); QueryBuilder& limit(int32_t limit); QueryBuilder& offset(int32_t offset); + QueryBuilder& groupBy(std::string group); soci::details::prepare_temp_type execute(soci::session& sql); private: @@ -63,6 +64,7 @@ namespace ledger { std::string _keys; std::string _table; std::string _output; + std::string _group; std::list> _order; std::vector> _outerJoins; std::shared_ptr _filter; diff --git a/core/src/jni/jni/OperationCount.cpp b/core/src/jni/jni/OperationCount.cpp new file mode 100644 index 0000000000..cbcdc80a1a --- /dev/null +++ b/core/src/jni/jni/OperationCount.cpp @@ -0,0 +1,31 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file generated by Djinni from wallet.djinni + +#include "OperationCount.hpp" // my header +#include "Marshal.hpp" +#include "OperationType.hpp" + +namespace djinni_generated { + +OperationCount::OperationCount() = default; + +OperationCount::~OperationCount() = default; + +auto OperationCount::fromCpp(JNIEnv* jniEnv, const CppType& c) -> ::djinni::LocalRef { + const auto& data = ::djinni::JniClass::get(); + auto r = ::djinni::LocalRef{jniEnv->NewObject(data.clazz.get(), data.jconstructor, + ::djinni::get(::djinni_generated::OperationType::fromCpp(jniEnv, c.type)), + ::djinni::get(::djinni::I64::fromCpp(jniEnv, c.count)))}; + ::djinni::jniExceptionCheck(jniEnv); + return r; +} + +auto OperationCount::toCpp(JNIEnv* jniEnv, JniType j) -> CppType { + ::djinni::JniLocalScope jscope(jniEnv, 3); + assert(j != nullptr); + const auto& data = ::djinni::JniClass::get(); + return {::djinni_generated::OperationType::toCpp(jniEnv, jniEnv->GetObjectField(j, data.field_type)), + ::djinni::I64::toCpp(jniEnv, jniEnv->GetLongField(j, data.field_count))}; +} + +} // namespace djinni_generated diff --git a/core/src/jni/jni/OperationCount.hpp b/core/src/jni/jni/OperationCount.hpp new file mode 100644 index 0000000000..35ea714f18 --- /dev/null +++ b/core/src/jni/jni/OperationCount.hpp @@ -0,0 +1,35 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file generated by Djinni from wallet.djinni + +#ifndef DJINNI_GENERATED_OPERATIONCOUNT_HPP_JNI_ +#define DJINNI_GENERATED_OPERATIONCOUNT_HPP_JNI_ + +#include "../../api/OperationCount.hpp" +#include "djinni_support.hpp" + +namespace djinni_generated { + +class OperationCount final { +public: + using CppType = ::ledger::core::api::OperationCount; + using JniType = jobject; + + using Boxed = OperationCount; + + ~OperationCount(); + + static CppType toCpp(JNIEnv* jniEnv, JniType j); + static ::djinni::LocalRef fromCpp(JNIEnv* jniEnv, const CppType& c); + +private: + OperationCount(); + friend ::djinni::JniClass; + + const ::djinni::GlobalRef clazz { ::djinni::jniFindClass("co/ledger/core/OperationCount") }; + const jmethodID jconstructor { ::djinni::jniGetMethodID(clazz.get(), "", "(Lco/ledger/core/OperationType;J)V") }; + const jfieldID field_type { ::djinni::jniGetFieldID(clazz.get(), "type", "Lco/ledger/core/OperationType;") }; + const jfieldID field_count { ::djinni::jniGetFieldID(clazz.get(), "count", "J") }; +}; + +} // namespace djinni_generated +#endif //DJINNI_GENERATED_OPERATIONCOUNT_HPP_JNI_ diff --git a/core/src/jni/jni/OperationCountListCallback.cpp b/core/src/jni/jni/OperationCountListCallback.cpp new file mode 100644 index 0000000000..5440295d9d --- /dev/null +++ b/core/src/jni/jni/OperationCountListCallback.cpp @@ -0,0 +1,29 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file generated by Djinni from callback.djinni + +#include "OperationCountListCallback.hpp" // my header +#include "Error.hpp" +#include "Marshal.hpp" +#include "OperationCount.hpp" + +namespace djinni_generated { + +OperationCountListCallback::OperationCountListCallback() : ::djinni::JniInterface<::ledger::core::api::OperationCountListCallback, OperationCountListCallback>() {} + +OperationCountListCallback::~OperationCountListCallback() = default; + +OperationCountListCallback::JavaProxy::JavaProxy(JniType j) : Handle(::djinni::jniGetThreadEnv(), j) { } + +OperationCountListCallback::JavaProxy::~JavaProxy() = default; + +void OperationCountListCallback::JavaProxy::onCallback(const std::experimental::optional> & c_result, const std::experimental::optional<::ledger::core::api::Error> & c_error) { + auto jniEnv = ::djinni::jniGetThreadEnv(); + ::djinni::JniLocalScope jscope(jniEnv, 10); + const auto& data = ::djinni::JniClass<::djinni_generated::OperationCountListCallback>::get(); + jniEnv->CallVoidMethod(Handle::get().get(), data.method_onCallback, + ::djinni::get(::djinni::Optional>::fromCpp(jniEnv, c_result)), + ::djinni::get(::djinni::Optional::fromCpp(jniEnv, c_error))); + ::djinni::jniExceptionCheck(jniEnv); +} + +} // namespace djinni_generated diff --git a/core/src/jni/jni/OperationCountListCallback.hpp b/core/src/jni/jni/OperationCountListCallback.hpp new file mode 100644 index 0000000000..cff4efc43d --- /dev/null +++ b/core/src/jni/jni/OperationCountListCallback.hpp @@ -0,0 +1,48 @@ +// AUTOGENERATED FILE - DO NOT MODIFY! +// This file generated by Djinni from callback.djinni + +#ifndef DJINNI_GENERATED_OPERATIONCOUNTLISTCALLBACK_HPP_JNI_ +#define DJINNI_GENERATED_OPERATIONCOUNTLISTCALLBACK_HPP_JNI_ + +#include "../../api/OperationCountListCallback.hpp" +#include "djinni_support.hpp" + +namespace djinni_generated { + +class OperationCountListCallback final : ::djinni::JniInterface<::ledger::core::api::OperationCountListCallback, OperationCountListCallback> { +public: + using CppType = std::shared_ptr<::ledger::core::api::OperationCountListCallback>; + using CppOptType = std::shared_ptr<::ledger::core::api::OperationCountListCallback>; + using JniType = jobject; + + using Boxed = OperationCountListCallback; + + ~OperationCountListCallback(); + + static CppType toCpp(JNIEnv* jniEnv, JniType j) { return ::djinni::JniClass::get()._fromJava(jniEnv, j); } + static ::djinni::LocalRef fromCppOpt(JNIEnv* jniEnv, const CppOptType& c) { return {jniEnv, ::djinni::JniClass::get()._toJava(jniEnv, c)}; } + static ::djinni::LocalRef fromCpp(JNIEnv* jniEnv, const CppType& c) { return fromCppOpt(jniEnv, c); } + +private: + OperationCountListCallback(); + friend ::djinni::JniClass; + friend ::djinni::JniInterface<::ledger::core::api::OperationCountListCallback, OperationCountListCallback>; + + class JavaProxy final : ::djinni::JavaProxyHandle, public ::ledger::core::api::OperationCountListCallback + { + public: + JavaProxy(JniType j); + ~JavaProxy(); + + void onCallback(const std::experimental::optional> & result, const std::experimental::optional<::ledger::core::api::Error> & error) override; + + private: + friend ::djinni::JniInterface<::ledger::core::api::OperationCountListCallback, ::djinni_generated::OperationCountListCallback>; + }; + + const ::djinni::GlobalRef clazz { ::djinni::jniFindClass("co/ledger/core/OperationCountListCallback") }; + const jmethodID method_onCallback { ::djinni::jniGetMethodID(clazz.get(), "onCallback", "(Ljava/util/ArrayList;Lco/ledger/core/Error;)V") }; +}; + +} // namespace djinni_generated +#endif //DJINNI_GENERATED_OPERATIONCOUNTLISTCALLBACK_HPP_JNI_ diff --git a/core/src/jni/jni/OperationQuery.cpp b/core/src/jni/jni/OperationQuery.cpp index fe335aef4c..0b85dcc7d8 100644 --- a/core/src/jni/jni/OperationQuery.cpp +++ b/core/src/jni/jni/OperationQuery.cpp @@ -3,6 +3,7 @@ #include "OperationQuery.hpp" // my header #include "Marshal.hpp" +#include "OperationCountListCallback.hpp" #include "OperationListCallback.hpp" #include "OperationOrderKey.hpp" #include "QueryFilter.hpp" @@ -92,4 +93,13 @@ CJNIEXPORT void JNICALL Java_co_ledger_core_OperationQuery_00024CppProxy_native_ } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, ) } +CJNIEXPORT void JNICALL Java_co_ledger_core_OperationQuery_00024CppProxy_native_1count(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, jobject j_callback) +{ + try { + DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef); + const auto& ref = ::djinni::objectFromHandleAddress<::ledger::core::api::OperationQuery>(nativeRef); + ref->count(::djinni_generated::OperationCountListCallback::toCpp(jniEnv, j_callback)); + } JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, ) +} + } // namespace djinni_generated diff --git a/core/src/wallet/common/OperationQuery.cpp b/core/src/wallet/common/OperationQuery.cpp index d9ba161fbb..d594805a08 100644 --- a/core/src/wallet/common/OperationQuery.cpp +++ b/core/src/wallet/common/OperationQuery.cpp @@ -30,6 +30,7 @@ */ #include "OperationQuery.h" #include +#include #include "Operation.h" #include #include @@ -110,6 +111,41 @@ namespace ledger { return shared_from_this(); } + void OperationQuery::count( + const std::shared_ptr &callback) { + count().callback(_mainContext, callback); + } + + Future> + OperationQuery::count() { + auto self = shared_from_this(); + return async>([=] () { + std::vector out; + self->performCount(out); + return out; + }); + } + + soci::rowset OperationQuery::performCount(soci::session &sql) { + return _builder.select("o.type, count(*)") + .from("operations").to("o") + .outerJoin("blocks AS b", "o.block_uid = b.uid") + .groupBy("o.type") + .execute(sql); + } + + void OperationQuery::performCount(std::vector &operations) { + soci::session sql(_pool->getPool()); + const soci::rowset rows = performCount(sql); + + for (const auto& row : rows) { + const auto type = api::from_string(row.get(0)); + const auto count = soci::get_number(row, 1); + + operations.emplace_back(api::OperationCount{type, count}); + } + } + void OperationQuery::execute(const std::shared_ptr &callback) { execute().callback(_mainContext, callback); } diff --git a/core/src/wallet/common/OperationQuery.h b/core/src/wallet/common/OperationQuery.h index 31760c744d..2822a7ab27 100644 --- a/core/src/wallet/common/OperationQuery.h +++ b/core/src/wallet/common/OperationQuery.h @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -69,11 +70,14 @@ namespace ledger { void execute(const std::shared_ptr &callback) override; Future>> execute(); + void count(const std::shared_ptr &callback) override; + Future> count(); std::shared_ptr registerAccount(const std::shared_ptr& account); private: void performExecute(std::vector>& operations); + void performCount(std::vector& operations); void inflateCompleteTransaction(soci::session& sql, const std::string &accountUid, OperationApi& operation); void inflateBitcoinLikeTransaction(soci::session& sql, const std::string &accountUid, OperationApi& operation); void inflateCosmosLikeTransaction(soci::session& sql, const std::string &accountUid, OperationApi& operation); @@ -86,6 +90,7 @@ namespace ledger { protected: virtual soci::rowset performExecute(soci::session &sql); + virtual soci::rowset performCount(soci::session &sql); QueryBuilder _builder; std::shared_ptr _headFilter; bool _fetchCompleteOperation; diff --git a/core/test/integration/pool_tests.cpp b/core/test/integration/pool_tests.cpp index c4a88983fa..dda27a557b 100644 --- a/core/test/integration/pool_tests.cpp +++ b/core/test/integration/pool_tests.cpp @@ -170,4 +170,4 @@ TEST_F(WalletPoolTest, CreateAndGetWallet) { auto wallets = uv::wait(pool->getWallets(0, 1)); EXPECT_TRUE(wallets.front()->getName() == walletName); } -} +} \ No newline at end of file