Skip to content

Commit

Permalink
GetAccountDetail interface: Postgres queries
Browse files Browse the repository at this point in the history
Signed-off-by: Akvinikym <[email protected]>
  • Loading branch information
Akvinikym authored and l4l committed Jul 25, 2018
1 parent dc578ac commit 5cafea5
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 18 deletions.
60 changes: 53 additions & 7 deletions irohad/ametsuchi/impl/postgres_wsv_query.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
namespace iroha {
namespace ametsuchi {

using shared_model::interface::types::AccountDetailKeyType;
using shared_model::interface::types::AccountIdType;
using shared_model::interface::types::AssetIdType;
using shared_model::interface::types::DomainIdType;
Expand Down Expand Up @@ -119,20 +120,65 @@ namespace iroha {
}

boost::optional<std::string> PostgresWsvQuery::getAccountDetail(
const std::string &account_id) {
return execute_("SELECT data#>>" + transaction_.quote("{}")
+ " FROM account WHERE account_id = "
+ transaction_.quote(account_id) + ";")
| [&](const auto &result) -> boost::optional<std::string> {
const std::string &account_id,
const AccountDetailKeyType &key,
const AccountIdType &writer) {
return [this, &account_id, &key, &writer]() {
if (key.empty() and writer.empty()) {
// retrieve all values for a specified account
return execute_("SELECT data#>>" + transaction_.quote("{}")
+ " FROM account WHERE account_id = "
+ transaction_.quote(account_id) + ";");
} else if (not key.empty() and not writer.empty()) {
// retrieve values for the account, under the key and added by the
// writer
return execute_(
"SELECT json_build_object("
+ transaction_.quote(writer) + ", json_build_object("
+ transaction_.quote(key) + ", ("
"SELECT data #>> '{\"" + writer + "\""
+ ", \"" + key + "\"}' "
"FROM account "
"WHERE account_id = " + transaction_.quote(account_id)
+ ")));");
} else if (not writer.empty()) {
// retrieve values added by the writer under all keys
return execute_(
"SELECT json_build_object("
+ transaction_.quote(writer) + ", ("
"SELECT data -> " + transaction_.quote(writer) + " "
"FROM account "
"WHERE account_id = " + transaction_.quote(account_id)
+ "));");
} else {
// retrieve values from all writers under the key
return execute_(
"SELECT json_object_agg(key, value) AS json "
"FROM ("
"SELECT json_build_object("
"kv.key, "
"json_build_object("
+ transaction_.quote(key) + ", "
"kv.value -> " + transaction_.quote(key) + ")) "
"FROM jsonb_each(("
"SELECT data "
"FROM account "
"WHERE account_id = "
+ transaction_.quote(account_id) + ")) kv "
"WHERE kv.value ? " + transaction_.quote(key) + ") "
"AS jsons, json_each(json_build_object);");
}
}() | [&](const auto &result) -> boost::optional<std::string> {
if (result.empty()) {
// result will be empty iff account id is not found
log_->info(kAccountNotFound, account_id);
return boost::none;
}
auto row = result.at(0);
std::string res;
auto row = result.at(0);
row.at(0) >> res;

// if res is empty, then that key does not exist for this account
// if res is empty, then there is no data for this account
if (res.empty()) {
return boost::none;
}
Expand Down
15 changes: 13 additions & 2 deletions irohad/ametsuchi/impl/postgres_wsv_query.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,30 +42,41 @@ namespace iroha {
boost::optional<std::shared_ptr<shared_model::interface::Account>>
getAccount(const shared_model::interface::types::AccountIdType
&account_id) override;

boost::optional<std::string> getAccountDetail(
const shared_model::interface::types::AccountIdType &account_id)
override;
const shared_model::interface::types::AccountIdType &account_id,
const shared_model::interface::types::AccountDetailKeyType &key = "",
const shared_model::interface::types::AccountIdType &writer =
"") override;

boost::optional<std::vector<shared_model::interface::types::PubkeyType>>
getSignatories(const shared_model::interface::types::AccountIdType
&account_id) override;

boost::optional<std::shared_ptr<shared_model::interface::Asset>> getAsset(
const shared_model::interface::types::AssetIdType &asset_id) override;

boost::optional<
std::vector<std::shared_ptr<shared_model::interface::AccountAsset>>>
getAccountAssets(const shared_model::interface::types::AccountIdType
&account_id) override;

boost::optional<std::shared_ptr<shared_model::interface::AccountAsset>>
getAccountAsset(
const shared_model::interface::types::AccountIdType &account_id,
const shared_model::interface::types::AssetIdType &asset_id) override;

boost::optional<
std::vector<std::shared_ptr<shared_model::interface::Peer>>>
getPeers() override;

boost::optional<std::vector<shared_model::interface::types::RoleIdType>>
getRoles() override;

boost::optional<std::shared_ptr<shared_model::interface::Domain>>
getDomain(const shared_model::interface::types::DomainIdType &domain_id)
override;

bool hasAccountGrantablePermission(
const shared_model::interface::types::AccountIdType
&permitee_account_id,
Expand Down
8 changes: 7 additions & 1 deletion irohad/ametsuchi/wsv_query.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,16 @@ namespace iroha {
/**
* Get accounts information from its key-value storage
* @param account_id - account to get details about
* @param key - only values under this key from Json are returned; default
* empty
* @param writer - only values, added by the writer's account, are
* returned; default empty
* @return optional of account details
*/
virtual boost::optional<std::string> getAccountDetail(
const std::string &account_id) = 0;
const std::string &account_id,
const std::string &key = "",
const std::string &writer = "") = 0;

/**
* Get signatories of account by user account_id
Expand Down
2 changes: 1 addition & 1 deletion test/module/iroha-cli/client_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ TEST_F(ClientServerTest, SendQueryWhenValid) {
EXPECT_CALL(*wsv_query, getSignatories("admin@test"))
.WillRepeatedly(Return(signatories));

EXPECT_CALL(*wsv_query, getAccountDetail("test@test"))
EXPECT_CALL(*wsv_query, getAccountDetail("test@test", "", ""))
.WillOnce(Return(boost::make_optional(std::string("value"))));

const std::vector<std::string> kRole{"role"};
Expand Down
12 changes: 7 additions & 5 deletions test/module/irohad/ametsuchi/ametsuchi_mocks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@
#include <gmock/gmock.h>
#include <boost/optional.hpp>
#include "ametsuchi/block_query.hpp"
#include "ametsuchi/key_value_storage.hpp"
#include "ametsuchi/mutable_factory.hpp"
#include "ametsuchi/mutable_storage.hpp"
#include "ametsuchi/peer_query.hpp"
#include "ametsuchi/storage.hpp"
#include "ametsuchi/temporary_factory.hpp"
#include "ametsuchi/temporary_wsv.hpp"
#include "ametsuchi/wsv_query.hpp"
#include "ametsuchi/key_value_storage.hpp"
#include "common/result.hpp"
#include "interfaces/common_objects/peer.hpp"

Expand All @@ -39,8 +39,11 @@ namespace iroha {
MOCK_METHOD1(getAccountRoles,
boost::optional<std::vector<std::string>>(
const std::string &account_id));
MOCK_METHOD1(getAccountDetail,
boost::optional<std::string>(const std::string &account_id));
MOCK_METHOD3(
getAccountDetail,
boost::optional<std::string>(const std::string &account_id,
const std::string &key,
const std::string &writer));
MOCK_METHOD1(getRolePermissions,
boost::optional<shared_model::interface::RolePermissionSet>(
const std::string &role_name));
Expand Down Expand Up @@ -261,8 +264,7 @@ namespace iroha {
class MockKeyValueStorage : public KeyValueStorage {
public:
MOCK_METHOD2(add, bool(Identifier, const Bytes &));
MOCK_CONST_METHOD1(get,
boost::optional<Bytes>(Identifier));
MOCK_CONST_METHOD1(get, boost::optional<Bytes>(Identifier));
MOCK_CONST_METHOD0(directory, std::string(void));
MOCK_CONST_METHOD0(last_id, Identifier(void));
MOCK_METHOD0(dropAll, void(void));
Expand Down
93 changes: 91 additions & 2 deletions test/module/irohad/ametsuchi/wsv_query_command_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ namespace iroha {

/**
* @given inserted role, domain, account
* @when update json data in account
* @when update json data in account
* @then get account and check json data is the same
*/
TEST_F(AccountTest, UpdateAccountJSONData) {
Expand All @@ -233,7 +233,96 @@ namespace iroha {
* @then getAccountDetail will return nullopt
*/
TEST_F(AccountTest, GetAccountDetailInvalidWhenNotFound) {
EXPECT_FALSE(query->getAccountDetail("invalid account id"));
EXPECT_FALSE(query->getAccountDetail("invalid account id", "", ""));
}

/**
* @given details, inserted for one account
* @when performing query to retrieve all account's details
* @then getAccountDetail will return all details of this account
*/
TEST_F(AccountTest, GetAccountDetailWithAccount) {
ASSERT_TRUE(val(command->insertAccount(*account)));
ASSERT_TRUE(val(command->setAccountKV(
account->accountId(), account->accountId(), "some_key", "some_val")));

auto acc_details = query->getAccountDetail(account->accountId(), "", "");
ASSERT_TRUE(acc_details);
ASSERT_EQ(R"({"id@domain": {"key": "value", "some_key": "some_val"}})",
*acc_details);
}

/**
* @given details, inserted into one account by two writers, with one of the
* keys repeated
* @when performing query to retrieve details under this key
* @then getAccountDetail will return details from both writers under the
* specified key
*/
TEST_F(AccountTest, GetAccountDetailWithKey) {
ASSERT_TRUE(val(command->insertAccount(*account)));
ASSERT_TRUE(val(command->setAccountKV(
account->accountId(), account->accountId(), "some_key", "some_val")));
ASSERT_TRUE(val(command->setAccountKV(account->accountId(),
account->accountId(),
"another_key",
"another_val")));
ASSERT_TRUE(val(command->setAccountKV(
account->accountId(), "admin", "some_key", "even_third_val")));

auto acc_details =
query->getAccountDetail(account->accountId(), "some_key", "");
ASSERT_TRUE(acc_details);
ASSERT_EQ(
"{ \"admin\" : {\"some_key\" : \"even_third_val\"}, "
"\"id@domain\" : {\"some_key\" : \"some_val\"} }",
*acc_details);
}

/**
* @given details, inserted into one account by two writers
* @when performing query to retrieve details, added by one of the writers
* @then getAccountDetail will return only details, added by the specified
* writer
*/
TEST_F(AccountTest, GetAccountDetailWithWriter) {
ASSERT_TRUE(val(command->insertAccount(*account)));
ASSERT_TRUE(val(command->setAccountKV(
account->accountId(), account->accountId(), "some_key", "some_val")));
ASSERT_TRUE(val(command->setAccountKV(
account->accountId(), "admin", "another_key", "another_val")));

auto acc_details =
query->getAccountDetail(account->accountId(), "", "admin");
ASSERT_TRUE(acc_details);
ASSERT_EQ(R"({"admin" : {"another_key": "another_val"}})",
*acc_details);
}

/**
* @given details, inserted into one account by two writers, with one of the
* keys repeated
* @when performing query to retrieve details under this key and added by
* one of the writers
* @then getAccountDetail will return only details, which are under the
* specified key and added by the specified writer
*/
TEST_F(AccountTest, GetAccountDetailWithKeyAndWriter) {
ASSERT_TRUE(val(command->insertAccount(*account)));
ASSERT_TRUE(val(command->setAccountKV(
account->accountId(), account->accountId(), "some_key", "some_val")));
ASSERT_TRUE(val(command->setAccountKV(account->accountId(),
account->accountId(),
"another_key",
"another_val")));
ASSERT_TRUE(val(command->setAccountKV(
account->accountId(), "admin", "some_key", "even_third_val")));

auto acc_details = query->getAccountDetail(
account->accountId(), "some_key", account->accountId());
ASSERT_TRUE(acc_details);
ASSERT_EQ(R"({"id@domain" : {"some_key" : "some_val"}})",
*acc_details);
}

class AccountRoleTest : public WsvQueryCommandTest {
Expand Down

0 comments on commit 5cafea5

Please sign in to comment.