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

realm / realm-core / 1691

20 Sep 2023 01:57AM UTC coverage: 91.217% (+0.05%) from 91.168%
1691

push

Evergreen

web-flow
Merge pull request #6837 from realm/tg/user-provider

Fix handling of users with multiple identities

95990 of 175908 branches covered (0.0%)

799 of 830 new or added lines in 24 files covered. (96.27%)

44 existing lines in 15 files now uncovered.

233732 of 256237 relevant lines covered (91.22%)

6741306.52 hits per line

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

28.81
/src/realm/object-store/c_api/app.cpp
1
////////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright 2021 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 utilied.
14
// See the License for the specific language governing permissions and
15
// limitations under the License.
16
//
17
////////////////////////////////////////////////////////////////////////////
18

19
#include "types.hpp"
20
#include "util.hpp"
21
#include "conversion.hpp"
22

23
#include <realm/object-store/sync/sync_user.hpp>
24
#include <realm/object-store/sync/mongo_client.hpp>
25
#include <realm/object-store/sync/mongo_database.hpp>
26

27
namespace realm::c_api {
28
using namespace realm::app;
29

30
static_assert(realm_user_state_e(SyncUser::State::LoggedOut) == RLM_USER_STATE_LOGGED_OUT);
31
static_assert(realm_user_state_e(SyncUser::State::LoggedIn) == RLM_USER_STATE_LOGGED_IN);
32
static_assert(realm_user_state_e(SyncUser::State::Removed) == RLM_USER_STATE_REMOVED);
33

34
static_assert(realm_auth_provider_e(AuthProvider::ANONYMOUS) == RLM_AUTH_PROVIDER_ANONYMOUS);
35
static_assert(realm_auth_provider_e(AuthProvider::ANONYMOUS_NO_REUSE) == RLM_AUTH_PROVIDER_ANONYMOUS_NO_REUSE);
36
static_assert(realm_auth_provider_e(AuthProvider::FACEBOOK) == RLM_AUTH_PROVIDER_FACEBOOK);
37
static_assert(realm_auth_provider_e(AuthProvider::GOOGLE) == RLM_AUTH_PROVIDER_GOOGLE);
38
static_assert(realm_auth_provider_e(AuthProvider::APPLE) == RLM_AUTH_PROVIDER_APPLE);
39
static_assert(realm_auth_provider_e(AuthProvider::CUSTOM) == RLM_AUTH_PROVIDER_CUSTOM);
40
static_assert(realm_auth_provider_e(AuthProvider::USERNAME_PASSWORD) == RLM_AUTH_PROVIDER_EMAIL_PASSWORD);
41
static_assert(realm_auth_provider_e(AuthProvider::FUNCTION) == RLM_AUTH_PROVIDER_FUNCTION);
42
static_assert(realm_auth_provider_e(AuthProvider::API_KEY) == RLM_AUTH_PROVIDER_API_KEY);
43

44

45
static realm_app_error_t to_capi(const AppError& error)
46
{
2✔
47
    auto ret = realm_app_error_t();
2✔
48

1✔
49
    ret.error = realm_errno_e(error.code());
2✔
50
    ret.categories = ErrorCodes::error_categories(error.code()).value();
2✔
51

1✔
52
    if (error.additional_status_code) {
2✔
53
        ret.http_status_code = *error.additional_status_code;
2✔
54
    }
2✔
55

1✔
56
    ret.message = error.what();
2✔
57

1✔
58
    if (error.link_to_server_logs.size() > 0) {
2✔
59
        ret.link_to_server_logs = error.link_to_server_logs.c_str();
×
60
    }
×
61

1✔
62
    return ret;
2✔
63
}
2✔
64

65
static inline realm_app_user_apikey_t to_capi(const App::UserAPIKey& apikey)
66
{
4✔
67
    return {to_capi(apikey.id), apikey.key ? apikey.key->c_str() : nullptr, apikey.name.c_str(), apikey.disabled};
3✔
68
}
4✔
69

70
static inline auto make_callback(realm_app_void_completion_func_t callback, realm_userdata_t userdata,
71
                                 realm_free_userdata_func_t userdata_free)
72
{
14✔
73
    return
14✔
74
        [callback, userdata = SharedUserdata(userdata, FreeUserdata(userdata_free))](util::Optional<AppError> error) {
14✔
75
            if (error) {
14✔
76
                realm_app_error_t c_err{to_capi(*error)};
×
77
                callback(userdata.get(), &c_err);
×
78
            }
×
79
            else {
14✔
80
                callback(userdata.get(), nullptr);
14✔
81
            }
14✔
82
        };
14✔
83
}
14✔
84

85
static inline auto make_callback(realm_app_user_completion_func_t callback, realm_userdata_t userdata,
86
                                 realm_free_userdata_func_t userdata_free)
87
{
12✔
88
    return [callback, userdata = SharedUserdata(userdata, FreeUserdata(userdata_free))](
12✔
89
               std::shared_ptr<SyncUser> user, util::Optional<AppError> error) {
12✔
90
        if (error) {
12✔
91
            realm_app_error_t c_err{to_capi(*error)};
×
92
            callback(userdata.get(), nullptr, &c_err);
×
93
        }
×
94
        else {
12✔
95
            auto c_user = realm_user_t(std::move(user));
12✔
96
            callback(userdata.get(), &c_user, nullptr);
12✔
97
        }
12✔
98
    };
12✔
99
}
12✔
100

101
static inline auto make_callback(void (*callback)(realm_userdata_t userdata, realm_app_user_apikey_t*,
102
                                                  const realm_app_error_t*),
103
                                 realm_userdata_t userdata, realm_free_userdata_func_t userdata_free)
104
{
2✔
105
    return [callback, userdata = SharedUserdata(userdata, FreeUserdata(userdata_free))](
2✔
106
               App::UserAPIKey apikey, util::Optional<AppError> error) {
2✔
107
        if (error) {
2✔
108
            realm_app_error_t c_error(to_capi(*error));
×
109
            callback(userdata.get(), nullptr, &c_error);
×
110
        }
×
111
        else {
2✔
112
            realm_app_user_apikey_t c_apikey(to_capi(apikey));
2✔
113
            callback(userdata.get(), &c_apikey, nullptr);
2✔
114
        }
2✔
115
    };
2✔
116
}
2✔
117

118
static inline bson::BsonArray parse_ejson_array(const char* serialized)
119
{
×
120
    if (!serialized) {
×
121
        return {};
×
122
    }
×
123
    else {
×
124
        return bson::BsonArray(bson::parse(serialized));
×
125
    }
×
126
}
×
127

128
RLM_API realm_app_credentials_t* realm_app_credentials_new_anonymous(bool reuse_credentials) noexcept
129
{
×
130
    return new realm_app_credentials_t(AppCredentials::anonymous(reuse_credentials));
×
131
}
×
132

133
RLM_API realm_app_credentials_t* realm_app_credentials_new_facebook(const char* access_token) noexcept
134
{
×
135
    return new realm_app_credentials_t(AppCredentials::facebook(access_token));
×
136
}
×
137

138
RLM_API realm_app_credentials_t* realm_app_credentials_new_google_id_token(const char* id_token) noexcept
139
{
×
140
    return new realm_app_credentials_t(AppCredentials::google(IdToken(id_token)));
×
141
}
×
142

143
RLM_API realm_app_credentials_t* realm_app_credentials_new_google_auth_code(const char* auth_code) noexcept
144
{
×
145
    return new realm_app_credentials_t(AppCredentials::google(AuthCode(auth_code)));
×
146
}
×
147

148
RLM_API realm_app_credentials_t* realm_app_credentials_new_apple(const char* id_token) noexcept
149
{
×
150
    return new realm_app_credentials_t(AppCredentials::apple(id_token));
×
151
}
×
152

153
RLM_API realm_app_credentials_t* realm_app_credentials_new_jwt(const char* jwt_token) noexcept
154
{
×
155
    return new realm_app_credentials_t(AppCredentials::custom(jwt_token));
×
156
}
×
157

158
RLM_API realm_app_credentials_t* realm_app_credentials_new_email_password(const char* email,
159
                                                                          realm_string_t password) noexcept
160
{
×
161
    return new realm_app_credentials_t(AppCredentials::username_password(email, from_capi(password)));
×
162
}
×
163

164
RLM_API realm_app_credentials_t* realm_app_credentials_new_function(const char* serialized_ejson_payload)
165
{
×
166
    return wrap_err([&] {
×
167
        return new realm_app_credentials_t(AppCredentials::function(serialized_ejson_payload));
×
168
    });
×
169
}
×
170

171
RLM_API realm_app_credentials_t* realm_app_credentials_new_api_key(const char* api_key) noexcept
172
{
×
173
    return new realm_app_credentials_t(AppCredentials::api_key(api_key));
×
174
}
×
175

176
RLM_API realm_auth_provider_e realm_auth_credentials_get_provider(realm_app_credentials_t* credentials) noexcept
177
{
×
178
    return realm_auth_provider_e(credentials->provider());
×
179
}
×
180

181
RLM_API realm_app_config_t* realm_app_config_new(const char* app_id,
182
                                                 const realm_http_transport_t* http_transport) noexcept
183
{
2✔
184
    auto* config = new realm_app_config_t;
2✔
185
    config->app_id = app_id;
2✔
186
    config->transport = *http_transport; // realm_http_transport_t is a shared_ptr
2✔
187
    return config;
2✔
188
}
2✔
189

190
RLM_API void realm_app_config_set_base_url(realm_app_config_t* config, const char* base_url) noexcept
191
{
2✔
192
    config->base_url = std::string(base_url);
2✔
193
}
2✔
194

195
RLM_API void realm_app_config_set_default_request_timeout(realm_app_config_t* config, uint64_t ms) noexcept
196
{
2✔
197
    config->default_request_timeout_ms = ms;
2✔
198
}
2✔
199

200
RLM_API void realm_app_config_set_platform_version(realm_app_config_t* config, const char* platform_version) noexcept
201
{
2✔
202
    config->device_info.platform_version = std::string(platform_version);
2✔
203
}
2✔
204

205
RLM_API void realm_app_config_set_sdk_version(realm_app_config_t* config, const char* sdk_version) noexcept
206
{
2✔
207
    config->device_info.sdk_version = std::string(sdk_version);
2✔
208
}
2✔
209

210
RLM_API void realm_app_config_set_sdk(realm_app_config_t* config, const char* sdk) noexcept
211
{
2✔
212
    config->device_info.sdk = std::string(sdk);
2✔
213
}
2✔
214

215
RLM_API void realm_app_config_set_device_name(realm_app_config_t* config, const char* device_name) noexcept
216
{
2✔
217
    config->device_info.device_name = std::string(device_name);
2✔
218
}
2✔
219

220
RLM_API void realm_app_config_set_device_version(realm_app_config_t* config, const char* device_version) noexcept
221
{
2✔
222
    config->device_info.device_version = std::string(device_version);
2✔
223
}
2✔
224

225
RLM_API void realm_app_config_set_framework_name(realm_app_config_t* config, const char* framework_name) noexcept
226
{
2✔
227
    config->device_info.framework_name = std::string(framework_name);
2✔
228
}
2✔
229

230
RLM_API void realm_app_config_set_framework_version(realm_app_config_t* config,
231
                                                    const char* framework_version) noexcept
232
{
2✔
233
    config->device_info.framework_version = std::string(framework_version);
2✔
234
}
2✔
235

236
RLM_API void realm_app_config_set_bundle_id(realm_app_config_t* config, const char* bundle_id) noexcept
237
{
2✔
238
    config->device_info.bundle_id = std::string(bundle_id);
2✔
239
}
2✔
240

241
RLM_API const char* realm_app_credentials_serialize_as_json(realm_app_credentials_t* app_credentials) noexcept
242
{
×
243
    return wrap_err([&] {
×
244
        return duplicate_string(app_credentials->serialize_as_json());
×
245
    });
×
246
}
×
247

248
RLM_API realm_app_t* realm_app_create(const realm_app_config_t* app_config,
249
                                      const realm_sync_client_config_t* sync_client_config)
250
{
×
251
    return wrap_err([&] {
×
252
        return new realm_app_t(App::get_uncached_app(*app_config, *sync_client_config));
×
253
    });
×
254
}
×
255

256
RLM_API void realm_clear_cached_apps(void) noexcept
257
{
×
258
    App::clear_cached_apps();
×
259
}
×
260

261
RLM_API const char* realm_app_get_app_id(const realm_app_t* app) noexcept
262
{
×
263
    return (*app)->config().app_id.c_str();
×
264
}
×
265

266
RLM_API realm_user_t* realm_app_get_current_user(const realm_app_t* app) noexcept
267
{
4✔
268
    if (auto user = (*app)->current_user()) {
4✔
269
        return new realm_user_t(user);
4✔
270
    }
4✔
271

272
    return nullptr;
×
273
}
×
274

275
RLM_API bool realm_app_get_all_users(const realm_app_t* app, realm_user_t** out_users, size_t capacity, size_t* out_n)
276
{
4✔
277
    return wrap_err([&] {
4✔
278
        const auto& users = (*app)->all_users();
4✔
279
        set_out_param(out_n, users.size());
4✔
280
        if (out_users && capacity >= users.size()) {
4✔
281
            OutBuffer<realm_user_t> buf(out_users);
2✔
282
            for (const auto& user : users) {
4✔
283
                buf.emplace(user);
4✔
284
            }
4✔
285
            buf.release(out_n);
2✔
286
        }
2✔
287
        return true;
4✔
288
    });
4✔
289
}
4✔
290

291
RLM_API bool realm_app_log_in_with_credentials(realm_app_t* app, realm_app_credentials_t* credentials,
292
                                               realm_app_user_completion_func_t callback, realm_userdata_t userdata,
293
                                               realm_free_userdata_func_t userdata_free)
294
{
10✔
295
    return wrap_err([&] {
10✔
296
        (*app)->log_in_with_credentials(*credentials, make_callback(callback, userdata, userdata_free));
10✔
297
        return true;
10✔
298
    });
10✔
299
}
10✔
300

301
RLM_API bool realm_app_log_out_current_user(realm_app_t* app, realm_app_void_completion_func_t callback,
302
                                            realm_userdata_t userdata, realm_free_userdata_func_t userdata_free)
303
{
×
304
    return wrap_err([&] {
×
305
        (*app)->log_out(make_callback(callback, userdata, userdata_free));
×
306
        return true;
×
307
    });
×
308
}
×
309

310
RLM_API bool realm_app_refresh_custom_data(realm_app_t* app, realm_user_t* user,
311
                                           realm_app_void_completion_func_t callback, realm_userdata_t userdata,
312
                                           realm_free_userdata_func_t userdata_free)
313
{
×
314
    return wrap_err([&] {
×
315
        (*app)->refresh_custom_data(*user, make_callback(callback, userdata, userdata_free));
×
316
        return true;
×
317
    });
×
318
}
×
319

320
RLM_API bool realm_app_log_out(realm_app_t* app, realm_user_t* user, realm_app_void_completion_func_t callback,
321
                               realm_userdata_t userdata, realm_free_userdata_func_t userdata_free)
322
{
×
323
    return wrap_err([&] {
×
324
        (*app)->log_out(*user, make_callback(callback, userdata, userdata_free));
×
325
        return true;
×
326
    });
×
327
}
×
328

329
RLM_API bool realm_app_link_user(realm_app_t* app, realm_user_t* user, realm_app_credentials_t* credentials,
330
                                 realm_app_user_completion_func_t callback, realm_userdata_t userdata,
331
                                 realm_free_userdata_func_t userdata_free)
332
{
2✔
333
    return wrap_err([&] {
2✔
334
        (*app)->link_user(*user, *credentials, make_callback(callback, userdata, userdata_free));
2✔
335
        return true;
2✔
336
    });
2✔
337
}
2✔
338

339
RLM_API bool realm_app_switch_user(realm_app_t* app, realm_user_t* user, realm_user_t** new_user)
340
{
2✔
341
    return wrap_err([&] {
2✔
342
        auto new_user_local = (*app)->switch_user(*user);
2✔
343
        if (new_user) {
2✔
344
            *new_user = new realm_user_t(std::move(new_user_local));
2✔
345
        }
2✔
346
        return true;
2✔
347
    });
2✔
348
}
2✔
349

350
RLM_API bool realm_app_remove_user(realm_app_t* app, realm_user_t* user, realm_app_void_completion_func_t callback,
351
                                   realm_userdata_t userdata, realm_free_userdata_func_t userdata_free)
352
{
2✔
353
    return wrap_err([&] {
2✔
354
        (*app)->remove_user(*user, make_callback(callback, userdata, userdata_free));
2✔
355
        return true;
2✔
356
    });
2✔
357
}
2✔
358

359
RLM_API bool realm_app_delete_user(realm_app_t* app, realm_user_t* user, realm_app_void_completion_func_t callback,
360
                                   realm_userdata_t userdata, realm_free_userdata_func_t userdata_free)
361
{
2✔
362
    return wrap_err([&] {
2✔
363
        (*app)->delete_user(*user, make_callback(callback, userdata, userdata_free));
2✔
364
        return true;
2✔
365
    });
2✔
366
}
2✔
367

368
RLM_API bool realm_app_email_password_provider_client_register_email(realm_app_t* app, const char* email,
369
                                                                     realm_string_t password,
370
                                                                     realm_app_void_completion_func_t callback,
371
                                                                     realm_userdata_t userdata,
372
                                                                     realm_free_userdata_func_t userdata_free)
373
{
10✔
374
    return wrap_err([&] {
10✔
375
        (*app)->provider_client<App::UsernamePasswordProviderClient>().register_email(
10✔
376
            email, from_capi(password), make_callback(callback, userdata, userdata_free));
10✔
377
        return true;
10✔
378
    });
10✔
379
}
10✔
380

381
RLM_API bool realm_app_email_password_provider_client_confirm_user(realm_app_t* app, const char* token,
382
                                                                   const char* token_id,
383
                                                                   realm_app_void_completion_func_t callback,
384
                                                                   realm_userdata_t userdata,
385
                                                                   realm_free_userdata_func_t userdata_free)
386
{
×
387
    return wrap_err([&] {
×
388
        (*app)->provider_client<App::UsernamePasswordProviderClient>().confirm_user(
×
389
            token, token_id, make_callback(callback, userdata, userdata_free));
×
390
        return true;
×
391
    });
×
392
}
×
393

394
RLM_API bool realm_app_email_password_provider_client_resend_confirmation_email(
395
    realm_app_t* app, const char* email, realm_app_void_completion_func_t callback, realm_userdata_t userdata,
396
    realm_free_userdata_func_t userdata_free)
397
{
×
398
    return wrap_err([&] {
×
399
        (*app)->provider_client<App::UsernamePasswordProviderClient>().resend_confirmation_email(
×
400
            email, make_callback(callback, userdata, userdata_free));
×
401
        return true;
×
402
    });
×
403
}
×
404

405
RLM_API bool realm_app_email_password_provider_client_send_reset_password_email(
406
    realm_app_t* app, const char* email, realm_app_void_completion_func_t callback, realm_userdata_t userdata,
407
    realm_free_userdata_func_t userdata_free)
408
{
×
409
    return wrap_err([&] {
×
410
        (*app)->provider_client<App::UsernamePasswordProviderClient>().send_reset_password_email(
×
411
            email, make_callback(callback, userdata, userdata_free));
×
412
        return true;
×
413
    });
×
414
}
×
415

416
RLM_API bool realm_app_email_password_provider_client_retry_custom_confirmation(
417
    realm_app_t* app, const char* email, realm_app_void_completion_func_t callback, realm_userdata_t userdata,
418
    realm_free_userdata_func_t userdata_free)
419
{
×
420
    return wrap_err([&] {
×
421
        (*app)->provider_client<App::UsernamePasswordProviderClient>().retry_custom_confirmation(
×
422
            email, make_callback(callback, userdata, userdata_free));
×
423
        return true;
×
424
    });
×
425
}
×
426

427
RLM_API bool realm_app_email_password_provider_client_reset_password(realm_app_t* app, realm_string_t password,
428
                                                                     const char* token, const char* token_id,
429
                                                                     realm_app_void_completion_func_t callback,
430
                                                                     realm_userdata_t userdata,
431
                                                                     realm_free_userdata_func_t userdata_free)
432
{
×
433
    return wrap_err([&] {
×
434
        (*app)->provider_client<App::UsernamePasswordProviderClient>().reset_password(
×
435
            from_capi(password), token, token_id, make_callback(callback, userdata, userdata_free));
×
436
        return true;
×
437
    });
×
438
}
×
439

440
RLM_API bool realm_app_email_password_provider_client_call_reset_password_function(
441
    realm_app_t* app, const char* email, realm_string_t password, const char* serialized_ejson_payload,
442
    realm_app_void_completion_func_t callback, realm_userdata_t userdata, realm_free_userdata_func_t userdata_free)
443
{
×
444
    return wrap_err([&] {
×
445
        bson::BsonArray args = parse_ejson_array(serialized_ejson_payload);
×
446
        (*app)->provider_client<App::UsernamePasswordProviderClient>().call_reset_password_function(
×
447
            email, from_capi(password), args, make_callback(callback, userdata, userdata_free));
×
448
        return true;
×
449
    });
×
450
}
×
451

452
RLM_API bool realm_app_user_apikey_provider_client_create_apikey(const realm_app_t* app, const realm_user_t* user,
453
                                                                 const char* name,
454
                                                                 realm_return_apikey_func_t callback,
455
                                                                 realm_userdata_t userdata,
456
                                                                 realm_free_userdata_func_t userdata_free)
457
{
2✔
458
    return wrap_err([&] {
2✔
459
        (*app)->provider_client<App::UserAPIKeyProviderClient>().create_api_key(
2✔
460
            name, *user, make_callback(callback, userdata, userdata_free));
2✔
461
        return true;
2✔
462
    });
2✔
463
}
2✔
464

465
RLM_API bool realm_app_user_apikey_provider_client_fetch_apikey(const realm_app_t* app, const realm_user_t* user,
466
                                                                realm_object_id_t id,
467
                                                                realm_return_apikey_func_t callback,
468
                                                                realm_userdata_t userdata,
469
                                                                realm_free_userdata_func_t userdata_free)
470
{
×
471
    return wrap_err([&] {
×
472
        (*app)->provider_client<App::UserAPIKeyProviderClient>().fetch_api_key(
×
473
            from_capi(id), *user, make_callback(callback, userdata, userdata_free));
×
474
        return true;
×
475
    });
×
476
}
×
477

478
RLM_API bool realm_app_user_apikey_provider_client_fetch_apikeys(const realm_app_t* app, const realm_user_t* user,
479
                                                                 realm_return_apikey_list_func_t callback,
480
                                                                 realm_userdata_t userdata,
481
                                                                 realm_free_userdata_func_t userdata_free)
482
{
4✔
483
    return wrap_err([&] {
4✔
484
        auto cb = [callback, userdata = SharedUserdata{userdata, FreeUserdata(userdata_free)}](
4✔
485
                      std::vector<App::UserAPIKey> apikeys, util::Optional<AppError> error) {
4✔
486
            if (error) {
4✔
487
                realm_app_error_t c_error(to_capi(*error));
2✔
488
                callback(userdata.get(), nullptr, 0, &c_error);
2✔
489
            }
2✔
490
            else {
2✔
491
                std::vector<realm_app_user_apikey_t> c_apikeys;
2✔
492
                c_apikeys.reserve(apikeys.size());
2✔
493
                for (const auto& apikey : apikeys) {
2✔
494
                    c_apikeys.push_back(to_capi(apikey));
2✔
495
                }
2✔
496
                callback(userdata.get(), c_apikeys.data(), c_apikeys.size(), nullptr);
2✔
497
            }
2✔
498
        };
4✔
499

2✔
500
        (*app)->provider_client<App::UserAPIKeyProviderClient>().fetch_api_keys(*user, std::move(cb));
4✔
501
        return true;
4✔
502
    });
4✔
503
}
4✔
504

505
RLM_API bool realm_app_user_apikey_provider_client_delete_apikey(const realm_app_t* app, const realm_user_t* user,
506
                                                                 realm_object_id_t id,
507
                                                                 realm_app_void_completion_func_t callback,
508
                                                                 realm_userdata_t userdata,
509
                                                                 realm_free_userdata_func_t userdata_free)
510
{
×
511
    return wrap_err([&] {
×
512
        (*app)->provider_client<App::UserAPIKeyProviderClient>().delete_api_key(
×
513
            from_capi(id), *user, make_callback(callback, userdata, userdata_free));
×
514
        return true;
×
515
    });
×
516
}
×
517

518
RLM_API bool realm_app_user_apikey_provider_client_enable_apikey(const realm_app_t* app, const realm_user_t* user,
519
                                                                 realm_object_id_t id,
520
                                                                 realm_app_void_completion_func_t callback,
521
                                                                 realm_userdata_t userdata,
522
                                                                 realm_free_userdata_func_t userdata_free)
523
{
×
524
    return wrap_err([&] {
×
525
        (*app)->provider_client<App::UserAPIKeyProviderClient>().enable_api_key(
×
526
            from_capi(id), *user, make_callback(callback, userdata, userdata_free));
×
527
        return true;
×
528
    });
×
529
}
×
530

531
RLM_API bool realm_app_user_apikey_provider_client_disable_apikey(const realm_app_t* app, const realm_user_t* user,
532
                                                                  realm_object_id_t id,
533
                                                                  realm_app_void_completion_func_t callback,
534
                                                                  realm_userdata_t userdata,
535
                                                                  realm_free_userdata_func_t userdata_free)
536
{
×
537
    return wrap_err([&] {
×
538
        (*app)->provider_client<App::UserAPIKeyProviderClient>().disable_api_key(
×
539
            from_capi(id), *user, make_callback(callback, userdata, userdata_free));
×
540
        return true;
×
541
    });
×
542
}
×
543

544
RLM_API bool realm_app_push_notification_client_register_device(
545
    const realm_app_t* app, const realm_user_t* user, const char* service_name, const char* registration_token,
546
    realm_app_void_completion_func_t callback, realm_userdata_t userdata, realm_free_userdata_func_t userdata_free)
547
{
×
548
    return wrap_err([&] {
×
549
        (*app)
×
550
            ->push_notification_client(service_name)
×
551
            .register_device(registration_token, *user, make_callback(callback, userdata, userdata_free));
×
552
        return true;
×
553
    });
×
554
}
×
555

556
RLM_API bool realm_app_push_notification_client_deregister_device(const realm_app_t* app, const realm_user_t* user,
557
                                                                  const char* service_name,
558
                                                                  realm_app_void_completion_func_t callback,
559
                                                                  realm_userdata_t userdata,
560
                                                                  realm_free_userdata_func_t userdata_free)
561
{
×
562
    return wrap_err([&] {
×
563
        (*app)
×
564
            ->push_notification_client(service_name)
×
565
            .deregister_device(*user, make_callback(callback, userdata, userdata_free));
×
566
        return true;
×
567
    });
×
568
}
×
569

570
RLM_API bool realm_app_call_function(const realm_app_t* app, const realm_user_t* user, const char* function_name,
571
                                     const char* serialized_ejson_payload, const char* service_name,
572
                                     realm_return_string_func_t callback, realm_userdata_t userdata,
573
                                     realm_free_userdata_func_t userdata_free)
574
{
×
575
    return wrap_err([&] {
×
576
        auto cb = [callback, userdata = SharedUserdata{userdata, FreeUserdata(userdata_free)}](
×
577
                      const std::string* reply, util::Optional<AppError> error) {
×
578
            if (error) {
×
579
                realm_app_error_t c_error(to_capi(*error));
×
580
                callback(userdata.get(), nullptr, &c_error);
×
581
            }
×
582
            else {
×
583
                callback(userdata.get(), reply->c_str(), nullptr);
×
584
            }
×
585
        };
×
586
        util::Optional<std::string> service_name_opt =
×
587
            service_name ? util::some<std::string>(service_name) : util::none;
×
588
        (*app)->call_function(*user, function_name, serialized_ejson_payload, service_name_opt, std::move(cb));
×
589
        return true;
×
590
    });
×
591
}
×
592

593
RLM_API void realm_app_sync_client_reconnect(realm_app_t* app) noexcept
594
{
×
595
    (*app)->sync_manager()->reconnect();
×
596
}
×
597

598
RLM_API bool realm_app_sync_client_has_sessions(const realm_app_t* app) noexcept
599
{
×
600
    return (*app)->sync_manager()->has_existing_sessions();
×
601
}
×
602

603
RLM_API void realm_app_sync_client_wait_for_sessions_to_terminate(realm_app_t* app) noexcept
604
{
×
605
    (*app)->sync_manager()->wait_for_sessions_to_terminate();
×
606
}
×
607

608
RLM_API char* realm_app_sync_client_get_default_file_path_for_realm(const realm_sync_config_t* config,
609
                                                                    const char* custom_filename)
610
{
×
611
    return wrap_err([&]() {
×
612
        util::Optional<std::string> filename =
×
613
            custom_filename ? util::some<std::string>(custom_filename) : util::none;
×
614
        std::string file_path = config->user->sync_manager()->path_for_realm(*config, std::move(filename));
×
615
        return duplicate_string(file_path);
×
616
    });
×
617
}
×
618

619
RLM_API const char* realm_user_get_identity(const realm_user_t* user) noexcept
620
{
×
621
    return (*user)->identity().c_str();
×
622
}
×
623

624
RLM_API realm_user_state_e realm_user_get_state(const realm_user_t* user) noexcept
625
{
4✔
626
    return realm_user_state_e((*user)->state());
4✔
627
}
4✔
628

629
RLM_API bool realm_user_get_all_identities(const realm_user_t* user, realm_user_identity_t* out_identities,
630
                                           size_t max, size_t* out_n)
631
{
2✔
632
    return wrap_err([&] {
2✔
633
        const auto& identities = (*user)->identities();
2✔
634
        set_out_param(out_n, identities.size());
2✔
635
        if (out_identities && max >= identities.size()) {
2✔
636
            for (size_t i = 0; i < identities.size(); i++) {
6✔
637
                out_identities[i] = {duplicate_string(identities[i].id),
4✔
638
                                     realm_auth_provider_e(enum_from_provider_type(identities[i].provider_type))};
4✔
639
            }
4✔
640
        }
2✔
641
        return true;
2✔
642
    });
2✔
643
}
2✔
644

645
RLM_API char* realm_user_get_device_id(const realm_user_t* user) noexcept
646
{
×
647
    if ((*user)->has_device_id()) {
×
648
        return duplicate_string((*user)->device_id());
×
649
    }
×
650

651
    return nullptr;
×
652
}
×
653

654
RLM_API bool realm_user_log_out(realm_user_t* user)
UNCOV
655
{
×
656
    return wrap_err([&] {
×
657
        (*user)->log_out();
×
658
        return true;
×
659
    });
×
660
}
×
661

662
RLM_API bool realm_user_is_logged_in(const realm_user_t* user) noexcept
663
{
×
664
    return (*user)->is_logged_in();
×
665
}
×
666

667
RLM_API char* realm_user_get_profile_data(const realm_user_t* user)
668
{
×
669
    return wrap_err([&] {
×
670
        std::string data = bson::Bson((*user)->user_profile().data()).to_string();
×
671
        return duplicate_string(data);
×
672
    });
×
673
}
×
674

675
RLM_API char* realm_user_get_custom_data(const realm_user_t* user) noexcept
676
{
×
677
    if (const auto& data = (*user)->custom_data()) {
×
678
        std::string json = bson::Bson(*data).to_string();
×
679
        return duplicate_string(json);
×
680
    }
×
681

682
    return nullptr;
×
683
}
×
684

685
RLM_API char* realm_user_get_access_token(const realm_user_t* user)
686
{
×
687
    return wrap_err([&] {
×
688
        return duplicate_string((*user)->access_token());
×
689
    });
×
690
}
×
691

692
RLM_API char* realm_user_get_refresh_token(const realm_user_t* user)
693
{
×
694
    return wrap_err([&] {
×
695
        return duplicate_string((*user)->refresh_token());
×
696
    });
×
697
}
×
698

699
RLM_API realm_app_t* realm_user_get_app(const realm_user_t* user) noexcept
700
{
×
701
    REALM_ASSERT(user);
×
702
    try {
×
703
        if (auto shared_app = (*user)->sync_manager()->app().lock()) {
×
704
            return new realm_app_t(shared_app);
×
705
        }
×
706
    }
×
707
    catch (const std::exception&) {
×
708
    }
×
709
    return nullptr;
×
710
}
×
711

712
template <typename T>
713
inline util::Optional<T> convert_to_optional(T data)
714
{
×
715
    return data ? util::Optional<T>(data) : util::Optional<T>();
×
716
}
×
717

718
template <typename T>
719
inline util::Optional<T> convert_to_optional_bson(realm_string_t doc)
720
{
×
721
    if (doc.data == nullptr || doc.size == 0) {
×
722
        return util::Optional<T>();
×
723
    }
×
724
    return util::Optional<T>(static_cast<T>(bson::parse({doc.data, doc.size})));
×
725
}
×
726

727
template <typename T>
728
inline T convert_to_bson(realm_string_t doc)
729
{
×
730
    auto res = convert_to_optional_bson<T>(doc);
×
731
    return res ? *res : T();
×
732
}
×
733

734
static MongoCollection::FindOptions to_mongodb_collection_find_options(const realm_mongodb_find_options_t* options)
735
{
×
736
    MongoCollection::FindOptions mongodb_options;
×
737
    mongodb_options.projection_bson = convert_to_optional_bson<bson::BsonDocument>(options->projection_bson);
×
738
    mongodb_options.sort_bson = convert_to_optional_bson<bson::BsonDocument>(options->sort_bson);
×
739
    mongodb_options.limit = convert_to_optional(options->limit);
×
740
    return mongodb_options;
×
741
}
×
742

743
static MongoCollection::FindOneAndModifyOptions
744
to_mongodb_collection_find_one_and_modify_options(const realm_mongodb_find_one_and_modify_options_t* options)
745
{
×
746
    MongoCollection::FindOneAndModifyOptions mongodb_options;
×
747
    mongodb_options.projection_bson = convert_to_optional_bson<bson::BsonDocument>(options->projection_bson);
×
748
    mongodb_options.sort_bson = convert_to_optional_bson<bson::BsonDocument>(options->sort_bson);
×
749
    mongodb_options.upsert = options->upsert;
×
750
    mongodb_options.return_new_document = options->return_new_document;
×
751
    return mongodb_options;
×
752
}
×
753

754
static void handle_mongodb_collection_result(util::Optional<bson::Bson> bson, util::Optional<AppError> app_error,
755
                                             UserdataPtr data, realm_mongodb_callback_t callback)
756
{
×
757
    if (app_error) {
×
758
        auto error = to_capi(*app_error);
×
759
        callback(data.get(), {nullptr, 0}, &error);
×
760
    }
×
761
    else if (bson) {
×
762
        const auto& bson_data = bson->to_string();
×
763
        callback(data.get(), {bson_data.c_str(), bson_data.size()}, nullptr);
×
764
    }
×
765
}
×
766

767
RLM_API realm_mongodb_collection_t* realm_mongo_collection_get(realm_user_t* user, const char* service,
768
                                                               const char* database, const char* collection)
769
{
×
770
    REALM_ASSERT(user);
×
771
    REALM_ASSERT(service);
×
772
    REALM_ASSERT(database);
×
773
    REALM_ASSERT(collection);
×
774
    return wrap_err([&]() {
×
775
        auto col = (*user)->mongo_client(service).db(database).collection(collection);
×
776
        return new realm_mongodb_collection_t(col);
×
777
    });
×
778
}
×
779

780
RLM_API bool realm_mongo_collection_find(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
781
                                         const realm_mongodb_find_options_t* options, realm_userdata_t data,
782
                                         realm_free_userdata_func_t delete_data, realm_mongodb_callback_t callback)
783
{
×
784
    REALM_ASSERT(collection);
×
785
    REALM_ASSERT(options);
×
786
    return wrap_err([&] {
×
787
        collection->find_bson(convert_to_bson<bson::BsonDocument>(filter_ejson),
×
788
                              to_mongodb_collection_find_options(options),
×
789
                              [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
790
                                  handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
791
                              });
×
792
        return true;
×
793
    });
×
794
}
×
795

796
RLM_API bool realm_mongo_collection_find_one(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
797
                                             const realm_mongodb_find_options_t* options, realm_userdata_t data,
798
                                             realm_free_userdata_func_t delete_data,
799
                                             realm_mongodb_callback_t callback)
800
{
×
801
    REALM_ASSERT(collection);
×
802
    REALM_ASSERT(options);
×
803
    return wrap_err([&] {
×
804
        collection->find_one_bson(
×
805
            convert_to_bson<bson::BsonDocument>(filter_ejson), to_mongodb_collection_find_options(options),
×
806
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
807
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
808
            });
×
809
        return true;
×
810
    });
×
811
}
×
812

813
RLM_API bool realm_mongo_collection_aggregate(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
814
                                              realm_userdata_t data, realm_free_userdata_func_t delete_data,
815
                                              realm_mongodb_callback_t callback)
816
{
×
817
    REALM_ASSERT(collection);
×
818
    return wrap_err([&] {
×
819
        collection->aggregate_bson(
×
820
            convert_to_bson<bson::BsonArray>(filter_ejson),
×
821
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
822
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
823
            });
×
824
        return true;
×
825
    });
×
826
}
×
827

828
RLM_API bool realm_mongo_collection_count(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
829
                                          int64_t limit, realm_userdata_t data,
830
                                          realm_free_userdata_func_t delete_data, realm_mongodb_callback_t callback)
831
{
×
832
    REALM_ASSERT(collection);
×
833
    return wrap_err([&] {
×
834
        collection->count_bson(convert_to_bson<bson::BsonDocument>(filter_ejson), limit,
×
835
                               [=](util::Optional<bson::Bson> bson, util::Optional<app::AppError> app_error) {
×
836
                                   handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
837
                               });
×
838
        return true;
×
839
    });
×
840
}
×
841

842
RLM_API bool realm_mongo_collection_insert_one(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
843
                                               realm_userdata_t data, realm_free_userdata_func_t delete_data,
844
                                               realm_mongodb_callback_t callback)
845
{
×
846
    REALM_ASSERT(collection);
×
847
    return wrap_err([&] {
×
848
        collection->insert_one_bson(
×
849
            convert_to_bson<bson::BsonDocument>(filter_ejson),
×
850
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
851
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
852
            });
×
853
        return true;
×
854
    });
×
855
}
×
856

857
RLM_API bool realm_mongo_collection_insert_many(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
858
                                                realm_userdata_t data, realm_free_userdata_func_t delete_data,
859
                                                realm_mongodb_callback_t callback)
860
{
×
861
    REALM_ASSERT(collection);
×
862
    return wrap_err([&] {
×
863
        collection->insert_many_bson(
×
864
            convert_to_bson<bson::BsonArray>(filter_ejson),
×
865
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
866
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
867
            });
×
868
        return true;
×
869
    });
×
870
}
×
871

872
RLM_API bool realm_mongo_collection_delete_one(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
873
                                               realm_userdata_t data, realm_free_userdata_func_t delete_data,
874
                                               realm_mongodb_callback_t callback)
875
{
×
876
    REALM_ASSERT(collection);
×
877
    return wrap_err([&] {
×
878
        collection->delete_one_bson(
×
879
            convert_to_bson<bson::BsonDocument>(filter_ejson),
×
880
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
881
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
882
            });
×
883
        return true;
×
884
    });
×
885
}
×
886

887
RLM_API bool realm_mongo_collection_delete_many(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
888
                                                realm_userdata_t data, realm_free_userdata_func_t delete_data,
889
                                                realm_mongodb_callback_t callback)
890
{
×
891
    REALM_ASSERT(collection);
×
892
    return wrap_err([&] {
×
893
        collection->delete_many_bson(
×
894
            convert_to_bson<bson::BsonDocument>(filter_ejson),
×
895
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
896
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
897
            });
×
898
        return true;
×
899
    });
×
900
}
×
901

902
RLM_API bool realm_mongo_collection_update_one(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
903
                                               realm_string_t update_ejson, bool upsert, realm_userdata_t data,
904
                                               realm_free_userdata_func_t delete_data,
905
                                               realm_mongodb_callback_t callback)
906
{
×
907
    REALM_ASSERT(collection);
×
908
    return wrap_err([&] {
×
909
        const auto& bson_filter = convert_to_bson<bson::BsonDocument>(filter_ejson);
×
910
        const auto& bson_update = convert_to_bson<bson::BsonDocument>(update_ejson);
×
911
        collection->update_one_bson(
×
912
            bson_filter, bson_update, upsert,
×
913
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
914
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
915
            });
×
916
        return true;
×
917
    });
×
918
}
×
919

920
RLM_API bool realm_mongo_collection_update_many(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
921
                                                realm_string_t update_ejson, bool upsert, realm_userdata_t data,
922
                                                realm_free_userdata_func_t delete_data,
923
                                                realm_mongodb_callback_t callback)
924
{
×
925
    REALM_ASSERT(collection);
×
926
    return wrap_err([&] {
×
927
        const auto& bson_filter = convert_to_bson<bson::BsonDocument>(filter_ejson);
×
928
        const auto& bson_update = convert_to_bson<bson::BsonDocument>(update_ejson);
×
929
        collection->update_many_bson(
×
930
            bson_filter, bson_update, upsert,
×
931
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
932
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
933
            });
×
934
        return true;
×
935
    });
×
936
}
×
937

938
RLM_API bool realm_mongo_collection_find_one_and_update(realm_mongodb_collection_t* collection,
939
                                                        realm_string_t filter_ejson, realm_string_t update_ejson,
940
                                                        const realm_mongodb_find_one_and_modify_options_t* options,
941
                                                        realm_userdata_t data, realm_free_userdata_func_t delete_data,
942
                                                        realm_mongodb_callback_t callback)
943
{
×
944
    REALM_ASSERT(collection);
×
945
    return wrap_err([&] {
×
946
        const auto& bson_filter = convert_to_bson<bson::BsonDocument>(filter_ejson);
×
947
        const auto& bson_update = convert_to_bson<bson::BsonDocument>(update_ejson);
×
948
        collection->find_one_and_update_bson(
×
949
            bson_filter, bson_update, to_mongodb_collection_find_one_and_modify_options(options),
×
950
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
951
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
952
            });
×
953
        return true;
×
954
    });
×
955
}
×
956

957
RLM_API bool realm_mongo_collection_find_one_and_replace(
958
    realm_mongodb_collection_t* collection, realm_string_t filter_ejson, realm_string_t replacement_ejson,
959
    const realm_mongodb_find_one_and_modify_options_t* options, realm_userdata_t data,
960
    realm_free_userdata_func_t delete_data, realm_mongodb_callback_t callback)
961
{
×
962
    REALM_ASSERT(collection);
×
963
    return wrap_err([&] {
×
964
        const auto& filter_bson = convert_to_bson<bson::BsonDocument>(filter_ejson);
×
965
        const auto& replacement_bson = convert_to_bson<bson::BsonDocument>(replacement_ejson);
×
966
        collection->find_one_and_replace_bson(
×
967
            filter_bson, replacement_bson, to_mongodb_collection_find_one_and_modify_options(options),
×
968
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
969
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
970
            });
×
971
        return true;
×
972
    });
×
973
}
×
974

975
RLM_API bool realm_mongo_collection_find_one_and_delete(realm_mongodb_collection_t* collection,
976
                                                        realm_string_t filter_ejson,
977
                                                        const realm_mongodb_find_one_and_modify_options_t* options,
978
                                                        realm_userdata_t data, realm_free_userdata_func_t delete_data,
979
                                                        realm_mongodb_callback_t callback)
980
{
×
981
    REALM_ASSERT(collection);
×
982
    return wrap_err([&] {
×
983
        const auto& bson_filter = convert_to_bson<bson::BsonDocument>(filter_ejson);
×
984
        collection->find_one_and_delete_bson(
×
985
            bson_filter, to_mongodb_collection_find_one_and_modify_options(options),
×
986
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
987
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
988
            });
×
989
        return true;
×
990
    });
×
991
}
×
992

993
} // namespace realm::c_api
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