diff --git a/src/app/main.cpp b/src/app/main.cpp index 5c9305c..49e7817 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -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); } diff --git a/src/libs/db/postgresql_connector/include/eedb/pg/Category.hpp b/src/libs/db/postgresql_connector/include/eedb/pg/Category.hpp index 2de646a..fb55801 100644 --- a/src/libs/db/postgresql_connector/include/eedb/pg/Category.hpp +++ b/src/libs/db/postgresql_connector/include/eedb/pg/Category.hpp @@ -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; diff --git a/src/libs/db/postgresql_connector/include/eedb/pg/Factory.hpp b/src/libs/db/postgresql_connector/include/eedb/pg/Factory.hpp index 82e1b39..6cabf41 100644 --- a/src/libs/db/postgresql_connector/include/eedb/pg/Factory.hpp +++ b/src/libs/db/postgresql_connector/include/eedb/pg/Factory.hpp @@ -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; diff --git a/src/libs/db/postgresql_connector/include/eedb/pg/User.hpp b/src/libs/db/postgresql_connector/include/eedb/pg/User.hpp index b609e28..540b995 100644 --- a/src/libs/db/postgresql_connector/include/eedb/pg/User.hpp +++ b/src/libs/db/postgresql_connector/include/eedb/pg/User.hpp @@ -7,6 +7,7 @@ #include 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 diff --git a/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/CategoryMock.hpp b/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/CategoryMock.hpp index 7e5d66d..87b4491 100644 --- a/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/CategoryMock.hpp +++ b/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/CategoryMock.hpp @@ -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(); diff --git a/src/libs/db/postgresql_connector/src/Category.cpp b/src/libs/db/postgresql_connector/src/Category.cpp index 12d5bd8..9e7d44f 100644 --- a/src/libs/db/postgresql_connector/src/Category.cpp +++ b/src/libs/db/postgresql_connector/src/Category.cpp @@ -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(); } diff --git a/src/libs/db/postgresql_connector/src/Factory.cpp b/src/libs/db/postgresql_connector/src/Factory.cpp index cc64f14..0f8d2c0 100644 --- a/src/libs/db/postgresql_connector/src/Factory.cpp +++ b/src/libs/db/postgresql_connector/src/Factory.cpp @@ -1,23 +1,72 @@ #include +#include +#include + #include #include #include +#include #include #include -#include +#include +#include #include 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 diff --git a/src/libs/db/postgresql_connector/src/User.cpp b/src/libs/db/postgresql_connector/src/User.cpp index df4ad66..3d51706 100644 --- a/src/libs/db/postgresql_connector/src/User.cpp +++ b/src/libs/db/postgresql_connector/src/User.cpp @@ -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; } diff --git a/src/libs/db/postgresql_connector/src/Users.cpp b/src/libs/db/postgresql_connector/src/Users.cpp index 55d8bd9..14f9ec0 100644 --- a/src/libs/db/postgresql_connector/src/Users.cpp +++ b/src/libs/db/postgresql_connector/src/Users.cpp @@ -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 { diff --git a/src/libs/db/postgresql_connector/test/test_Category.cpp b/src/libs/db/postgresql_connector/test/test_Category.cpp index 12af86f..760bf3b 100644 --- a/src/libs/db/postgresql_connector/test/test_Category.cpp +++ b/src/libs/db/postgresql_connector/test/test_Category.cpp @@ -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()); +//} diff --git a/src/libs/eedb/include/eedb/Category.hpp b/src/libs/eedb/include/eedb/Category.hpp index ab73d2e..4346c2a 100644 --- a/src/libs/eedb/include/eedb/Category.hpp +++ b/src/libs/eedb/include/eedb/Category.hpp @@ -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 diff --git a/src/libs/eedb/include/eedb/Factory.hpp b/src/libs/eedb/include/eedb/Factory.hpp index 2a00d25..9dcb640 100644 --- a/src/libs/eedb/include/eedb/Factory.hpp +++ b/src/libs/eedb/include/eedb/Factory.hpp @@ -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; diff --git a/src/libs/eedb/include/eedb/User.hpp b/src/libs/eedb/include/eedb/User.hpp index 11f22ba..bc6a408 100644 --- a/src/libs/eedb/include/eedb/User.hpp +++ b/src/libs/eedb/include/eedb/User.hpp @@ -8,6 +8,7 @@ class UserConfig {}; class AuthTokens; class AuthInfo; class AuthIdentities; +class AuthIdentity; /** * @brief The User class diff --git a/src/libs/webapp/mock/include/eedb/mock/CategoryMock.hpp b/src/libs/webapp/mock/include/eedb/mock/CategoryMock.hpp index 095b5b4..d82ca73 100644 --- a/src/libs/webapp/mock/include/eedb/mock/CategoryMock.hpp +++ b/src/libs/webapp/mock/include/eedb/mock/CategoryMock.hpp @@ -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 diff --git a/src/libs/webapp/src/DefaultCategoryTree.cpp b/src/libs/webapp/src/DefaultCategoryTree.cpp index 6e1a1c8..011e3df 100644 --- a/src/libs/webapp/src/DefaultCategoryTree.cpp +++ b/src/libs/webapp/src/DefaultCategoryTree.cpp @@ -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);