diff --git a/CMakeLists.txt b/CMakeLists.txt index 45c8100..ca7f81a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,8 +3,8 @@ cmake_minimum_required(VERSION 3.8) include(cmake/HunterGate.cmake) HunterGate( - URL "https://github.com/ruslo/hunter/archive/v0.19.232.tar.gz" - SHA1 "a412c45fe4c5a72fed386f62dd8d753bd4fd3d11" + URL "https://github.com/ruslo/hunter/archive/v0.23.1.tar.gz" + SHA1 "51d2d6be411251c8de18c4ca20ef778880cf4cce" ) project(eedb) @@ -27,13 +27,13 @@ list(APPEND CMAKE_PREFIX_PATH "${CMAKE_BINARY_DIR}/local/lib") link_directories(${CMAKE_BINARY_DIR}/local/lib) list(APPEND CMAKE_INSTALL_RPATH "${HUNTER_INSTALL_PREFIX}/lib") +link_directories("${HUNTER_INSTALL_PREFIX}/lib") list(APPEND CMAKE_INSTALL_RPATH "${CMAKE_BINARY_DIR}/local/lib") set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) set(Sqlpp11_DIR "${CMAKE_BINARY_DIR}/local/lib/cmake/Sqlpp11") set(wt_DIR "${CMAKE_BINARY_DIR}/local/lib/cmake/wt") set(Sqlpp-connector-postgresql_DIR "${CMAKE_BINARY_DIR}/local/lib/cmake/sqlpp-connector-postgresql") -set(HinnantDate_ROOT_DIR "${CMAKE_BINARY_DIR}/local/include/") find_program( CLANG_TIDY_EXE diff --git a/cmake/FindAllRequirements.cmake b/cmake/FindAllRequirements.cmake index 708c265..1918555 100644 --- a/cmake/FindAllRequirements.cmake +++ b/cmake/FindAllRequirements.cmake @@ -19,6 +19,8 @@ find_package(spdlog CONFIG REQUIRED) #hunter_add_package(range-v3) #find_package(range-v3 CONFIG REQUIRED) +hunter_add_package(date) +find_package(date CONFIG REQUIRED) hunter_add_package(bison) hunter_add_package(flex) @@ -32,18 +34,18 @@ execute_process( COMMAND ${INSTALL_DEPS_SCRIPT} "${CMAKE_COMMAND}" "${CMAKE_GENERATOR}" - ${CMAKE_CXX_COMPILER} - ${CMAKE_C_COMPILER} - ${CMAKE_PREFIX_PATH} - ${Boost_LIBRARY_DIR_DEBUG} - ${CMAKE_BINARY_DIR}/local - ${CMAKE_BUILD_TYPE} + "${CMAKE_CXX_COMPILER}" + "${CMAKE_C_COMPILER}" + "${CMAKE_PREFIX_PATH}" + "${Boost_LIBRARY_DIR_DEBUG}" + "${CMAKE_BINARY_DIR}/local" + "${CMAKE_BUILD_TYPE}" RESULT_VARIABLE rv OUTPUT_VARIABLE ov - ERROR_VARIABLE ev + ERROR_VARIABLE ov WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/_3rdParty ) message("rv='${rv}'") message("ov='${ov}'") -message("ev='${ev}'") +#message("ev='${ev}'") diff --git a/cmake/install_deps.sh b/cmake/install_deps.sh index d7e18e6..e512ca2 100755 --- a/cmake/install_deps.sh +++ b/cmake/install_deps.sh @@ -12,12 +12,6 @@ if [ ! -d ${CMAKE_INSTALL_PREFIX} ]; then fi # clone all -if [ ! -d date/.git ]; then - git clone --depth=1 https://github.com/HowardHinnant/date.git -else - cd date; git pull; cd ../ -fi - if [ ! -d sqlpp11/.git ]; then git clone -b develop --depth=1 https://github.com/rbock/sqlpp11.git else @@ -42,37 +36,31 @@ else cd ChaiScript; git pull; cd ../ fi +echo ${CMAKE_PREFIX_PATH} + # install all - -mkdir date-build; cd date-build -${CMAKE_COMMAND} -G"${CMAKE_GENERATOR}" ../date\ - -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}\ - -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} \ - -DCMAKE_INSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}" -${CMAKE_COMMAND} --build . --target install -cd ../ - -mkdir sqlpp11-build; cd sqlpp11-build +mkdir sqlpp11-build -p; cd sqlpp11-build ${CMAKE_COMMAND} -G"${CMAKE_GENERATOR}" ../sqlpp11\ -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}\ -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}\ -DCMAKE_INSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}"\ - -DENABLE_TESTS=FALSE -${CMAKE_COMMAND} --build . --target install -cd ../ - -mkdir sqlpp11-connector-postgresql-build; cd sqlpp11-connector-postgresql-build -${CMAKE_COMMAND} -G"${CMAKE_GENERATOR}" ../sqlpp11-connector-postgresql\ - -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}\ - -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}\ - -DCMAKE_INSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}"\ - -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}"\ -DCMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH}"\ -DENABLE_TESTS=FALSE ${CMAKE_COMMAND} --build . --target install cd ../ -mkdir wt-build; cd wt-build +mkdir sqlpp11-connector-postgresql-build -p; cd sqlpp11-connector-postgresql-build +${CMAKE_COMMAND} -G"${CMAKE_GENERATOR}" ../sqlpp11-connector-postgresql\ + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}\ + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}\ + -DCMAKE_INSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}"\ + -DCMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH}"\ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}"\ + -DENABLE_TESTS=FALSE +${CMAKE_COMMAND} --build . --target install +cd ../ + +mkdir wt-build -p; cd wt-build ${CMAKE_COMMAND} -G"${CMAKE_GENERATOR}" ../wt\ -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}\ -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}\ @@ -100,7 +88,7 @@ ${CMAKE_COMMAND} -G"${CMAKE_GENERATOR}" ../wt\ ${CMAKE_COMMAND} --build . --target install cd ../ -mkdir ChaiScript-build; cd ChaiScript-build +mkdir ChaiScript-build -p; cd ChaiScript-build ${CMAKE_COMMAND} -G"${CMAKE_GENERATOR}" ../ChaiScript\ -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}\ -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}\ diff --git a/sql/schema.sql b/sql/schema.sql index c21de32..bcda6d1 100644 --- a/sql/schema.sql +++ b/sql/schema.sql @@ -4,7 +4,6 @@ GRANT ALL ON SCHEMA public TO postgres; GRANT ALL ON SCHEMA public TO public; create extension IF NOT EXISTS "ltree"; -create extension if not exists "uuid-ossp"; create OR REPLACE function perm_to_numeric ( m_owner INT, m_group INT, m_other INT ) RETURNS INT AS $$ @@ -31,6 +30,15 @@ end $$ --$$ language plpgsql stable cost 1; +create or replace function app_root_id() +returns int as $$ +begin + -- return current_setting('eedb.root_user.id', TRUE)::int; + return 0; +end $$ --$$ +language plpgsql stable cost 1; + + create or replace function app_current_user_group() returns int as $$ begin @@ -327,7 +335,7 @@ create table "category"( constraint "fk_category_stat_owner" foreign key ("owner") references "auth_info"("id") deferrable initially immediate ) inherits (stat); create index "ix_category_parent_path" on "category" using GIST ("parent_path"); -create unique index "ux_category_name" on "category"("parent_id", "name" ); +create unique index "ux_category_name" on "category"("parent_id", "name" ) where ("parent_id" is not null); create trigger "update_category_last_update" before update on "category" for each row execute procedure last_update_column(); create trigger "update_category_parent_path" before insert or update on "category" for each row execute procedure update_category_parent_path(); comment on table "category" diff --git a/src/app/main.cpp b/src/app/main.cpp index fbfb5ab..5c9305c 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -13,7 +13,7 @@ static auto _createSinks() { std::vector< spdlog::sink_ptr > sinks; auto stdout_sink = spdlog::sinks::stdout_sink_mt::instance(); - sinks.emplace_back(std::make_shared< spdlog::sinks::ansicolor_sink >(stdout_sink)); + // sinks.emplace_back(std::make_shared< spdlog::sinks::ansicolor_sink >(stdout_sink)); sinks.emplace_back(std::make_shared< spdlog::sinks::daily_file_sink_st >("logfile", 23, 59)); return sinks; } diff --git a/src/libs/db/postgresql_connector/include/eedb/pg/Categories.hpp b/src/libs/db/postgresql_connector/include/eedb/pg/Categories.hpp deleted file mode 100644 index 4a4b76c..0000000 --- a/src/libs/db/postgresql_connector/include/eedb/pg/Categories.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include - -#include - -namespace eedb::pg { -class Connection; - -class CategoriesImpl : public CategoriesChildren { - // Categories interface - public: - CategoriesImpl(std::unique_ptr< std::vector< std::unique_ptr< eedb::Category > > > data); - - private: - struct CategoriesImplPriv; - spimpl::unique_impl_ptr< CategoriesImplPriv > _priv; -}; -} // namespace eedb diff --git a/src/libs/db/postgresql_connector/include/eedb/pg/CategoriesRepository.hpp b/src/libs/db/postgresql_connector/include/eedb/pg/CategoriesRepository.hpp index 4d6b9ca..6e51fda 100644 --- a/src/libs/db/postgresql_connector/include/eedb/pg/CategoriesRepository.hpp +++ b/src/libs/db/postgresql_connector/include/eedb/pg/CategoriesRepository.hpp @@ -4,9 +4,10 @@ #include -namespace eedb{ +namespace eedb { class User; -} +class Category; +} // namespace eedb namespace eedb::pg { class Connection; @@ -15,12 +16,14 @@ class CategoriesRepositoryImpl : public CategoriesRepository { public: // CategoriesRepository interface public: - CategoriesRepositoryImpl(Connection &db); + CategoriesRepositoryImpl(Connection & db); std::unique_ptr< Category > root() const override; + std::unique_ptr< Category > create(std::string name, std::string description) const override; + private: struct CategoriesRepositoryImplPriv; spimpl::unique_impl_ptr< CategoriesRepositoryImplPriv > _priv; }; -} // namespace eedb +} // namespace eedb::pg 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 2a3726a..2de646a 100644 --- a/src/libs/db/postgresql_connector/include/eedb/pg/Category.hpp +++ b/src/libs/db/postgresql_connector/include/eedb/pg/Category.hpp @@ -21,30 +21,40 @@ namespace impl { * @return id of object in database */ virtual int64_t id() const = 0; + + virtual void setParent(PgCategory * parent) = 0; }; } // namespace impl class Connection; class CategoryImpl : public impl::PgCategory { - private: + public: + /// TODO should be private struct CategoryImplPrivate; - spimpl::impl_ptr< CategoryImplPrivate > _priv; + spimpl::unique_impl_ptr< CategoryImplPrivate > _priv; public: + // ROOT category CategoryImpl(Connection & db); - CategoryImpl(spimpl::impl_ptr< CategoryImplPrivate > priv); + + // creates new category + CategoryImpl(Connection & db, std::string name, std::string description); + + // creates category from private data + CategoryImpl(spimpl::unique_impl_ptr< CategoryImplPrivate > && priv); string_view displayName() const override; Category * parent() const override; - std::unique_ptr< CategoriesChildren > children() const override; + Category * addChild(std::unique_ptr< Category > category) noexcept(false) override; - std::unique_ptr< Category > create(std::string name, std::string description) const override; + std::unique_ptr< CategoriesChildren > children() const override; public: int64_t id() const override; + void setParent(PgCategory * parent) override; }; } // namespace eedb::pg 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 7e4a371..82e1b39 100644 --- a/src/libs/db/postgresql_connector/include/eedb/pg/Factory.hpp +++ b/src/libs/db/postgresql_connector/include/eedb/pg/Factory.hpp @@ -21,5 +21,7 @@ class FactoryImpl : public Factory { private: std::unique_ptr< Connection > _connection; + + void createRootUser() const; }; } // namespace eedb::pg diff --git a/src/libs/db/postgresql_connector/include/eedb/pg/Stats.hpp b/src/libs/db/postgresql_connector/include/eedb/pg/Stats.hpp index 1d26a5d..f003fba 100644 --- a/src/libs/db/postgresql_connector/include/eedb/pg/Stats.hpp +++ b/src/libs/db/postgresql_connector/include/eedb/pg/Stats.hpp @@ -4,15 +4,21 @@ #include -namespace eedb::pg::details { +#include -inline auto app_current_user_id() { - return sqlpp::verbatim< sqlpp::integer >("app_current_user_id()"); -} +namespace eedb::pg::details { template < typename Table > auto insert_user_stat(const Table & table) { - return std::tuple{table.owner = app_current_user_id()}; + return std::tuple{// + table.owner = app_current_user_id()}; +} + +template < typename Table > +inline auto set_stat(){ + constexpr Table table; + return std::tuple{// + table.owner = app_current_user_id()}; } template < typename Table > diff --git a/src/libs/db/postgresql_connector/include/eedb/pg/utils.hpp b/src/libs/db/postgresql_connector/include/eedb/pg/utils.hpp new file mode 100644 index 0000000..7804174 --- /dev/null +++ b/src/libs/db/postgresql_connector/include/eedb/pg/utils.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +extern int64_t _global_root_uid; + +namespace eedb::pg { + +inline auto app_current_user_id() { + return sqlpp::verbatim< sqlpp::integer >("app_current_user_id()"); +} + +inline auto app_root_id() { + return _global_root_uid; +} + +} // namespace eedb::pg diff --git a/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/CategoriesRepositoryMock.hpp b/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/CategoriesRepositoryMock.hpp new file mode 100644 index 0000000..4744cf9 --- /dev/null +++ b/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/CategoriesRepositoryMock.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include +#include +#include +#include + +#include +#include + +namespace eedb::pg { + +class CategoriesRepositoryMock : public ::eedb::CategoriesRepository { + public: + /// TODO chenge to transaction + CategoriesRepositoryMock(Connection & db, User & user) : _db{db}, _owner{user} {} + + void _init() {} + + void _expect_create_category() {} + + private: + Connection & _db; + User & _owner; + + // CategoriesRepository interface + public: + MOCK_CONST_METHOD0(root, std::unique_ptr< Category >()); + MOCK_CONST_METHOD2(create, std::unique_ptr< Category >(std::string name, std::string description)); +}; + +} // 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 157578a..7e5d66d 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 @@ -10,6 +10,8 @@ #include #include +#include + namespace eedb::pg { class CategoryMock : public impl::PgCategory { @@ -33,9 +35,13 @@ class CategoryMock : public impl::PgCategory { MOCK_CONST_METHOD0(displayName, std::string_view()); MOCK_CONST_METHOD0(path, std::string_view()); MOCK_CONST_METHOD0(parent, Category *()); - MOCK_CONST_METHOD2(create, std::unique_ptr< Category >(std::string, std::string)); + MOCK_METHOD1(doAddChild, Category *( Category * ) ); + Category * addChild(std::unique_ptr< Category > c) { + return doAddChild(c.get()); + } MOCK_CONST_METHOD0(children, std::unique_ptr< CategoriesChildren >()); MOCK_CONST_METHOD0(id, int64_t()); + MOCK_METHOD1(setParent, void(impl::PgCategory *)); void _init_simple(std::string name) { _root_guard(); @@ -47,7 +53,7 @@ class CategoryMock : public impl::PgCategory { const auto & row = _db(sqlpp::postgresql::insert_into(t_category) // .set(t_category.name = "root", // t_category.parent_id = sqlpp::null, - t_category.owner = sqlpp::verbatim< sqlpp::integer >(" app_current_user_id() ")) + t_category.owner = app_current_user_id()) .returning(t_category.id, t_category.parent_path)) .front(); diff --git a/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/UserMock.hpp b/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/UserMock.hpp index 6802ff2..d7c4353 100644 --- a/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/UserMock.hpp +++ b/src/libs/db/postgresql_connector/mock/include/eedb/mock/db/pg/UserMock.hpp @@ -17,6 +17,10 @@ #include +#include + +extern int64_t _global_root_uid; + namespace eedb::pg { class UserMock : public ::eedb::pg::impl::UserImplPriv { @@ -34,6 +38,17 @@ class UserMock : public ::eedb::pg::impl::UserImplPriv { void _init() { using namespace testing; + _global_root_uid = _db(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; + _id = _db(sqlpp::postgresql::insert_into(t_auth_info) .set( // t_auth_info.password_hash = "$2y$07$RyytUhDhLDbAPjf0b0r2Y.dsg.FlQ7L.xzWHMmoelI81u0MfBrW7q", diff --git a/src/libs/db/postgresql_connector/src/Categories.cpp b/src/libs/db/postgresql_connector/src/Categories.cpp deleted file mode 100644 index 84d05aa..0000000 --- a/src/libs/db/postgresql_connector/src/Categories.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "eedb/pg/Categories.hpp" - -#include - -#include - -namespace eedb::pg { -struct CategoriesImpl::CategoriesImplPriv { - public: - static auto getTransform() { - return [](auto & cat) { return cat.get(); }; - } - - explicit CategoriesImplPriv(std::unique_ptr< std::vector< std::unique_ptr< Category > > > data) : _cache{std::move(data)} {} - - private: - std::unique_ptr< std::vector< std::unique_ptr< Category > > > _cache; -}; - -CategoriesImpl::CategoriesImpl(std::unique_ptr< std::vector< std::unique_ptr< Category > > > data) - : eedb::CategoriesChildren(boost::make_transform_iterator(data->begin(), CategoriesImplPriv::getTransform()), - boost::make_transform_iterator(data->end(), CategoriesImplPriv::getTransform())), - _priv{spimpl::make_unique_impl< CategoriesImplPriv >(std::move(data))} {} - -} // namespace eedb::pg diff --git a/src/libs/db/postgresql_connector/src/CategoriesRepository.cpp b/src/libs/db/postgresql_connector/src/CategoriesRepository.cpp index e5d85af..99c9540 100644 --- a/src/libs/db/postgresql_connector/src/CategoriesRepository.cpp +++ b/src/libs/db/postgresql_connector/src/CategoriesRepository.cpp @@ -1,9 +1,7 @@ #include #include - -#include -#include +#include namespace eedb::pg { struct CategoriesRepositoryImpl::CategoriesRepositoryImplPriv { @@ -13,12 +11,20 @@ struct CategoriesRepositoryImpl::CategoriesRepositoryImplPriv { return std::make_unique< eedb::pg::CategoryImpl >(_db); } + auto create(std::string name, std::string description) const { + return std::make_unique< pg::CategoryImpl >(_db, std::move(name), std::move(description)); + } + private: Connection & _db; }; CategoriesRepositoryImpl::CategoriesRepositoryImpl(Connection & db) : _priv{spimpl::make_unique_impl< CategoriesRepositoryImplPriv >(db)} {} +std::unique_ptr< Category > CategoriesRepositoryImpl::create(std::string name, std::string description) const { + return _priv->create(std::move(name), std::move(description)); +} + std::unique_ptr< Category > CategoriesRepositoryImpl::root() const { // return _priv->root(); } diff --git a/src/libs/db/postgresql_connector/src/Category.cpp b/src/libs/db/postgresql_connector/src/Category.cpp index d58af48..12d5bd8 100644 --- a/src/libs/db/postgresql_connector/src/Category.cpp +++ b/src/libs/db/postgresql_connector/src/Category.cpp @@ -1,18 +1,21 @@ #include -#include -#include -#include +#include #include -#include +#include #include +#include #include +#include +#include +#include + +#include namespace eedb::pg { - struct CategoryImpl::CategoryImplPrivate { private: // select @@ -28,6 +31,14 @@ struct CategoryImpl::CategoryImplPrivate { return std::tuple_cat(stat_columns(), category_columns()); } + auto cast(Category * category) const { + return dynamic_cast< CategoryImpl * >(category); + } + + auto cast(std::unique_ptr< Category > & category) const { + return cast(category.get()); + } + // from auto table_list() const { return t_category; @@ -42,55 +53,111 @@ struct CategoryImpl::CategoryImplPrivate { return t_category.parent_path == "root"; } - public: - Category * _self{nullptr}; + // set + auto set_nameAndDescription(std::string name, std::string description) const { + return std::tuple{t_category.name = std::move(name), t_category.description = std::move(description)}; + } - CategoryImplPrivate(Connection & db, Category * parent) : _db{db}, _parent{parent} { + auto set_noParent() const { + return std::tuple{t_category.parent_id = sqlpp::null}; + } + + auto set_rootCategoryValues() const { + 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"); + } + + auto set_newCategoryValues(std::string name, std::string description) const { + return std::tuple_cat( // + set_nameAndDescription(std::move(name), std::move(description)), + set_noParent(), + details::set_stat< eedb::db::category >()); + } + + auto create(std::string name, std::string description) { + const auto & row = _db(sqlpp::postgresql::insert_into(t_category) + .set(set_newCategoryValues(std::move(name), std::move(description))) + .returning(columns())) + .front(); + init_data(row); + } + + 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()) { - // no root category - row = _db(sqlpp::postgresql::insert_into(t_category) - .set( // - t_category.owner = sqlpp::verbatim< sqlpp::integer >("app_current_user_id()"), - t_category.status = 0, - t_category.parent_id = sqlpp::null, - t_category.name = "root", - t_category.description = "root category") // - .returning(columns())); + row = _db(sqlpp::postgresql::insert_into(t_category).set(set_rootCategoryValues()).returning(columns())); } init_data(row.front()); } + CategoryImplPrivate(Connection & db, std::string name, std::string description) : _db{db}, _parent{nullptr} { + create(std::move(name), std::move(description)); + } + auto categories() const { - auto children = std::make_unique< std::vector< std::unique_ptr< Category > > >(); - children->reserve(100); - for(auto & category_row : _db(select(columns()).from(table_list()).where(parent_match()))) { - auto priv = spimpl::make_impl< CategoryImplPrivate >(_db, _self); - priv->init_data(category_row); - children->emplace_back(std::make_unique< CategoryImpl >(std::move(priv))); + auto getTransform = []() { return [](auto & cat) { return cat.get(); }; }; + + return std::make_unique< CategoriesChildren >( // + boost::make_transform_iterator(_children.begin(), getTransform()), + boost::make_transform_iterator(_children.end(), getTransform())); + } + + void setParent(impl::PgCategory * parent) { + _parent = parent; + } + + 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 } - return children; + + 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 { return _parent == nullptr && _path != "root"; } - auto create(std::string name, std::string description) const { - const auto & row = _db(sqlpp::postgresql::insert_into(t_category) - .set(t_category.owner = sqlpp::verbatim< sqlpp::integer >("app_current_user_id()"), - t_category.status = 0, - t_category.parent_id = _uid, - t_category.name = std::move(name), - t_category.description = std::move(description)) // - .returning(columns())) - .front(); - auto priv = spimpl::make_impl< CategoryImplPrivate >(_db, _self); - priv->init_data(row); - return std::make_unique< CategoryImpl >(std::move(priv)); - } - auto parent() const { return _parent; } @@ -107,7 +174,7 @@ struct CategoryImpl::CategoryImplPrivate { return std::string_view{_path}; } - protected: + public: template < typename Row > void init_data(Row & row) { _uid = row.id; @@ -118,19 +185,27 @@ struct CategoryImpl::CategoryImplPrivate { private: Connection & _db; + impl::PgCategory * _parent{nullptr}; int64_t _uid{}; - Category * _parent{nullptr}; + std::vector< std::unique_ptr< Category > > _children; std::string _name; std::string _path; std::string _description; }; -CategoryImpl::CategoryImpl(Connection & db) : _priv{spimpl::make_impl< CategoryImplPrivate >(db, nullptr)} { +CategoryImpl::CategoryImpl(Connection & db) // + : _priv{spimpl::make_unique_impl< CategoryImplPrivate >(db, nullptr)} { _priv->_self = this; } -CategoryImpl::CategoryImpl(spimpl::impl_ptr< CategoryImplPrivate > priv) : _priv{std::move(priv)} { +CategoryImpl::CategoryImpl(Connection & db, std::string name, std::string description) + : _priv{spimpl::make_unique_impl< CategoryImplPrivate >(db, std::move(name), std::move(description))} { + _priv->_self = this; +} + +CategoryImpl::CategoryImpl(spimpl::unique_impl_ptr< CategoryImplPrivate > && priv) // + : _priv{std::move(priv)} { _priv->_self = this; } @@ -142,16 +217,19 @@ Category * CategoryImpl::parent() const { return _priv->parent(); } -std::unique_ptr< CategoriesChildren > CategoryImpl::children() const { - return std::make_unique< CategoriesImpl >(_priv->categories()); +Category * CategoryImpl::addChild(std::unique_ptr< Category > category) noexcept(false) { + return _priv->addChild(std::move(category)); } -std::unique_ptr< Category > CategoryImpl::create(std::string name, std::string description) const { - return _priv->create(std::move(name), std::move(description)); +std::unique_ptr< CategoriesChildren > CategoryImpl::children() const { + return _priv->categories(); } int64_t CategoryImpl::id() const { return _priv->id(); } +void CategoryImpl::setParent(impl::PgCategory * parent) { + _priv->setParent(parent); +} } // namespace eedb::pg diff --git a/src/libs/db/postgresql_connector/src/Factory.cpp b/src/libs/db/postgresql_connector/src/Factory.cpp index 73a3057..cc64f14 100644 --- a/src/libs/db/postgresql_connector/src/Factory.cpp +++ b/src/libs/db/postgresql_connector/src/Factory.cpp @@ -5,13 +5,19 @@ #include #include #include +#include +#include + +int64_t _global_root_uid; namespace eedb::pg { + FactoryImpl::FactoryImpl(std::unique_ptr< Connection > connection) : _connection{std::move(connection)} {} FactoryImpl::~FactoryImpl() {} std::unique_ptr< Users > FactoryImpl::usersRepository() const { + createRootUser(); return std::make_unique< UsersImpl >(*_connection); } @@ -27,4 +33,17 @@ 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; +} + } // namespace eedb::pg diff --git a/src/libs/db/postgresql_connector/test/DbTestBase.hpp b/src/libs/db/postgresql_connector/test/DbTestBase.hpp index 883992c..90f69c8 100644 --- a/src/libs/db/postgresql_connector/test/DbTestBase.hpp +++ b/src/libs/db/postgresql_connector/test/DbTestBase.hpp @@ -117,7 +117,7 @@ class DbTestBase : public testing::Test { static void SetUpTestCase() { _test_db = std::make_unique< PgTestDatabasePrepare >(); _test_db->db().execute("SET synchronous_commit=off;"); - eedb::pg::exec_file(_test_db->db(), "/home/bwieczor/src/eedb/sql/schema.sql"); + eedb::pg::exec_file(_test_db->db(), "/home/wieczbar/src/eedb/sql/schema.sql"); } static void TearDownTestCase() { diff --git a/src/libs/db/postgresql_connector/test/test_Categories.cpp b/src/libs/db/postgresql_connector/test/test_Categories.cpp deleted file mode 100644 index 23f2f6e..0000000 --- a/src/libs/db/postgresql_connector/test/test_Categories.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include - -#include "DbTestBase.hpp" - -#include -#include - -class PgCategoriesTest : public DbTestBase< PgCategoriesTest > { - public: - PgCategoriesTest() { - // sut = std::make_unique< eedb::pg::CategoriesImpl >(_categories); - } - - protected: - // std::vector< UniquePtrMockWrapper< eedb::db:: > > _categories; - - std::unique_ptr< eedb::pg::CategoriesImpl > sut; -}; - -template <> -std::unique_ptr< PgTestDatabasePrepare > DbTestBase< PgCategoriesTest >::_test_db = {}; - -// TEST_F(PgCategoriesTest, size) { -// EXPECT_EQ(sut->size(), 4); -//} - -// TEST_F(PgCategoriesTest, ) diff --git a/src/libs/db/postgresql_connector/test/test_CategoriesRepository.cpp b/src/libs/db/postgresql_connector/test/test_CategoriesRepository.cpp index efc4fc1..cb143fe 100644 --- a/src/libs/db/postgresql_connector/test/test_CategoriesRepository.cpp +++ b/src/libs/db/postgresql_connector/test/test_CategoriesRepository.cpp @@ -23,6 +23,11 @@ std::unique_ptr< PgTestDatabasePrepare > DbTestBase< PgCategoriesRepositoryTest using namespace testing; +TEST_F(PgCategoriesRepositoryTest, createMultipleChildren) { + auto child1 = sut->create("child", "desc"); + auto child2 = sut->create("child", "desc"); +} + TEST_F(PgCategoriesRepositoryTest, rootNotNull) { auto root = sut->root(); ASSERT_TRUE(root); diff --git a/src/libs/db/postgresql_connector/test/test_Category.cpp b/src/libs/db/postgresql_connector/test/test_Category.cpp index 3d611ca..12af86f 100644 --- a/src/libs/db/postgresql_connector/test/test_Category.cpp +++ b/src/libs/db/postgresql_connector/test/test_Category.cpp @@ -2,11 +2,12 @@ #include "DbTestBase.hpp" +#include #include class PgCategoryTest : public DbTestBase< PgCategoryTest > { public: - PgCategoryTest() : user{db()} { + PgCategoryTest() : user{db()}, categoriesRepository{db(), user} { user._init(); sut = std::make_unique< eedb::pg::CategoryImpl >(db()); } @@ -19,6 +20,8 @@ class PgCategoryTest : public DbTestBase< PgCategoryTest > { protected: eedb::pg::UserMock user; + eedb::pg::CategoriesRepositoryMock categoriesRepository; + std::unique_ptr< eedb::pg::CategoryImpl > sut; }; @@ -32,30 +35,31 @@ TEST_F(PgCategoryTest, rootCategoryHasNoParent) { } TEST_F(PgCategoryTest, id) { - auto category = sut->create("name", "description"); - EXPECT_NE(dynamic_cast< eedb::pg::CategoryImpl * >(category.get())->id(), 0); + EXPECT_NE(dynamic_cast< eedb::pg::CategoryImpl * >(sut.get())->id(), 0); } -TEST_F(PgCategoryTest, createChild) { - sut->create("name", "description"); +TEST_F(PgCategoryTest, createCategory) { + auto category = std::make_unique< eedb::pg::CategoryImpl >(db(), "category", "asdf"); + EXPECT_NE(dynamic_cast< eedb::pg::CategoryImpl * >(category.get())->id(), 0); + EXPECT_FALSE(category->parent()); +} + +TEST_F(PgCategoryTest, addCategory) { + auto category = sut->addChild(std::make_unique< eedb::pg::CategoryImpl >(db(), "category", "asdf")); + ASSERT_TRUE(category); + EXPECT_NE(dynamic_cast< eedb::pg::CategoryImpl * >(category)->id(), 0); + EXPECT_TRUE(category->parent()); +} + +TEST_F(PgCategoryTest, getChildren) { + sut->addChild(std::make_unique< eedb::pg::CategoryImpl >(db(), "category", "asdf")); auto children = sut->children(); ASSERT_TRUE(children); EXPECT_EQ(std::distance(children->begin(), children->end()), 1); } -TEST_F(PgCategoryTest, getChildren) { - auto children = sut->children(); - ASSERT_TRUE(children); - EXPECT_EQ(std::distance(children->begin(), children->end()), 0); -} - -TEST_F(PgCategoryTest, childHasParent) { - sut->create("name1", "description"); - sut->create("name2", "description"); - - auto children = sut->children(); - EXPECT_EQ(std::distance(children->begin(), children->end()), 2); - for(auto child : *children) { - EXPECT_EQ(child->parent(), sut.get()); - } +TEST_F(PgCategoryTest, addSameCategoryMultipleTimesShouldFail) { + sut->addChild(std::make_unique< eedb::pg::CategoryImpl >(db(), "category", "asdf")); + EXPECT_THROW( + sut->addChild(std::make_unique< eedb::pg::CategoryImpl >(db(), "category", "asdf")), eedb::exceptions::CategoryAddException); } diff --git a/src/libs/db/postgresql_connector/test/test_ParametersRepository.cpp b/src/libs/db/postgresql_connector/test/test_ParametersRepository.cpp index 848f4aa..1482d86 100644 --- a/src/libs/db/postgresql_connector/test/test_ParametersRepository.cpp +++ b/src/libs/db/postgresql_connector/test/test_ParametersRepository.cpp @@ -35,4 +35,6 @@ TEST_F(PgParametersRepositoryTest, createWillNotThrow) { // EXPECT_NO_THROW(sut->create("name", "description")); } -TEST_F(PgParametersRepositoryTest, createSameThrowsException) {} +TEST_F(PgParametersRepositoryTest, createSameThrowsException) { + // +} diff --git a/src/libs/eedb/include/eedb/Category.hpp b/src/libs/eedb/include/eedb/Category.hpp index b7ec38a..ab73d2e 100644 --- a/src/libs/eedb/include/eedb/Category.hpp +++ b/src/libs/eedb/include/eedb/Category.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -19,6 +20,12 @@ class CategoriesRepository { * @return root category */ virtual std::unique_ptr< Category > root() const = 0; + + /** + * @brief create + * @return category in detached state (not connected to any parent category) + */ + virtual std::unique_ptr< Category > create(std::string name, std::string description) const = 0; }; namespace details { @@ -27,6 +34,24 @@ namespace details { using CategoryPtrIteratorRange = boost::iterator_range< CategoryPtrIterator >; } // namespace details +namespace exceptions { + /** + * @brief The CategoryAddException class, thrown when a child category cannot be inserted to given category + */ + class CategoryAddException : public std::logic_error { + // exception interface + public: + CategoryAddException(std::unique_ptr< Category > category, std::string reason) + : std::logic_error(std::move(reason)), _badCategory{std::move(category)} {} + + std::unique_ptr< Category > get() { + return std::move(_badCategory); + } + + private: + std::unique_ptr< Category > _badCategory; + }; +} // namespace exceptions /** * @brief The Categories class */ @@ -47,7 +72,7 @@ class Category { public: virtual ~Category() = default; - virtual int foo(){} + virtual int foo() {} /** * @brief displayName @@ -62,10 +87,12 @@ class Category { virtual Category * parent() const = 0; /** - * @brief create - * @return category in detached state (not connected to any root category) + * @brief addChild + * @param category + * @throws + * @return pointer to newely added category */ - virtual std::unique_ptr< Category > create(std::string name, std::string description) const = 0; + virtual Category * addChild(std::unique_ptr< Category > category) = 0; /** * @brief children diff --git a/src/libs/webapp/mock/include/eedb/mock/CategoriesRepositoryMock.hpp b/src/libs/webapp/mock/include/eedb/mock/CategoriesRepositoryMock.hpp index 0d54f9d..d89deb1 100644 --- a/src/libs/webapp/mock/include/eedb/mock/CategoriesRepositoryMock.hpp +++ b/src/libs/webapp/mock/include/eedb/mock/CategoriesRepositoryMock.hpp @@ -4,10 +4,10 @@ #include -namespace eedb{ +namespace eedb { class CategoriesRepositoryMock : public CategoriesRepository { public: MOCK_CONST_METHOD0(root, std::unique_ptr< Category >()); + MOCK_CONST_METHOD2(create, std::unique_ptr< Category >(std::string name, std::string description)); }; -} - +} // namespace eedb diff --git a/src/libs/webapp/mock/include/eedb/mock/CategoryMock.hpp b/src/libs/webapp/mock/include/eedb/mock/CategoryMock.hpp index 85b7af0..095b5b4 100644 --- a/src/libs/webapp/mock/include/eedb/mock/CategoryMock.hpp +++ b/src/libs/webapp/mock/include/eedb/mock/CategoryMock.hpp @@ -4,15 +4,17 @@ #include -namespace eedb{ +namespace eedb { class CategoryMock : public Category { public: // Category interface public: MOCK_CONST_METHOD0(displayName, string_view()); MOCK_CONST_METHOD0(parent, Category *()); - MOCK_CONST_METHOD2(create, std::unique_ptr< Category >(std::string name, std::string description)); + MOCK_METHOD1(doAddChild, Category *( Category * ) ); + Category * addChild(std::unique_ptr< Category > c) { + return doAddChild(c.get()); + } MOCK_CONST_METHOD0(children, std::unique_ptr< CategoriesChildren >()); }; -} - +} // namespace eedb