Skip to content

Commit 112baf3

Browse files
committed
Split profile management and add tests
1 parent b10f240 commit 112baf3

10 files changed

Lines changed: 507 additions & 71 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ out/
5555
/dist/
5656
*.AppImage
5757
runtime/
58+
.env
5859

5960
# ---> MacOSX
6061
.DS_Store

src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ set(KEMAI_QML_SOURCES
3131
)
3232

3333
set(KEMAI_LIB_SOURCES
34+
cpp/context/profileController.h
35+
cpp/context/profileController.cpp
3436
cpp/client/kimaiSystem.h
3537
cpp/client/kimaiSystem.cpp
3638
cpp/client/kimaiResources.h
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// SPDX-FileCopyrightText: 2026 Alexandre Petitjean
2+
// SPDX-License-Identifier: MIT
3+
#include "profileController.h"
4+
5+
// Project headers
6+
#include <misc/pathHelpers.h>
7+
#include <model/profile.h>
8+
#include <storage/profileStore.h>
9+
10+
namespace kemai
11+
{
12+
ProfileController::ProfileController(QObject *parent):
13+
QObject(parent),
14+
m_profileModel(std::make_unique<ProfileModel>()),
15+
m_profileStore(std::make_unique<ProfileStore>())
16+
{
17+
auto profilesPath = PathHelpers::getFilePath(PathHelpers::FilePathType::Profiles);
18+
m_profileStore->setStorePath(profilesPath);
19+
20+
connect(m_profileStore.get(), &ProfileStore::profilesLoaded, this, &ProfileController::onProfilesLoaded);
21+
connect(m_profileStore.get(), &ProfileStore::profileSaved, this, &ProfileController::onProfileSaved);
22+
connect(m_profileStore.get(), &ProfileStore::profileRemoved, this, &ProfileController::onProfileRemoved);
23+
24+
m_profileStore->loadAll();
25+
}
26+
27+
ProfileController::~ProfileController() = default;
28+
29+
ProfileModel *ProfileController::profileModel() const
30+
{
31+
return m_profileModel.get();
32+
}
33+
34+
void ProfileController::upsert(const ProfileId &profileId, const QString &name, const QString &host, const QString &token)
35+
{
36+
Profile profile;
37+
profile.id = profileId.isNull() ? ProfileId::createUuid() : profileId;
38+
profile.name = name;
39+
profile.host = host;
40+
profile.token = token;
41+
42+
m_profileStore->save(profile);
43+
}
44+
45+
void ProfileController::remove(const ProfileId &profileId)
46+
{
47+
m_profileStore->remove(profileId);
48+
}
49+
50+
void ProfileController::onProfilesLoaded(const std::vector<Profile> &profiles)
51+
{
52+
m_profileModel->setProfiles(profiles);
53+
}
54+
55+
void ProfileController::onProfileSaved(ProfileId /*profileId*/)
56+
{
57+
m_profileStore->loadAll();
58+
}
59+
60+
void ProfileController::onProfileRemoved(ProfileId /*profileId*/)
61+
{
62+
m_profileStore->loadAll();
63+
}
64+
65+
} // namespace kemai
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// SPDX-FileCopyrightText: 2026 Alexandre Petitjean
2+
// SPDX-License-Identifier: MIT
3+
#pragma once
4+
5+
// STL headers
6+
#include <memory>
7+
8+
// Qt headers
9+
#include <QObject>
10+
11+
// Project headers
12+
#include <data/profile.h>
13+
#include <model/profile.h>
14+
15+
namespace kemai
16+
{
17+
class ProfileStore;
18+
19+
class ProfileController : public QObject
20+
{
21+
Q_OBJECT
22+
23+
public:
24+
explicit ProfileController(QObject *parent = nullptr);
25+
~ProfileController() override;
26+
27+
ProfileModel *profileModel() const;
28+
29+
void upsert(const ProfileId &profileId, const QString &name, const QString &host, const QString &token);
30+
void remove(const ProfileId &profileId);
31+
32+
private:
33+
void onProfilesLoaded(const std::vector<Profile> &profiles);
34+
void onProfileSaved(ProfileId profileId);
35+
void onProfileRemoved(ProfileId profileId);
36+
37+
std::unique_ptr<ProfileModel> m_profileModel;
38+
std::unique_ptr<ProfileStore> m_profileStore;
39+
};
40+
} // namespace kemai

src/cpp/context/profileManager.cpp

Lines changed: 3 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,64 +2,21 @@
22
// SPDX-License-Identifier: MIT
33
#include "profileManager.h"
44

5-
// Project headers
6-
#include <misc/pathHelpers.h>
7-
#include <model/profile.h>
8-
#include <storage/profileStore.h>
9-
105
namespace kemai
116
{
127
ProfileManager::ProfileManager(QObject *parent):
13-
QObject(parent),
14-
m_profileModel(std::make_unique<ProfileModel>()),
15-
m_profileStore(std::make_unique<ProfileStore>())
8+
ProfileController(parent)
169
{
17-
auto profilesPath = PathHelpers::getFilePath(PathHelpers::FilePathType::Profiles);
18-
m_profileStore->setStorePath(profilesPath);
19-
20-
connect(m_profileStore.get(), &ProfileStore::profilesLoaded, this, &ProfileManager::onProfilesLoaded);
21-
connect(m_profileStore.get(), &ProfileStore::profileSaved, this, &ProfileManager::onProfileSaved);
22-
connect(m_profileStore.get(), &ProfileStore::profileRemoved, this, &ProfileManager::onProfileRemoved);
23-
24-
m_profileStore->loadAll();
25-
}
26-
27-
ProfileManager::~ProfileManager() = default;
28-
29-
ProfileModel *ProfileManager::profileModel() const
30-
{
31-
return m_profileModel.get();
3210
}
3311

3412
void ProfileManager::upsert(const ProfileId &profileId, const QString &name, const QString &host, const QString &token)
3513
{
36-
Profile profile;
37-
profile.id = profileId.isNull() ? ProfileId::createUuid() : profileId;
38-
profile.name = name;
39-
profile.host = host;
40-
profile.token = token;
41-
42-
m_profileStore->save(profile);
14+
ProfileController::upsert(profileId, name, host, token);
4315
}
4416

4517
void ProfileManager::remove(const ProfileId &profileId)
4618
{
47-
m_profileStore->remove(profileId);
48-
}
49-
50-
void ProfileManager::onProfilesLoaded(const std::vector<Profile> &profiles)
51-
{
52-
m_profileModel->setProfiles(profiles);
53-
}
54-
55-
void ProfileManager::onProfileSaved(ProfileId /*profileId*/)
56-
{
57-
m_profileStore->loadAll();
58-
}
59-
60-
void ProfileManager::onProfileRemoved(ProfileId /*profileId*/)
61-
{
62-
m_profileStore->loadAll();
19+
ProfileController::remove(profileId);
6320
}
6421

6522
} // namespace kemai

src/cpp/context/profileManager.h

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,15 @@
22
// SPDX-License-Identifier: MIT
33
#pragma once
44

5-
// STL headers
6-
#include <memory>
7-
85
// Qt headers
9-
#include <QObject>
106
#include <QtQmlIntegration/qqmlintegration.h>
117

128
// Project headers
13-
#include <data/profile.h>
14-
#include <model/profile.h>
9+
#include <context/profileController.h>
1510

1611
namespace kemai
1712
{
18-
class ProfileStore;
19-
20-
class ProfileManager : public QObject
13+
class ProfileManager : public ProfileController
2114
{
2215
Q_OBJECT
2316
QML_ELEMENT
@@ -26,19 +19,8 @@ namespace kemai
2619

2720
public:
2821
explicit ProfileManager(QObject *parent = nullptr);
29-
~ProfileManager() override;
30-
31-
ProfileModel *profileModel() const;
3222

3323
Q_INVOKABLE void upsert(const ProfileId &profileId, const QString &name, const QString &host, const QString &token);
3424
Q_INVOKABLE void remove(const ProfileId &profileId);
35-
36-
private:
37-
void onProfilesLoaded(const std::vector<Profile> &profiles);
38-
void onProfileSaved(ProfileId profileId);
39-
void onProfileRemoved(ProfileId profileId);
40-
41-
std::unique_ptr<ProfileModel> m_profileModel;
42-
std::unique_ptr<ProfileStore> m_profileStore;
4325
};
44-
} // namespace kemai
26+
} // namespace kemai

src/cpp/context/sessionContext.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ namespace kemai
1919
{
2020
Q_OBJECT
2121
QML_ELEMENT
22+
QML_UNCREATABLE("SessionContext is created by the application")
2223

2324
Q_PROPERTY(QString host READ host CONSTANT)
2425
Q_PROPERTY(QString username READ username CONSTANT)

src/cpp/storage/profileStore.cpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// Project helpers
1212
#include <misc/customFmt.h>
1313
#include <misc/jsonHelpers.h>
14+
#include <misc/pathHelpers.h>
1415

1516
namespace
1617
{
@@ -116,10 +117,20 @@ namespace kemai
116117
{
117118
if (profile.isValid())
118119
{
119-
auto profiles = loadProfilesFromJson(m_storePath);
120-
auto it = std::ranges::find_if(profiles, [&profile](const Profile &p)
121-
{ return p.id == profile.id; });
120+
std::vector<Profile> profiles;
121+
try
122+
{
123+
profiles = loadProfilesFromJson(m_storePath);
124+
}
125+
catch (const std::runtime_error &)
126+
{
127+
// File doesn't exist yet — start with an empty list and ensure
128+
// the parent directory exists before writing
129+
PathHelpers::ensureDirectoryExists(m_storePath);
130+
}
122131

132+
auto it = std::ranges::find_if(profiles, [&profile](const Profile &p)
133+
{ return p.id == profile.id; });
123134
if (it != profiles.end())
124135
{
125136
*it = profile;
@@ -137,7 +148,17 @@ namespace kemai
137148

138149
void ProfileStore::remove(const ProfileId &profileId)
139150
{
140-
auto profiles = loadProfilesFromJson(m_storePath);
151+
std::vector<Profile> profiles;
152+
try
153+
{
154+
profiles = loadProfilesFromJson(m_storePath);
155+
}
156+
catch (const std::runtime_error &)
157+
{
158+
// File doesn't exist — nothing to remove
159+
return;
160+
}
161+
141162
std::erase_if(profiles, [&profileId](const Profile &p)
142163
{ return p.id == profileId; });
143164

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ list(APPEND CMAKE_MODULE_PATH ${Catch2_SOURCE_DIR}/extras)
55
qt_add_executable(${PROJECT_NAME}
66
main.cpp
77
client/tst_kimaiClient.cpp
8+
context/tst_profileManager.cpp
89
)
910

1011
target_link_libraries(${PROJECT_NAME} PRIVATE

0 commit comments

Comments
 (0)