move 'create user' functionality to user implementation class

This commit is contained in:
Wieczorek Bartosz 2018-07-30 11:02:02 +02:00
parent 087405ec08
commit fa7beb80fe
15 changed files with 205 additions and 111 deletions

View File

@ -49,6 +49,8 @@ std::unique_ptr< Wt::WApplication > createApplication(const Wt::WEnvironment & e
auto factory = std::make_unique< eedb::pg::FactoryImpl >(std::move(connection));
factory->init();
return eedb::WebApplicationFactory{}.create(std::move(factory), env);
}

View File

@ -28,6 +28,10 @@ namespace impl {
class Connection;
class CategoriesChildrenImpl : CategoriesChildren{
};
class CategoryImpl : public impl::PgCategory {
public:
/// TODO should be private
@ -35,7 +39,7 @@ class CategoryImpl : public impl::PgCategory {
spimpl::unique_impl_ptr< CategoryImplPrivate > _priv;
public:
// ROOT category
// root category
CategoryImpl(Connection & db);
// creates new category
@ -52,6 +56,11 @@ class CategoryImpl : public impl::PgCategory {
std::unique_ptr< CategoriesChildren > children() const override;
void detach();
Category * attachTo(Category *parent);
// postgresql api
public:
int64_t id() const override;
void setParent(PgCategory * parent) override;

View File

@ -11,6 +11,8 @@ class FactoryImpl : public Factory {
FactoryImpl(std::unique_ptr< Connection > connection);
~FactoryImpl() override;
void init() override;
std::unique_ptr< Users > usersRepository() const override;
std::unique_ptr< CategoriesRepository > categoriesRepository() const override;

View File

@ -7,6 +7,7 @@
#include <utils/spimpl.hpp>
namespace eedb::pg {
namespace impl {
class UserImplPriv : public User {
public:
@ -23,6 +24,8 @@ class UserImpl : public impl::UserImplPriv {
public:
UserImpl(Connection & db, int uid);
UserImpl(Connection & db, std::unique_ptr< AuthInfo > authInfo, std::unique_ptr< AuthIdentity > authIdentity);
const UserConfig & config() const override;
AuthTokens & authTokens() const override;
@ -32,7 +35,6 @@ class UserImpl : public impl::UserImplPriv {
AuthInfo & authInfo() const override;
public:
int64_t id() const override;
};
} // namespace eedb::pg

View File

@ -42,6 +42,8 @@ class CategoryMock : public impl::PgCategory {
MOCK_CONST_METHOD0(children, std::unique_ptr< CategoriesChildren >());
MOCK_CONST_METHOD0(id, int64_t());
MOCK_METHOD1(setParent, void(impl::PgCategory *));
MOCK_METHOD0(detach, void());
MOCK_METHOD1(attachTo, Category *( Category * ) );
void _init_simple(std::string name) {
_root_guard();

View File

@ -17,6 +17,16 @@
namespace eedb::pg {
struct CategoryImpl::CategoryImplPrivate {
private:
Connection & _db;
impl::PgCategory * _parent{nullptr};
int64_t _uid{};
std::vector< std::unique_ptr< Category > > _children;
std::string _name;
std::string _path;
std::string _description;
private:
// select
constexpr auto stat_columns() const {
@ -89,14 +99,12 @@ struct CategoryImpl::CategoryImplPrivate {
public:
impl::PgCategory * _self{nullptr};
CategoryImplPrivate(Connection & db, Category * parent) : _db{db}, _parent{cast(parent)} {
auto row = _db(select(columns()).from(table_list()).where(root_path_match()).limit(1ul));
// no root category if row empty
if(row.empty()) {
row = _db(sqlpp::postgresql::insert_into(t_category).set(set_rootCategoryValues()).returning(columns()));
CategoryImplPrivate(Connection & db) : _db{db}, _parent{nullptr} {
try {
init_data(_db(select(columns()).from(table_list()).where(root_path_match()).limit(1ul)).front());
} catch(const sqlpp::postgresql::failure & f) {
throw;
}
init_data(row.front());
}
CategoryImplPrivate(Connection & db, std::string name, std::string description) : _db{db}, _parent{nullptr} {
@ -115,43 +123,37 @@ struct CategoryImpl::CategoryImplPrivate {
_parent = parent;
}
void detach() {
throw std::logic_error{"unimplemented"};
try {
_db(update(table_list()).set(t_category.parent_id = sqlpp::null).where(t_category.id == _uid));
_parent = nullptr;
} catch(sqlpp::exception) {
}
}
Category * attachTo(Category * ) {
throw std::logic_error{"unimplemented"};
}
Category * addChild(std::unique_ptr< Category > category) {
auto pgCategory = cast(category);
// category can be added if
// #1 is detached
// #2 is unique within parent
if(category->parent()) {
/// TODO can't add a category that is already attached to some other category
throw exceptions::CategoryAddException(std::move(category), "Cannot change the parent of category");
}
try {
{
auto data = _db(sqlpp::select(t_category.name, t_category.id, t_category.parent_id).from(t_category).unconditionally());
std::cout << "blah1\n";
for(const auto & row : data) {
std::cout << row.name << " " << row.id << " " << row.parent_id << '\n';
}
}
_db(sqlpp::update(table_list()).set(t_category.parent_id = _uid).where(t_category.id == pgCategory->id()));
pgCategory->setParent(_self);
{
auto data = _db(sqlpp::select(t_category.name, t_category.id, t_category.parent_id).from(t_category).unconditionally());
std::cout << "blah2\n";
for(const auto & row : data) {
std::cout << row.name << " " << row.id << " " << row.parent_id << '\n';
}
}
return _children.emplace_back(std::move(category)).get();
} catch(sqlpp::postgresql::unique_violation e) {
throw exceptions::CategoryAddException(std::move(category), e.what());
}
return nullptr;
}
bool detached() const {
@ -170,33 +172,19 @@ struct CategoryImpl::CategoryImplPrivate {
return _uid;
}
auto path() const {
return std::string_view{_path};
}
public:
template < typename Row >
void init_data(Row & row) {
void init_data(const Row & row) {
_uid = row.id;
_name = row.name;
_path = row.parent_path;
_description = row.description;
}
private:
Connection & _db;
impl::PgCategory * _parent{nullptr};
int64_t _uid{};
std::vector< std::unique_ptr< Category > > _children;
std::string _name;
std::string _path;
std::string _description;
};
CategoryImpl::CategoryImpl(Connection & db) //
: _priv{spimpl::make_unique_impl< CategoryImplPrivate >(db, nullptr)} {
_priv->_self = this;
CategoryImpl::CategoryImpl(Connection &db) : _priv{spimpl::make_unique_impl< CategoryImplPrivate >(db)}
{
}
CategoryImpl::CategoryImpl(Connection & db, std::string name, std::string description)
@ -225,6 +213,15 @@ std::unique_ptr< CategoriesChildren > CategoryImpl::children() const {
return _priv->categories();
}
void CategoryImpl::detach() {
///TODO set parent children as "dirty" to force cache update
_priv->detach();
}
Category * CategoryImpl::attachTo(Category * parent) {
return _priv->attachTo(parent);
}
int64_t CategoryImpl::id() const {
return _priv->id();
}

View File

@ -1,23 +1,72 @@
#include <eedb/pg/Factory.hpp>
#include <eedb/AuthIdentityConst.hpp>
#include <eedb/AuthInfoConst.hpp>
#include <eedb/pg/CategoriesRepository.hpp>
#include <eedb/pg/ItemsRepository.hpp>
#include <eedb/pg/Session.hpp>
#include <eedb/pg/User.hpp>
#include <eedb/pg/Users.hpp>
#include <eedb/pg/connection.hpp>
#include <eedb/pg/model/all.hpp>
#include <eedb/pg/model/category.h>
#include <eedb/pg/model/system_info.h>
#include <eedb/pg/utils.hpp>
int64_t _global_root_uid;
constexpr eedb::db::category t_category;
namespace eedb::pg {
FactoryImpl::FactoryImpl(std::unique_ptr< Connection > connection) : _connection{std::move(connection)} {}
FactoryImpl::~FactoryImpl() {}
auto set_rootCategoryValues() {
return std::make_tuple( //
t_category.owner = app_root_id(),
t_category.status = 0, /// TODO set status "DETACHED"
t_category.parent_id = sqlpp::null,
t_category.name = "root",
t_category.description = "root category");
}
void FactoryImpl::init() {
_global_root_uid = [& db = (*_connection)]() {
UsersImpl users{db};
auto rootIdentity = std::make_unique< AuthIdentityConst >("root", "loginname");
auto rootUser = users.findWith(*rootIdentity);
if(rootUser) {
// root available, return created ID
return dynamic_cast< UserImpl * >(rootUser.get())->id();
} else {
// root user not found, create root user and return it's ID
auto authInfo = std::make_unique< AuthInfoConst >(
/// TODO get password from someware
Password{"bcrypt", "OM/Z1c4WBFXvwkxh", "$2y$07$RyytUhDhLDbAPjf0b0r2Y.dsg.FlQ7L.xzWHMmoelI81u0MfBrW7q"},
/// TODO get root user email
Email{"root@eedb.pl"});
auto root = UserImpl{db, std::move(authInfo), std::move(rootIdentity)};
return root.id();
}
}();
auto rootCategoryExists = (*_connection)( //
sqlpp::select( //
sqlpp::exists(select(t_category.id) //
.from(t_category)
.where(t_category.parent_path == "root"))))
.front()
.exists;
if(not rootCategoryExists) {
(*_connection)(insert_into(t_category).set(set_rootCategoryValues()));
}
}
std::unique_ptr< Users > FactoryImpl::usersRepository() const {
createRootUser();
return std::make_unique< UsersImpl >(*_connection);
}
@ -33,17 +82,6 @@ std::unique_ptr< Session > FactoryImpl::session() const {
return std::make_unique< SessionImpl >(*_connection);
}
void FactoryImpl::createRootUser() const {
_global_root_uid = (*_connection)(sqlpp::postgresql::insert_into(t_auth_info)
.set( //
t_auth_info.password_hash = "$2y$07$RyytUhDhLDbAPjf0b0r2Y.dsg.FlQ7L.xzWHMmoelI81u0MfBrW7q",
t_auth_info.password_method = "bcrypt",
t_auth_info.password_salt = "OM/Z1c4WBFXvwkxh",
t_auth_info.email = "root@eedb.pl", //
t_auth_info.status = 0)
.returning(t_auth_info.id))
.front()
.id;
}
void FactoryImpl::createRootUser() const {}
} // namespace eedb::pg

View File

@ -10,6 +10,36 @@
namespace eedb::pg {
namespace {
auto setPassword(const Password & pass) {
return std::make_tuple(
t_auth_info.password_hash = pass.value(), t_auth_info.password_method = pass.function(), t_auth_info.password_salt = pass.salt());
}
auto setEmail(const Email & email) {
return std::make_tuple(t_auth_info.email = std::string{email.address()});
}
auto setStatus() {
return std::make_tuple(t_auth_info.status = 0);
}
auto setAuthInfo(AuthInfo * authInfo) {
const auto & pass = authInfo->password();
const auto & email = authInfo->email();
return std::tuple_cat(setPassword(pass), setEmail(email), setStatus());
}
auto setAuthIdentity(const int64_t auth_info_id, AuthIdentity * authIdentity) {
return std::make_tuple( //
t_auth_identity.auth_info_id = auth_info_id,
t_auth_identity.identity = std::string{authIdentity->identity()},
t_auth_identity.provider = std::string{authIdentity->provider()});
}
} // namespace
struct UserImpl::UserImplPriv {
UserImplPriv(Connection & db, int uid, UserImpl * owner) : _db{db}, id{uid}, self{owner} {}
@ -48,6 +78,27 @@ struct UserImpl::UserImplPriv {
UserImpl::UserImpl(Connection & db, int uid) : _priv{spimpl::make_unique_impl< UserImplPriv >(db, uid, this)} {}
UserImpl::UserImpl(Connection & db, std::unique_ptr< AuthInfo > authInfo, std::unique_ptr< AuthIdentity > authIdentity)
: _priv{spimpl::make_unique_impl< UserImplPriv >(db, 0, this)} {
assert(authInfo);
assert(authIdentity);
namespace pg = sqlpp::postgresql;
auto transaction = sqlpp::start_transaction(db);
auto auth_info_id = db(pg::insert_into(t_auth_info) //
.set(setAuthInfo(authInfo.get()))
.returning(t_auth_info.id))
.front()
.id;
db(pg::insert_into(t_auth_identity) //
.set(setAuthIdentity(auth_info_id, authIdentity.get())));
transaction.commit();
_priv->id = auth_info_id;
}
const UserConfig & UserImpl::config() const {
return _priv->config;
}

View File

@ -82,20 +82,6 @@ struct UsersImpl::UsersImplPriv {
.on(t_auth_info.id == t_auth_token.auth_info_id);
}
// setters
auto setPassword(const Password & pass) const {
return std::make_tuple(
t_auth_info.password_hash = pass.value(), t_auth_info.password_method = pass.function(), t_auth_info.password_salt = pass.salt());
}
auto setEmail(const Email & email) const {
return std::make_tuple(t_auth_info.email = std::string{email.address()});
}
auto setStatus() const {
return std::make_tuple(t_auth_info.status = 0);
}
// find
auto by(const int64_t id) const {
return select(userData()) //
@ -127,20 +113,6 @@ struct UsersImpl::UsersImplPriv {
.where(authTokenEq(authToken) and statusIsValid());
}
auto setAuthInfo(AuthInfo * authInfo) const {
const auto & pass = authInfo->password();
const auto & email = authInfo->email();
return std::tuple_cat(setPassword(pass), setEmail(email), setStatus());
}
auto setAuthIdentity(const int64_t auth_info_id, AuthIdentity * authIdentity) {
return std::make_tuple( //
t_auth_identity.auth_info_id = auth_info_id,
t_auth_identity.identity = std::string{authIdentity->identity()},
t_auth_identity.provider = std::string{authIdentity->provider()});
}
public:
template < typename Key >
auto find(Key && key) -> std::unique_ptr< UserImpl > {
@ -153,20 +125,8 @@ struct UsersImpl::UsersImplPriv {
return std::make_unique< UserImpl >(_db, result.front().id);
}
auto add(AuthInfo * authInfo, AuthIdentity * authIdentity) {
namespace pg = sqlpp::postgresql;
/// TODO assert transaction
auto auth_info_id = _db(pg::insert_into(t_auth_info) //
.set(setAuthInfo(authInfo))
.returning(t_auth_info.id))
.front()
.id;
_db(pg::insert_into(t_auth_identity) //
.set(setAuthIdentity(auth_info_id, authIdentity)));
return std::make_unique< eedb::pg::UserImpl >(_db, auth_info_id);
auto add(std::unique_ptr< AuthInfo > authInfo, std::unique_ptr< AuthIdentity > authIdentity) {
return std::make_unique< UserImpl >(_db, std::move(authInfo), std::move(authIdentity));
}
void setDisabled( eedb::pg::UserImpl * u) const {
@ -197,10 +157,7 @@ unique_ptr< User > UsersImpl::findWith(const user::AuthTokenHash & token) const
}
unique_ptr< User > UsersImpl::addUser(unique_ptr< AuthInfo > authInfo, unique_ptr< AuthIdentity > authIdentity) {
assert(authInfo);
assert(authIdentity);
return _priv->add(authInfo.get(), authIdentity.get());
return _priv->add(std::move(authInfo), std::move(authIdentity));
}
void UsersImpl::removeUser(std::unique_ptr< User > user) const {

View File

@ -63,3 +63,18 @@ TEST_F(PgCategoryTest, addSameCategoryMultipleTimesShouldFail) {
EXPECT_THROW(
sut->addChild(std::make_unique< eedb::pg::CategoryImpl >(db(), "category", "asdf")), eedb::exceptions::CategoryAddException);
}
//TEST_F(PgCategoryTest, attachToParent) {
// auto cat = std::make_unique< eedb::pg::CategoryImpl >(db(), "category", "asdf");
// cat->attachTo(sut.get());
// EXPECT_TRUE(cat->parent()->parent());
//}
//TEST_F(PgCategoryTest, detachFromParent) {
// auto cat = std::make_unique< eedb::pg::CategoryImpl >(db(), "category", "asdf");
// cat->attachTo(sut.get());
// cat->detach();
// EXPECT_FALSE(cat->parent()->parent());
//}

View File

@ -72,8 +72,6 @@ class Category {
public:
virtual ~Category() = default;
virtual int foo() {}
/**
* @brief displayName
* @return category display name
@ -89,7 +87,7 @@ class Category {
/**
* @brief addChild
* @param category
* @throws
* @throws CategoryAddException()
* @return pointer to newely added category
*/
virtual Category * addChild(std::unique_ptr< Category > category) = 0;
@ -99,6 +97,17 @@ class Category {
* @return sequence of child categories
*/
virtual std::unique_ptr< CategoriesChildren > children() const = 0;
/**
* @brief detach category from it's parent
*/
virtual void detach() = 0;
/**
* @brief attachTo schould act the same as addChild
* @return a pointer to created category
*/
virtual Category * attachTo(Category *) = 0;
};
} // namespace eedb

View File

@ -13,6 +13,11 @@ class Factory {
public:
virtual ~Factory() = default;
/**
* @brief init all data structures and default values for data store, also update to newer version
*/
virtual void init() = 0;
virtual std::unique_ptr< Users > usersRepository() const = 0;
virtual std::unique_ptr< CategoriesRepository > categoriesRepository() const = 0;

View File

@ -8,6 +8,7 @@ class UserConfig {};
class AuthTokens;
class AuthInfo;
class AuthIdentities;
class AuthIdentity;
/**
* @brief The User class

View File

@ -16,5 +16,7 @@ class CategoryMock : public Category {
return doAddChild(c.get());
}
MOCK_CONST_METHOD0(children, std::unique_ptr< CategoriesChildren >());
MOCK_METHOD0(detach, void());
MOCK_METHOD1(attachTo, Category *( Category * ) );
};
} // namespace eedb

View File

@ -16,7 +16,9 @@ DefaultCategoriesTree::DefaultCategoriesTree(std::unique_ptr< CategoriesReposito
setSelectionMode(Wt::SelectionMode::Single);
auto node = std::make_unique< WTreeNode >(_root->displayName().data());
const auto displayName = _root->displayName();
auto node = std::make_unique< WTreeNode >(std::string{displayName.data()});
setTreeRoot(std::move(node));
treeRoot()->label()->setTextFormat(Wt::TextFormat::Plain);