• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / thomas.goyne_275

09 Apr 2024 03:33AM UTC coverage: 92.608% (+0.5%) from 92.088%
thomas.goyne_275

Pull #7300

Evergreen

tgoyne
Extract some duplicated code in PushClient
Pull Request #7300: Rework sync user handling and metadata storage

102672 of 194970 branches covered (52.66%)

3165 of 3247 new or added lines in 46 files covered. (97.47%)

34 existing lines in 9 files now uncovered.

249420 of 269329 relevant lines covered (92.61%)

45087511.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.24
/src/realm/object-store/sync/app_user.cpp
1
////////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright 2024 Realm Inc.
4
//
5
// Licensed under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
8
//
9
// http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing, software
12
// distributed under the License is distributed on an "AS IS" BASIS,
13
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
// See the License for the specific language governing permissions and
15
// limitations under the License.
16
//
17
////////////////////////////////////////////////////////////////////////////
18

19
#include <realm/object-store/sync/app_user.hpp>
20

21
#include <realm/object-store/sync/app.hpp>
22
#include <realm/object-store/sync/app_credentials.hpp>
23
#include <realm/object-store/sync/generic_network_transport.hpp>
24
#include <realm/object-store/sync/impl/app_metadata.hpp>
25
#include <realm/object-store/sync/impl/sync_file.hpp>
26
#include <realm/object-store/sync/mongo_client.hpp>
27
#include <realm/object-store/sync/sync_manager.hpp>
28

29
namespace realm::app {
30

31
UserIdentity::UserIdentity(const std::string& id, const std::string& provider_type)
32
    : id(id)
33
    , provider_type(provider_type)
34
{
5,756✔
35
}
5,756✔
36

37
User::User(Private, std::shared_ptr<app::App> app, std::string_view user_id)
38
    : m_app(std::move(app))
39
    , m_app_id(m_app->app_id())
40
    , m_user_id(user_id)
41
{
9,201✔
42
    REALM_ASSERT(m_app);
9,201✔
43
    m_app->register_sync_user(*this);
9,201✔
44
}
9,201✔
45

46
User::~User()
47
{
9,201✔
48
    if (m_app) {
9,201✔
49
        m_app->unregister_sync_user(*this);
9,158✔
50
    }
9,158✔
51
}
9,201✔
52

53
std::string User::user_id() const noexcept
54
{
43,745✔
55
    return m_user_id;
43,745✔
56
}
43,745✔
57

58
std::string User::app_id() const noexcept
59
{
22✔
60
    return m_app_id;
22✔
61
}
22✔
62

63
std::vector<std::string> User::legacy_identities() const
64
{
28✔
65
    util::CheckedLockGuard lock(m_mutex);
28✔
66
    return m_data.legacy_identities;
28✔
67
}
28✔
68

69
std::string User::access_token() const
70
{
6,925✔
71
    util::CheckedLockGuard lock(m_mutex);
6,925✔
72
    return m_data.access_token.token;
6,925✔
73
}
6,925✔
74

75
std::string User::refresh_token() const
76
{
202✔
77
    util::CheckedLockGuard lock(m_mutex);
202✔
78
    return m_data.refresh_token.token;
202✔
79
}
202✔
80

81
SyncUser::State User::state() const
82
{
16,007✔
83
    util::CheckedLockGuard lock(m_mutex);
16,007✔
84
    if (!m_app)
16,007✔
85
        return SyncUser::State::Removed;
67✔
86
    return m_data.access_token ? SyncUser::State::LoggedIn : SyncUser::State::LoggedOut;
15,940✔
87
}
15,940✔
88

89
bool User::is_anonymous() const
90
{
65✔
91
    util::CheckedLockGuard lock(m_mutex);
65✔
92
    return do_is_anonymous();
65✔
93
}
65✔
94

95
bool User::do_is_anonymous() const
96
{
65✔
97
    return m_data.access_token && m_data.identities.size() == 1 &&
65✔
98
           m_data.identities[0].provider_type == app::IdentityProviderAnonymous;
53✔
99
}
65✔
100

101
std::string User::device_id() const
102
{
2✔
103
    util::CheckedLockGuard lock(m_mutex);
2✔
104
    return m_data.device_id;
2✔
105
}
2✔
106

107
bool User::has_device_id() const
108
{
2✔
109
    // The server will sometimes send us an all-zero device ID as a way to
1✔
110
    // explicitly signal that it did not generate a device ID for this login.
1✔
111
    util::CheckedLockGuard lock(m_mutex);
2✔
112
    return !m_data.device_id.empty() && m_data.device_id != "000000000000000000000000";
2✔
113
}
2✔
114

115
UserProfile User::user_profile() const
116
{
104✔
117
    util::CheckedLockGuard lock(m_mutex);
104✔
118
    return m_data.profile;
104✔
119
}
104✔
120

121
std::vector<UserIdentity> User::identities() const
122
{
30✔
123
    util::CheckedLockGuard lock(m_mutex);
30✔
124
    return m_data.identities;
30✔
125
}
30✔
126

127
std::optional<bson::BsonDocument> User::custom_data() const
128
{
4✔
129
    util::CheckedLockGuard lock(m_mutex);
4✔
130
    return m_data.access_token.user_data;
4✔
131
}
4✔
132

133
std::shared_ptr<App> User::app() const
134
{
199✔
135
    util::CheckedLockGuard lock(m_mutex);
199✔
136
    return m_app;
199✔
137
}
199✔
138

139
SyncManager* User::sync_manager()
140
{
2,048✔
141
    util::CheckedLockGuard lock(m_mutex);
2,048✔
142
    return m_app ? m_app->sync_manager().get() : nullptr;
2,045✔
143
}
2,048✔
144

145
app::MongoClient User::mongo_client(const std::string& service_name)
146
{
77✔
147
    util::CheckedLockGuard lock(m_mutex);
77✔
148
    return app::MongoClient(shared_from_this(), m_app->app_service_client(), service_name);
77✔
149
}
77✔
150

151
bool User::access_token_refresh_required() const
152
{
1,591✔
153
    using namespace std::chrono;
1,591✔
154
    constexpr size_t buffer_seconds = 5; // arbitrary
1,591✔
155
    util::CheckedLockGuard lock(m_mutex);
1,591✔
156
    const auto now = duration_cast<seconds>(system_clock::now().time_since_epoch()).count() +
1,591✔
157
                     m_seconds_to_adjust_time_for_testing.load(std::memory_order_relaxed);
1,591✔
158
    const auto threshold = now - buffer_seconds;
1,591✔
159
    return m_data.access_token && m_data.access_token.expires_at < static_cast<int64_t>(threshold);
1,591✔
160
}
1,591✔
161

162
void User::log_out()
163
{
7✔
164
    if (auto app = this->app()) {
7✔
165
        app->log_out(shared_from_this(), nullptr);
7✔
166
    }
7✔
167
}
7✔
168

169
void User::detach_and_tear_down()
170
{
58✔
171
    std::shared_ptr<App> app;
58✔
172
    {
58✔
173
        util::CheckedLockGuard lk(m_mutex);
58✔
174
        m_data.access_token.token.clear();
58✔
175
        m_data.refresh_token.token.clear();
58✔
176
        app = std::exchange(m_app, nullptr);
58✔
177
    }
58✔
178

28✔
179
    if (app) {
58✔
180
        app->sync_manager()->update_sessions_for(*this, SyncUser::State::LoggedIn, SyncUser::State::Removed, {});
43✔
181
        app->unregister_sync_user(*this);
43✔
182
    }
43✔
183
}
58✔
184

185
void User::update_data_for_testing(util::FunctionRef<void(UserData&)> fn)
186
{
20✔
187
    UserData data;
20✔
188
    {
20✔
189
        util::CheckedLockGuard lock(m_mutex);
20✔
190
        data = m_data;
20✔
191
    }
20✔
192
    fn(data);
20✔
193
    update_backing_data(std::move(data));
20✔
194
}
20✔
195

196
void User::update_backing_data(std::optional<UserData>&& data)
197
{
14,086✔
198
    if (!data) {
14,086✔
199
        detach_and_tear_down();
50✔
200
        emit_change_to_subscribers(*this);
50✔
201
        return;
50✔
202
    }
50✔
203

6,986✔
204
    std::string new_token;
14,036✔
205
    SyncUser::State old_state;
14,036✔
206
    SyncUser::State new_state = data->access_token ? SyncUser::State::LoggedIn : SyncUser::State::LoggedOut;
14,021✔
207
    std::shared_ptr<SyncManager> sync_manager;
14,036✔
208
    {
14,036✔
209
        util::CheckedLockGuard lock(m_mutex);
14,036✔
210
        if (!m_app) {
14,036✔
NEW
211
            return; // is already detached
×
NEW
212
        }
×
213
        sync_manager = m_app->sync_manager();
14,036✔
214
        old_state = m_data.access_token ? SyncUser::State::LoggedIn : SyncUser::State::LoggedOut;
11,611✔
215
        if (new_state == SyncUser::State::LoggedIn && data->access_token != m_data.access_token)
14,036✔
216
            new_token = data->access_token.token;
9,255✔
217
        m_data = std::move(*data);
14,036✔
218
    }
14,036✔
219

6,986✔
220
    sync_manager->update_sessions_for(*this, old_state, new_state, new_token);
14,036✔
221
    emit_change_to_subscribers(*this);
14,036✔
222
}
14,036✔
223

224
void User::request_log_out()
225
{
14✔
226
    if (auto app = this->app()) {
14✔
227
        auto new_state = is_anonymous() ? SyncUser::State::Removed : SyncUser::State::LoggedOut;
11✔
228
        app->m_metadata_store->log_out(m_user_id, new_state);
14✔
229
        update_backing_data(app->m_metadata_store->get_user(m_user_id));
14✔
230
    }
14✔
231
}
14✔
232

233
void User::request_refresh_user(util::UniqueFunction<void(util::Optional<app::AppError>)>&& completion)
NEW
234
{
×
NEW
235
    if (auto app = this->app()) {
×
NEW
236
        app->get_profile(shared_from_this(), [completion = std::move(completion)](auto, auto error) {
×
NEW
237
            completion(std::move(error));
×
NEW
238
        });
×
NEW
239
    }
×
NEW
240
}
×
241

242
void User::request_refresh_location(util::UniqueFunction<void(util::Optional<app::AppError>)>&& completion)
243
{
34✔
244
    if (auto app = this->app()) {
34✔
245
        bool update_location = true;
34✔
246
        app->refresh_access_token(shared_from_this(), update_location, std::move(completion));
34✔
247
    }
34✔
248
}
34✔
249

250
void User::request_access_token(util::UniqueFunction<void(util::Optional<app::AppError>)>&& completion)
251
{
30✔
252
    if (auto app = this->app()) {
30✔
253
        bool update_location = false;
30✔
254
        app->refresh_access_token(shared_from_this(), update_location, std::move(completion));
30✔
255
    }
30✔
256
}
30✔
257

258
void User::track_realm(std::string_view path)
259
{
28✔
260
    if (auto app = this->app()) {
28✔
261
        app->m_metadata_store->add_realm_path(m_user_id, path);
28✔
262
    }
28✔
263
}
28✔
264

265
std::string User::create_file_action(SyncFileAction action, std::string_view original_path,
266
                                     std::optional<std::string> requested_recovery_dir)
267
{
74✔
268
    if (auto app = this->app()) {
74✔
269
        std::string recovery_path;
74✔
270
        if (action == SyncFileAction::BackUpThenDeleteRealm) {
74✔
271
            recovery_path =
74✔
272
                util::reserve_unique_file_name(app->m_file_manager->recovery_directory_path(requested_recovery_dir),
74✔
273
                                               util::create_timestamped_template("recovered_realm"));
74✔
274
        }
74✔
275
        app->m_metadata_store->create_file_action(action, original_path, recovery_path);
74✔
276
        return recovery_path;
74✔
277
    }
74✔
NEW
278
    return "";
×
NEW
279
}
×
280

281
void User::refresh_custom_data(util::UniqueFunction<void(util::Optional<app::AppError>)> completion_block)
282
    REQUIRES(!m_mutex)
283
{
12✔
284
    refresh_custom_data(false, std::move(completion_block));
12✔
285
}
12✔
286

287
void User::refresh_custom_data(bool update_location,
288
                               util::UniqueFunction<void(util::Optional<app::AppError>)> completion_block)
289
{
12✔
290
    if (auto app = this->app()) {
12✔
291
        app->refresh_custom_data(shared_from_this(), update_location, std::move(completion_block));
10✔
292
        return;
10✔
293
    }
10✔
294
    completion_block(app::AppError(
2✔
295
        ErrorCodes::ClientUserNotFound,
2✔
296
        util::format("Cannot initiate a refresh on user '%1' because the user has been removed", m_user_id)));
2✔
297
}
2✔
298

299
std::string User::path_for_realm(const SyncConfig& config, std::optional<std::string> custom_file_name) const
NEW
300
{
×
NEW
301
    if (auto app = this->app()) {
×
NEW
302
        return app->m_file_manager->path_for_realm(config, std::move(custom_file_name));
×
NEW
303
    }
×
NEW
304
    return "";
×
NEW
305
}
×
306
} // namespace realm::app
307

308
namespace std {
309
size_t hash<realm::app::UserIdentity>::operator()(const realm::app::UserIdentity& k) const
NEW
310
{
×
NEW
311
    return ((hash<string>()(k.id) ^ (hash<string>()(k.provider_type) << 1)) >> 1);
×
NEW
312
}
×
313
} // namespace std
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc