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

realm / realm-core / 2273

29 Apr 2024 05:54PM UTC coverage: 90.748% (+0.008%) from 90.74%
2273

push

Evergreen

web-flow
glue code for CAPI custom user via callbacks (#7615)

* glue code for CAPI custom user via callbacks

* review feedback

* remove request_refresh_user from the SyncUser interface, it is unused

* further feedback addressed

101982 of 180246 branches covered (56.58%)

10 of 39 new or added lines in 2 files covered. (25.64%)

39 existing lines in 7 files now uncovered.

212481 of 234145 relevant lines covered (90.75%)

5777063.68 hits per line

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

34.52
/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
#if REALM_APP_SERVICES
35

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

46
static_assert(realm_sync_client_metadata_mode_e(app::AppConfig::MetadataMode::NoEncryption) ==
47
              RLM_SYNC_CLIENT_METADATA_MODE_PLAINTEXT);
48
static_assert(realm_sync_client_metadata_mode_e(app::AppConfig::MetadataMode::Encryption) ==
49
              RLM_SYNC_CLIENT_METADATA_MODE_ENCRYPTED);
50
static_assert(realm_sync_client_metadata_mode_e(app::AppConfig::MetadataMode::InMemory) ==
51
              RLM_SYNC_CLIENT_METADATA_MODE_DISABLED);
52

53
static inline bson::BsonArray parse_ejson_array(const char* serialized)
54
{
×
55
    if (!serialized) {
×
56
        return {};
×
57
    }
×
58
    else {
×
59
        return bson::BsonArray(bson::parse({serialized, strlen(serialized)}));
×
60
    }
×
61
}
×
62

63
static realm_app_error_t to_capi(const AppError& error)
64
{
2✔
65
    auto ret = realm_app_error_t();
2✔
66

67
    ret.error = realm_errno_e(error.code());
2✔
68
    ret.categories = ErrorCodes::error_categories(error.code()).value();
2✔
69

70
    if (error.additional_status_code) {
2✔
71
        ret.http_status_code = *error.additional_status_code;
2✔
72
    }
2✔
73

74
    ret.message = error.what();
2✔
75

76
    if (error.link_to_server_logs.size() > 0) {
2✔
77
        ret.link_to_server_logs = error.link_to_server_logs.c_str();
×
78
    }
×
79

80
    return ret;
2✔
81
}
2✔
82

83
static inline realm_app_user_apikey_t to_capi(const App::UserAPIKey& apikey)
84
{
4✔
85
    return {to_capi(apikey.id), apikey.key ? apikey.key->c_str() : nullptr, apikey.name.c_str(), apikey.disabled};
4✔
86
}
4✔
87

88
static inline auto make_callback(realm_app_void_completion_func_t callback, realm_userdata_t userdata,
89
                                 realm_free_userdata_func_t userdata_free)
90
{
26✔
91
    return
26✔
92
        [callback, userdata = SharedUserdata(userdata, FreeUserdata(userdata_free))](util::Optional<AppError> error) {
26✔
93
            if (error) {
26✔
94
                realm_app_error_t c_err{to_capi(*error)};
×
95
                callback(userdata.get(), &c_err);
×
96
            }
×
97
            else {
26✔
98
                callback(userdata.get(), nullptr);
26✔
99
            }
26✔
100
        };
26✔
101
}
26✔
102

103
static inline auto make_callback(realm_app_user_completion_func_t callback, realm_userdata_t userdata,
104
                                 realm_free_userdata_func_t userdata_free)
105
{
14✔
106
    return [callback, userdata = SharedUserdata(userdata, FreeUserdata(userdata_free))](
14✔
107
               std::shared_ptr<app::User> user, util::Optional<AppError> error) {
14✔
108
        if (error) {
14✔
109
            realm_app_error_t c_err{to_capi(*error)};
×
110
            callback(userdata.get(), nullptr, &c_err);
×
111
        }
×
112
        else {
14✔
113
            auto c_user = realm_user_t(std::move(user));
14✔
114
            callback(userdata.get(), &c_user, nullptr);
14✔
115
        }
14✔
116
    };
14✔
117
}
14✔
118

119
static inline auto make_callback(void (*callback)(realm_userdata_t userdata, realm_app_user_apikey_t*,
120
                                                  const realm_app_error_t*),
121
                                 realm_userdata_t userdata, realm_free_userdata_func_t userdata_free)
122
{
2✔
123
    return [callback, userdata = SharedUserdata(userdata, FreeUserdata(userdata_free))](
2✔
124
               App::UserAPIKey apikey, util::Optional<AppError> error) {
2✔
125
        if (error) {
2✔
126
            realm_app_error_t c_error(to_capi(*error));
×
127
            callback(userdata.get(), nullptr, &c_error);
×
128
        }
×
129
        else {
2✔
130
            realm_app_user_apikey_t c_apikey(to_capi(apikey));
2✔
131
            callback(userdata.get(), &c_apikey, nullptr);
2✔
132
        }
2✔
133
    };
2✔
134
}
2✔
135

136
RLM_API const char* realm_app_get_default_base_url(void) noexcept
137
{
2✔
138
    return app::App::default_base_url().data();
2✔
139
}
2✔
140

141
RLM_API realm_app_credentials_t* realm_app_credentials_new_anonymous(bool reuse_credentials) noexcept
142
{
2✔
143
    return new realm_app_credentials_t(AppCredentials::anonymous(reuse_credentials));
2✔
144
}
2✔
145

146
RLM_API realm_app_credentials_t* realm_app_credentials_new_facebook(const char* access_token) noexcept
147
{
×
148
    return new realm_app_credentials_t(AppCredentials::facebook(access_token));
×
149
}
×
150

151
RLM_API realm_app_credentials_t* realm_app_credentials_new_google_id_token(const char* id_token) noexcept
152
{
×
153
    return new realm_app_credentials_t(AppCredentials::google(IdToken(id_token)));
×
154
}
×
155

156
RLM_API realm_app_credentials_t* realm_app_credentials_new_google_auth_code(const char* auth_code) noexcept
157
{
×
158
    return new realm_app_credentials_t(AppCredentials::google(AuthCode(auth_code)));
×
159
}
×
160

161
RLM_API realm_app_credentials_t* realm_app_credentials_new_apple(const char* id_token) noexcept
162
{
×
163
    return new realm_app_credentials_t(AppCredentials::apple(id_token));
×
164
}
×
165

166
RLM_API realm_app_credentials_t* realm_app_credentials_new_jwt(const char* jwt_token) noexcept
167
{
×
168
    return new realm_app_credentials_t(AppCredentials::custom(jwt_token));
×
169
}
×
170

171
RLM_API realm_app_credentials_t* realm_app_credentials_new_email_password(const char* email,
172
                                                                          realm_string_t password) noexcept
173
{
×
174
    return new realm_app_credentials_t(AppCredentials::username_password(email, from_capi(password)));
×
175
}
×
176

177
RLM_API realm_app_credentials_t* realm_app_credentials_new_function(const char* serialized_ejson_payload)
178
{
×
179
    return wrap_err([&] {
×
180
        return new realm_app_credentials_t(AppCredentials::function(serialized_ejson_payload));
×
181
    });
×
182
}
×
183

184
RLM_API realm_app_credentials_t* realm_app_credentials_new_api_key(const char* api_key) noexcept
185
{
×
186
    return new realm_app_credentials_t(AppCredentials::api_key(api_key));
×
187
}
×
188

189
RLM_API realm_auth_provider_e realm_auth_credentials_get_provider(realm_app_credentials_t* credentials) noexcept
190
{
×
191
    return realm_auth_provider_e(credentials->provider());
×
192
}
×
193

194
RLM_API realm_app_config_t* realm_app_config_new(const char* app_id,
195
                                                 const realm_http_transport_t* http_transport) noexcept
196
{
2✔
197
    auto* config = new realm_app_config_t;
2✔
198
    config->app_id = app_id;
2✔
199
    config->transport = *http_transport; // realm_http_transport_t is a shared_ptr
2✔
200
    return config;
2✔
201
}
2✔
202

203
RLM_API void realm_app_config_set_base_url(realm_app_config_t* config, const char* base_url) noexcept
204
{
2✔
205
    config->base_url = std::string(base_url);
2✔
206
}
2✔
207

208
RLM_API void realm_app_config_set_default_request_timeout(realm_app_config_t* config, uint64_t ms) noexcept
209
{
2✔
210
    config->default_request_timeout_ms = ms;
2✔
211
}
2✔
212

213
RLM_API void realm_app_config_set_platform_version(realm_app_config_t* config, const char* platform_version) noexcept
214
{
2✔
215
    config->device_info.platform_version = std::string(platform_version);
2✔
216
}
2✔
217

218
RLM_API void realm_app_config_set_sdk_version(realm_app_config_t* config, const char* sdk_version) noexcept
219
{
2✔
220
    config->device_info.sdk_version = std::string(sdk_version);
2✔
221
}
2✔
222

223
RLM_API void realm_app_config_set_sdk(realm_app_config_t* config, const char* sdk) noexcept
224
{
2✔
225
    config->device_info.sdk = std::string(sdk);
2✔
226
}
2✔
227

228
RLM_API void realm_app_config_set_device_name(realm_app_config_t* config, const char* device_name) noexcept
229
{
2✔
230
    config->device_info.device_name = std::string(device_name);
2✔
231
}
2✔
232

233
RLM_API void realm_app_config_set_device_version(realm_app_config_t* config, const char* device_version) noexcept
234
{
2✔
235
    config->device_info.device_version = std::string(device_version);
2✔
236
}
2✔
237

238
RLM_API void realm_app_config_set_framework_name(realm_app_config_t* config, const char* framework_name) noexcept
239
{
2✔
240
    config->device_info.framework_name = std::string(framework_name);
2✔
241
}
2✔
242

243
RLM_API void realm_app_config_set_framework_version(realm_app_config_t* config,
244
                                                    const char* framework_version) noexcept
245
{
2✔
246
    config->device_info.framework_version = std::string(framework_version);
2✔
247
}
2✔
248

249
RLM_API void realm_app_config_set_bundle_id(realm_app_config_t* config, const char* bundle_id) noexcept
250
{
2✔
251
    config->device_info.bundle_id = std::string(bundle_id);
2✔
252
}
2✔
253

254
RLM_API void realm_app_config_set_base_file_path(realm_app_config_t* config, const char* path) noexcept
255
{
4✔
256
    config->base_file_path = path;
4✔
257
}
4✔
258

259
RLM_API void realm_app_config_set_metadata_mode(realm_app_config_t* config,
260
                                                realm_sync_client_metadata_mode_e mode) noexcept
261
{
8✔
262
    config->metadata_mode = app::AppConfig::MetadataMode(mode);
8✔
263
}
8✔
264

265
RLM_API void realm_app_config_set_metadata_encryption_key(realm_app_config_t* config, const uint8_t key[64]) noexcept
266
{
2✔
267
    config->custom_encryption_key = std::vector<char>(key, key + 64);
2✔
268
}
2✔
269

270
RLM_API void realm_app_config_set_security_access_group(realm_app_config_t* config, const char* group) noexcept
271
{
4✔
272
    config->security_access_group = group;
4✔
273
}
4✔
274

275
RLM_API const char* realm_app_credentials_serialize_as_json(realm_app_credentials_t* app_credentials) noexcept
276
{
×
277
    return wrap_err([&] {
×
278
        return duplicate_string(app_credentials->serialize_as_json());
×
279
    });
×
280
}
×
281

282
RLM_API realm_app_t* realm_app_create(const realm_app_config_t* app_config)
283
{
2✔
284
    return wrap_err([&] {
2✔
285
        return new realm_app_t(App::get_app(app::App::CacheMode::Disabled, *app_config));
2✔
286
    });
2✔
287
}
2✔
288

289
RLM_API realm_app_t* realm_app_create_cached(const realm_app_config_t* app_config)
290
{
×
291
    return wrap_err([&] {
×
292
        return new realm_app_t(App::get_app(app::App::CacheMode::Enabled, *app_config));
×
293
    });
×
294
}
×
295

296
RLM_API bool realm_app_get_cached(const char* app_id, const char* base_url, realm_app_t** out_app)
297
{
×
298
    return wrap_err([&] {
×
299
        auto app =
×
300
            App::get_cached_app(std::string(app_id), base_url ? util::some<std::string>(base_url) : util::none);
×
301
        if (out_app) {
×
302
            *out_app = app ? new realm_app_t(app) : nullptr;
×
303
        }
×
304

305
        return true;
×
306
    });
×
307
}
×
308

309
RLM_API void realm_clear_cached_apps(void) noexcept
310
{
×
311
    App::clear_cached_apps();
×
312
}
×
313

314
RLM_API const char* realm_app_get_app_id(const realm_app_t* app) noexcept
315
{
×
316
    return (*app)->app_id().c_str();
×
317
}
×
318

319
RLM_API bool realm_app_update_base_url(realm_app_t* app, const char* base_url,
320
                                       realm_app_void_completion_func_t callback, realm_userdata_t userdata,
321
                                       realm_free_userdata_func_t userdata_free)
322
{
6✔
323
    return wrap_err([&] {
6✔
324
        (*app)->update_base_url(base_url ? base_url : "", make_callback(callback, userdata, userdata_free));
6✔
325
        return true;
6✔
326
    });
6✔
327
}
6✔
328

329
RLM_API char* realm_app_get_base_url(realm_app_t* app) noexcept
330
{
8✔
331
    auto url_stg = (*app)->get_base_url();
8✔
332
    return duplicate_string(url_stg);
8✔
333
}
8✔
334

335
RLM_API realm_user_t* realm_app_get_current_user(const realm_app_t* app) noexcept
336
{
4✔
337
    if (auto user = (*app)->current_user()) {
4✔
338
        return new realm_user_t(user);
4✔
339
    }
4✔
340

341
    return nullptr;
×
342
}
4✔
343

344
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)
345
{
4✔
346
    return wrap_err([&] {
4✔
347
        const auto& users = (*app)->all_users();
4✔
348
        set_out_param(out_n, users.size());
4✔
349
        if (out_users && capacity >= users.size()) {
4✔
350
            OutBuffer<realm_user_t> buf(out_users);
2✔
351
            for (const auto& user : users) {
4✔
352
                buf.emplace(user);
4✔
353
            }
4✔
354
            buf.release(out_n);
2✔
355
        }
2✔
356
        return true;
4✔
357
    });
4✔
358
}
4✔
359

360
RLM_API bool realm_app_log_in_with_credentials(realm_app_t* app, realm_app_credentials_t* credentials,
361
                                               realm_app_user_completion_func_t callback, realm_userdata_t userdata,
362
                                               realm_free_userdata_func_t userdata_free)
363
{
12✔
364
    return wrap_err([&] {
12✔
365
        (*app)->log_in_with_credentials(*credentials, make_callback(callback, userdata, userdata_free));
12✔
366
        return true;
12✔
367
    });
12✔
368
}
12✔
369

370
RLM_API bool realm_app_log_out_current_user(realm_app_t* app, realm_app_void_completion_func_t callback,
371
                                            realm_userdata_t userdata, realm_free_userdata_func_t userdata_free)
372
{
×
373
    return wrap_err([&] {
×
374
        (*app)->log_out(make_callback(callback, userdata, userdata_free));
×
375
        return true;
×
376
    });
×
377
}
×
378

379
namespace {
380
template <typename Fn>
381
auto with_app_user(const realm_user_t* user, Fn&& fn)
382
{
24✔
383
    auto app_user = std::dynamic_pointer_cast<realm::app::User>(*user);
24✔
384
    return wrap_err([&] {
24✔
385
        if (!app_user) {
24!
386
            throw Exception(ErrorCodes::InvalidArgument, "App Services function require a user obtained from an App");
×
387
        }
×
388
        if constexpr (std::is_void_v<decltype(fn(app_user))>) {
24✔
389
            fn(app_user);
12✔
390
            return true;
12✔
391
        }
1✔
392
        else {
2✔
393
            return fn(app_user);
2✔
394
        }
2✔
395
    });
24✔
396
}
24✔
397
} // anonymous namespace
398

399
RLM_API bool realm_app_refresh_custom_data(realm_app_t* app, realm_user_t* user,
400
                                           realm_app_void_completion_func_t callback, realm_userdata_t userdata,
401
                                           realm_free_userdata_func_t userdata_free)
402
{
6✔
403
    return with_app_user(user, [&](auto& user) {
6✔
404
        (*app)->refresh_custom_data(user, make_callback(callback, userdata, userdata_free));
6✔
405
    });
6✔
406
}
6✔
407

408
RLM_API bool realm_app_log_out(realm_app_t* app, realm_user_t* user, realm_app_void_completion_func_t callback,
409
                               realm_userdata_t userdata, realm_free_userdata_func_t userdata_free)
410
{
×
411
    return with_app_user(user, [&](auto& user) {
×
412
        (*app)->log_out(user, make_callback(callback, userdata, userdata_free));
×
413
    });
×
414
}
×
415

416
RLM_API bool realm_app_link_user(realm_app_t* app, realm_user_t* user, realm_app_credentials_t* credentials,
417
                                 realm_app_user_completion_func_t callback, realm_userdata_t userdata,
418
                                 realm_free_userdata_func_t userdata_free)
419
{
2✔
420
    return with_app_user(user, [&](auto& user) {
2✔
421
        (*app)->link_user(user, *credentials, make_callback(callback, userdata, userdata_free));
2✔
422
    });
2✔
423
}
2✔
424

425
RLM_API bool realm_app_switch_user(realm_app_t* app, realm_user_t* user)
426
{
2✔
427
    return with_app_user(user, [&](auto& user) {
2✔
428
        (*app)->switch_user(user);
2✔
429
    });
2✔
430
}
2✔
431

432
RLM_API bool realm_app_remove_user(realm_app_t* app, realm_user_t* user, realm_app_void_completion_func_t callback,
433
                                   realm_userdata_t userdata, realm_free_userdata_func_t userdata_free)
434
{
2✔
435
    return with_app_user(user, [&](auto& user) {
2✔
436
        (*app)->remove_user(user, make_callback(callback, userdata, userdata_free));
2✔
437
    });
2✔
438
}
2✔
439

440
RLM_API bool realm_app_delete_user(realm_app_t* app, realm_user_t* user, realm_app_void_completion_func_t callback,
441
                                   realm_userdata_t userdata, realm_free_userdata_func_t userdata_free)
442
{
2✔
443
    return with_app_user(user, [&](auto& user) {
2✔
444
        (*app)->delete_user(user, make_callback(callback, userdata, userdata_free));
2✔
445
    });
2✔
446
}
2✔
447

448
RLM_API bool realm_app_email_password_provider_client_register_email(realm_app_t* app, const char* email,
449
                                                                     realm_string_t password,
450
                                                                     realm_app_void_completion_func_t callback,
451
                                                                     realm_userdata_t userdata,
452
                                                                     realm_free_userdata_func_t userdata_free)
453
{
10✔
454
    return wrap_err([&] {
10✔
455
        (*app)->provider_client<App::UsernamePasswordProviderClient>().register_email(
10✔
456
            email, from_capi(password), make_callback(callback, userdata, userdata_free));
10✔
457
        return true;
10✔
458
    });
10✔
459
}
10✔
460

461
RLM_API bool realm_app_email_password_provider_client_confirm_user(realm_app_t* app, const char* token,
462
                                                                   const char* token_id,
463
                                                                   realm_app_void_completion_func_t callback,
464
                                                                   realm_userdata_t userdata,
465
                                                                   realm_free_userdata_func_t userdata_free)
466
{
×
467
    return wrap_err([&] {
×
468
        (*app)->provider_client<App::UsernamePasswordProviderClient>().confirm_user(
×
469
            token, token_id, make_callback(callback, userdata, userdata_free));
×
470
        return true;
×
471
    });
×
472
}
×
473

474
RLM_API bool realm_app_email_password_provider_client_resend_confirmation_email(
475
    realm_app_t* app, const char* email, realm_app_void_completion_func_t callback, realm_userdata_t userdata,
476
    realm_free_userdata_func_t userdata_free)
477
{
×
478
    return wrap_err([&] {
×
479
        (*app)->provider_client<App::UsernamePasswordProviderClient>().resend_confirmation_email(
×
480
            email, make_callback(callback, userdata, userdata_free));
×
481
        return true;
×
482
    });
×
483
}
×
484

485
RLM_API bool realm_app_email_password_provider_client_send_reset_password_email(
486
    realm_app_t* app, const char* email, realm_app_void_completion_func_t callback, realm_userdata_t userdata,
487
    realm_free_userdata_func_t userdata_free)
488
{
×
489
    return wrap_err([&] {
×
490
        (*app)->provider_client<App::UsernamePasswordProviderClient>().send_reset_password_email(
×
491
            email, make_callback(callback, userdata, userdata_free));
×
492
        return true;
×
493
    });
×
494
}
×
495

496
RLM_API bool realm_app_email_password_provider_client_retry_custom_confirmation(
497
    realm_app_t* app, const char* email, realm_app_void_completion_func_t callback, realm_userdata_t userdata,
498
    realm_free_userdata_func_t userdata_free)
499
{
×
500
    return wrap_err([&] {
×
501
        (*app)->provider_client<App::UsernamePasswordProviderClient>().retry_custom_confirmation(
×
502
            email, make_callback(callback, userdata, userdata_free));
×
503
        return true;
×
504
    });
×
505
}
×
506

507
RLM_API bool realm_app_email_password_provider_client_reset_password(realm_app_t* app, realm_string_t password,
508
                                                                     const char* token, const char* token_id,
509
                                                                     realm_app_void_completion_func_t callback,
510
                                                                     realm_userdata_t userdata,
511
                                                                     realm_free_userdata_func_t userdata_free)
512
{
×
513
    return wrap_err([&] {
×
514
        (*app)->provider_client<App::UsernamePasswordProviderClient>().reset_password(
×
515
            from_capi(password), token, token_id, make_callback(callback, userdata, userdata_free));
×
516
        return true;
×
517
    });
×
518
}
×
519

520
RLM_API bool realm_app_email_password_provider_client_call_reset_password_function(
521
    realm_app_t* app, const char* email, realm_string_t password, const char* serialized_ejson_payload,
522
    realm_app_void_completion_func_t callback, realm_userdata_t userdata, realm_free_userdata_func_t userdata_free)
523
{
×
524
    return wrap_err([&] {
×
525
        bson::BsonArray args = parse_ejson_array(serialized_ejson_payload);
×
526
        (*app)->provider_client<App::UsernamePasswordProviderClient>().call_reset_password_function(
×
527
            email, from_capi(password), args, make_callback(callback, userdata, userdata_free));
×
528
        return true;
×
529
    });
×
530
}
×
531

532
RLM_API bool realm_app_user_apikey_provider_client_create_apikey(const realm_app_t* app, const realm_user_t* user,
533
                                                                 const char* name,
534
                                                                 realm_return_apikey_func_t callback,
535
                                                                 realm_userdata_t userdata,
536
                                                                 realm_free_userdata_func_t userdata_free)
537
{
2✔
538
    return with_app_user(user, [&](auto& user) {
2✔
539
        (*app)->provider_client<App::UserAPIKeyProviderClient>().create_api_key(
2✔
540
            name, user, make_callback(callback, userdata, userdata_free));
2✔
541
    });
2✔
542
}
2✔
543

544
RLM_API bool realm_app_user_apikey_provider_client_fetch_apikey(const realm_app_t* app, const realm_user_t* user,
545
                                                                realm_object_id_t id,
546
                                                                realm_return_apikey_func_t callback,
547
                                                                realm_userdata_t userdata,
548
                                                                realm_free_userdata_func_t userdata_free)
549
{
×
550
    return with_app_user(user, [&](auto& user) {
×
551
        (*app)->provider_client<App::UserAPIKeyProviderClient>().fetch_api_key(
×
552
            from_capi(id), user, make_callback(callback, userdata, userdata_free));
×
553
    });
×
554
}
×
555

556
RLM_API bool realm_app_user_apikey_provider_client_fetch_apikeys(const realm_app_t* app, const realm_user_t* user,
557
                                                                 realm_return_apikey_list_func_t callback,
558
                                                                 realm_userdata_t userdata,
559
                                                                 realm_free_userdata_func_t userdata_free)
560
{
4✔
561
    return with_app_user(user, [&](auto& user) {
4✔
562
        auto cb = [callback, userdata = SharedUserdata{userdata, FreeUserdata(userdata_free)}](
4✔
563
                      std::vector<App::UserAPIKey> apikeys, util::Optional<AppError> error) {
4✔
564
            if (error) {
4✔
565
                realm_app_error_t c_error(to_capi(*error));
2✔
566
                callback(userdata.get(), nullptr, 0, &c_error);
2✔
567
            }
2✔
568
            else {
2✔
569
                std::vector<realm_app_user_apikey_t> c_apikeys;
2✔
570
                c_apikeys.reserve(apikeys.size());
2✔
571
                for (const auto& apikey : apikeys) {
2✔
572
                    c_apikeys.push_back(to_capi(apikey));
2✔
573
                }
2✔
574
                callback(userdata.get(), c_apikeys.data(), c_apikeys.size(), nullptr);
2✔
575
            }
2✔
576
        };
4✔
577

578
        (*app)->provider_client<App::UserAPIKeyProviderClient>().fetch_api_keys(user, std::move(cb));
4✔
579
    });
4✔
580
}
4✔
581

582
RLM_API bool realm_app_user_apikey_provider_client_delete_apikey(const realm_app_t* app, const realm_user_t* user,
583
                                                                 realm_object_id_t id,
584
                                                                 realm_app_void_completion_func_t callback,
585
                                                                 realm_userdata_t userdata,
586
                                                                 realm_free_userdata_func_t userdata_free)
587
{
×
588
    return with_app_user(user, [&](auto& user) {
×
589
        (*app)->provider_client<App::UserAPIKeyProviderClient>().delete_api_key(
×
590
            from_capi(id), user, make_callback(callback, userdata, userdata_free));
×
591
    });
×
592
}
×
593

594
RLM_API bool realm_app_user_apikey_provider_client_enable_apikey(const realm_app_t* app, const realm_user_t* user,
595
                                                                 realm_object_id_t id,
596
                                                                 realm_app_void_completion_func_t callback,
597
                                                                 realm_userdata_t userdata,
598
                                                                 realm_free_userdata_func_t userdata_free)
599
{
×
600
    return with_app_user(user, [&](auto& user) {
×
601
        (*app)->provider_client<App::UserAPIKeyProviderClient>().enable_api_key(
×
602
            from_capi(id), user, make_callback(callback, userdata, userdata_free));
×
603
    });
×
604
}
×
605

606
RLM_API bool realm_app_user_apikey_provider_client_disable_apikey(const realm_app_t* app, const realm_user_t* user,
607
                                                                  realm_object_id_t id,
608
                                                                  realm_app_void_completion_func_t callback,
609
                                                                  realm_userdata_t userdata,
610
                                                                  realm_free_userdata_func_t userdata_free)
611
{
×
612
    return with_app_user(user, [&](auto& user) {
×
613
        (*app)->provider_client<App::UserAPIKeyProviderClient>().disable_api_key(
×
614
            from_capi(id), user, make_callback(callback, userdata, userdata_free));
×
615
    });
×
616
}
×
617

618
RLM_API bool realm_app_push_notification_client_register_device(
619
    const realm_app_t* app, const realm_user_t* user, const char* service_name, const char* registration_token,
620
    realm_app_void_completion_func_t callback, realm_userdata_t userdata, realm_free_userdata_func_t userdata_free)
621
{
×
622
    return with_app_user(user, [&](auto& user) {
×
623
        (*app)
×
624
            ->push_notification_client(service_name)
×
625
            .register_device(registration_token, user, make_callback(callback, userdata, userdata_free));
×
626
    });
×
627
}
×
628

629
RLM_API bool realm_app_push_notification_client_deregister_device(const realm_app_t* app, const realm_user_t* user,
630
                                                                  const char* service_name,
631
                                                                  realm_app_void_completion_func_t callback,
632
                                                                  realm_userdata_t userdata,
633
                                                                  realm_free_userdata_func_t userdata_free)
634
{
×
635
    return with_app_user(user, [&](auto& user) {
×
636
        (*app)
×
637
            ->push_notification_client(service_name)
×
638
            .deregister_device(user, make_callback(callback, userdata, userdata_free));
×
639
    });
×
640
}
×
641

642
RLM_API bool realm_app_call_function(const realm_app_t* app, const realm_user_t* user, const char* function_name,
643
                                     const char* serialized_ejson_payload, const char* service_name,
644
                                     realm_return_string_func_t callback, realm_userdata_t userdata,
645
                                     realm_free_userdata_func_t userdata_free)
646
{
×
647
    return with_app_user(user, [&](auto& user) {
×
648
        auto cb = [callback, userdata = SharedUserdata{userdata, FreeUserdata(userdata_free)}](
×
649
                      const std::string* reply, util::Optional<AppError> error) {
×
650
            if (error) {
×
651
                realm_app_error_t c_error(to_capi(*error));
×
652
                callback(userdata.get(), nullptr, &c_error);
×
653
            }
×
654
            else {
×
655
                callback(userdata.get(), reply->c_str(), nullptr);
×
656
            }
×
657
        };
×
658
        util::Optional<std::string> service_name_opt =
×
659
            service_name ? util::some<std::string>(service_name) : util::none;
×
660
        (*app)->call_function(user, function_name, serialized_ejson_payload, service_name_opt, std::move(cb));
×
661
    });
×
662
}
×
663

664
RLM_API void realm_app_sync_client_reconnect(realm_app_t* app) noexcept
665
{
×
666
    (*app)->sync_manager()->reconnect();
×
667
}
×
668

669
RLM_API bool realm_app_sync_client_has_sessions(const realm_app_t* app) noexcept
670
{
×
671
    return (*app)->sync_manager()->has_existing_sessions();
×
672
}
×
673

674
RLM_API void realm_app_sync_client_wait_for_sessions_to_terminate(realm_app_t* app) noexcept
675
{
×
676
    (*app)->sync_manager()->wait_for_sessions_to_terminate();
×
677
}
×
678

679
RLM_API char* realm_app_sync_client_get_default_file_path_for_realm(const realm_sync_config_t* config,
680
                                                                    const char* custom_filename)
681
{
×
682
    return wrap_err([&]() -> char* {
×
683
        auto user = std::dynamic_pointer_cast<app::User>(config->user);
×
684
        if (!user) {
×
685
            return nullptr;
×
686
        }
×
687
        util::Optional<std::string> filename =
×
688
            custom_filename ? util::some<std::string>(custom_filename) : util::none;
×
689
        std::string file_path = user->app()->path_for_realm(*config, std::move(filename));
×
690
        return duplicate_string(file_path);
×
691
    });
×
692
}
×
693

694
RLM_API bool realm_user_get_all_identities(const realm_user_t* user, realm_user_identity_t* out_identities,
695
                                           size_t max, size_t* out_n)
696
{
2✔
697
    return with_app_user(user, [&](auto& user) {
2✔
698
        const auto& identities = user->identities();
2✔
699
        set_out_param(out_n, identities.size());
2✔
700
        if (out_identities && max >= identities.size()) {
2✔
701
            for (size_t i = 0; i < identities.size(); i++) {
6✔
702
                out_identities[i] = {duplicate_string(identities[i].id),
4✔
703
                                     realm_auth_provider_e(enum_from_provider_type(identities[i].provider_type))};
4✔
704
            }
4✔
705
        }
2✔
706
    });
2✔
707
}
2✔
708

709
RLM_API char* realm_user_get_device_id(const realm_user_t* user) noexcept
710
{
×
711
    char* device_id = nullptr;
×
712
    with_app_user(user, [&](auto& user) {
×
713
        if (user->has_device_id()) {
×
714
            device_id = duplicate_string(user->device_id());
×
715
        }
×
716
    });
×
717
    return device_id;
×
718
}
×
719

720
RLM_API bool realm_user_log_out(realm_user_t* user)
721
{
×
722
    return with_app_user(user, [&](auto& user) {
×
723
        user->log_out();
×
724
    });
×
725
}
×
726

727
RLM_API char* realm_user_get_profile_data(const realm_user_t* user)
728
{
×
729
    return with_app_user(user, [&](auto& user) {
×
730
        std::string data = bson::Bson(user->user_profile().data()).to_string();
×
731
        return duplicate_string(data);
×
732
    });
×
733
}
×
734

735
RLM_API char* realm_user_get_custom_data(const realm_user_t* user) noexcept
736
{
×
737
    return with_app_user(user, [&](auto& user) -> char* {
×
738
        if (const auto& data = user->custom_data()) {
×
739
            std::string json = bson::Bson(*data).to_string();
×
740
            return duplicate_string(json);
×
741
        }
×
742
        return nullptr;
×
743
    });
×
744
}
×
745

746
RLM_API realm_app_t* realm_user_get_app(const realm_user_t* user) noexcept
747
{
×
748
    REALM_ASSERT(user);
×
749
    return with_app_user(user, [&](auto& user) {
×
750
        return new realm_app_t(user->app());
×
751
    });
×
752
}
×
753

754
RLM_API char* realm_user_get_identity(const realm_user_t* user) noexcept
NEW
755
{
×
NEW
756
    return duplicate_string((*user)->user_id());
×
NEW
757
}
×
758

759
RLM_API realm_user_state_e realm_user_get_state(const realm_user_t* user) noexcept
760
{
4✔
761
    return realm_user_state_e((*user)->state());
4✔
762
}
4✔
763

764
RLM_API bool realm_user_is_logged_in(const realm_user_t* user) noexcept
NEW
765
{
×
NEW
766
    return (*user)->is_logged_in();
×
NEW
767
}
×
768

769
RLM_API char* realm_user_get_access_token(const realm_user_t* user)
NEW
770
{
×
NEW
771
    return wrap_err([&] {
×
NEW
772
        return duplicate_string((*user)->access_token());
×
NEW
773
    });
×
NEW
774
}
×
775

776
RLM_API char* realm_user_get_refresh_token(const realm_user_t* user)
NEW
777
{
×
NEW
778
    return wrap_err([&] {
×
NEW
779
        return duplicate_string((*user)->refresh_token());
×
NEW
780
    });
×
NEW
781
}
×
782

783
RLM_API realm_app_user_subscription_token_t*
784
realm_sync_user_on_state_change_register_callback(realm_user_t* user, realm_sync_on_user_state_changed_t callback,
785
                                                  realm_userdata_t userdata, realm_free_userdata_func_t userdata_free)
786
{
2✔
787
    return with_app_user(user, [&](auto& user) {
2✔
788
        auto cb = [callback,
2✔
789
                   userdata = SharedUserdata{userdata, FreeUserdata(userdata_free)}](const SyncUser& sync_user) {
6✔
790
            callback(userdata.get(), realm_user_state_e(sync_user.state()));
6✔
791
        };
6✔
792
        auto token = user->subscribe(std::move(cb));
2✔
793
        return new realm_app_user_subscription_token_t{user, std::move(token)};
2✔
794
    });
2✔
795
}
2✔
796

797
RLM_API bool realm_sync_immediately_run_file_actions(realm_app_t* realm_app, const char* sync_path,
798
                                                     bool* did_run) noexcept
799
{
8✔
800
    return wrap_err([&]() {
8✔
801
        *did_run = (*realm_app)->immediately_run_file_actions(sync_path);
8✔
802
        return true;
8✔
803
    });
8✔
804
}
8✔
805

806
#endif // REALM_APP_SERVICES
807

808
#if !REALM_APP_SERVICES
809

810
static void cb_proxy_for_completion(realm_userdata_t userdata, const realm_app_error_t* err)
811
{
812
    SyncUser::CompletionHandler* cxx_cb = static_cast<SyncUser::CompletionHandler*>(userdata);
813
    REALM_ASSERT(cxx_cb);
814
    std::optional<AppError> cxx_err;
815
    if (err) {
816
        std::optional<int> additional_error_code;
817
        if (err->http_status_code) {
818
            additional_error_code = err->http_status_code;
819
        }
820
        cxx_err =
821
            AppError(ErrorCodes::Error(err->error), err->message, err->link_to_server_logs, additional_error_code);
822
    }
823
    (*cxx_cb)(cxx_err);
824
    delete cxx_cb;
825
}
826

827
struct CAPIAppUser : SyncUser {
828
    void* m_userdata = nullptr;
829
    realm_free_userdata_func_t m_free = nullptr;
830
    const std::string m_app_id;
831
    const std::string m_user_id;
832
    realm_user_get_access_token_cb_t m_access_token_cb = nullptr;
833
    realm_user_get_refresh_token_cb_t m_refresh_token_cb = nullptr;
834
    realm_user_state_cb_t m_state_cb = nullptr;
835
    realm_user_access_token_refresh_required_cb_t m_atrr_cb = nullptr;
836
    realm_user_get_sync_manager_cb_t m_sync_manager_cb = nullptr;
837
    realm_user_request_log_out_cb_t m_request_log_out_cb = nullptr;
838
    realm_user_request_refresh_location_cb_t m_request_refresh_location_cb = nullptr;
839
    realm_user_request_access_token_cb_t m_request_access_token_cb = nullptr;
840
    realm_user_track_realm_cb_t m_track_realm_cb = nullptr;
841
    realm_user_create_file_action_cb_t m_create_fa_cb = nullptr;
842

843
    CAPIAppUser(const char* app_id, const char* user_id)
844
        : m_app_id(app_id)
845
        , m_user_id(user_id)
846
    {
847
    }
848
    CAPIAppUser(CAPIAppUser&& other)
849
        : m_userdata(std::exchange(other.m_userdata, nullptr))
850
        , m_free(std::exchange(other.m_free, nullptr))
851
        , m_app_id(std::move(other.m_app_id))
852
        , m_user_id(std::move(other.m_user_id))
853
        , m_access_token_cb(std::move(other.m_access_token_cb))
854
        , m_refresh_token_cb(std::move(other.m_refresh_token_cb))
855
        , m_state_cb(std::move(other.m_state_cb))
856
        , m_atrr_cb(std::move(other.m_atrr_cb))
857
        , m_sync_manager_cb(std::move(other.m_sync_manager_cb))
858
        , m_request_log_out_cb(std::move(other.m_request_log_out_cb))
859
        , m_request_refresh_location_cb(std::move(other.m_request_refresh_location_cb))
860
        , m_request_access_token_cb(std::move(other.m_request_access_token_cb))
861
        , m_track_realm_cb(std::move(other.m_track_realm_cb))
862
        , m_create_fa_cb(std::move(other.m_create_fa_cb))
863
    {
864
    }
865

866
    ~CAPIAppUser()
867
    {
868
        if (m_free)
869
            m_free(m_userdata);
870
    }
871
    std::string user_id() const noexcept override
872
    {
873
        return m_user_id;
874
    }
875
    std::string app_id() const noexcept override
876
    {
877
        return m_app_id;
878
    }
879
    std::string access_token() const override
880
    {
881
        return m_access_token_cb(m_userdata);
882
    }
883
    std::string refresh_token() const override
884
    {
885
        return m_refresh_token_cb(m_userdata);
886
    }
887
    State state() const override
888
    {
889
        return State(m_state_cb(m_userdata));
890
    }
891
    bool access_token_refresh_required() const override
892
    {
893
        return m_atrr_cb(m_userdata);
894
    }
895
    SyncManager* sync_manager() override
896
    {
897
        auto value = m_sync_manager_cb(m_userdata);
898
        if (value && value->get()) {
899
            return (value->get());
900
        }
901
        return nullptr;
902
    }
903
    void request_log_out() override
904
    {
905
        m_request_log_out_cb(m_userdata);
906
    }
907
    void request_refresh_location(CompletionHandler&& callback) override
908
    {
909
        auto unscoped_cb = new CompletionHandler(std::move(callback));
910
        m_request_refresh_location_cb(m_userdata, cb_proxy_for_completion, unscoped_cb);
911
    }
912
    void request_access_token(CompletionHandler&& callback) override
913
    {
914
        auto unscoped_cb = new CompletionHandler(std::move(callback));
915
        m_request_access_token_cb(m_userdata, cb_proxy_for_completion, unscoped_cb);
916
    }
917
    void track_realm(std::string_view path) override
918
    {
919
        if (m_track_realm_cb) {
920
            m_track_realm_cb(m_userdata, path.data());
921
        }
922
    }
923
    std::string create_file_action(SyncFileAction a, std::string_view path,
924
                                   std::optional<std::string> recovery_dir) override
925
    {
926

927
        if (m_create_fa_cb) {
928
            return m_create_fa_cb(m_userdata, realm_sync_file_action_e(a), path.data(),
929
                                  recovery_dir ? recovery_dir->data() : nullptr);
930
        }
931
        return "";
932
    }
933
};
934

935
RLM_API realm_user_t* realm_user_new(realm_sync_user_create_config_t c) noexcept
936
{
937
    // optional to provide:
938
    // m_userdata
939
    // m_free
940
    // m_track_realm_cb
941
    // m_create_fa_cb
942

943
    REALM_ASSERT(c.app_id);
944
    REALM_ASSERT(c.user_id);
945
    REALM_ASSERT(c.access_token_cb);
946
    REALM_ASSERT(c.refresh_token_cb);
947
    REALM_ASSERT(c.state_cb);
948
    REALM_ASSERT(c.atrr_cb);
949
    REALM_ASSERT(c.sync_manager_cb);
950
    REALM_ASSERT(c.request_log_out_cb);
951
    REALM_ASSERT(c.request_refresh_location_cb);
952
    REALM_ASSERT(c.request_access_token_cb);
953

954
    return wrap_err([&]() {
955
        auto capi_user = std::make_shared<CAPIAppUser>(c.app_id, c.user_id);
956
        capi_user->m_userdata = c.userdata;
957
        capi_user->m_free = c.free_func;
958
        capi_user->m_access_token_cb = c.access_token_cb;
959
        capi_user->m_refresh_token_cb = c.refresh_token_cb;
960
        capi_user->m_state_cb = c.state_cb;
961
        capi_user->m_atrr_cb = c.atrr_cb;
962
        capi_user->m_sync_manager_cb = c.sync_manager_cb;
963
        capi_user->m_request_log_out_cb = c.request_log_out_cb;
964
        capi_user->m_request_refresh_location_cb = c.request_refresh_location_cb;
965
        capi_user->m_request_access_token_cb = c.request_access_token_cb;
966
        capi_user->m_track_realm_cb = c.track_realm_cb;
967
        capi_user->m_create_fa_cb = c.create_fa_cb;
968

969
        return new realm_user_t(std::move(capi_user));
970
    });
971
}
972

973
RLM_API realm_sync_manager_t* realm_sync_manager_create(const realm_sync_client_config_t* config)
974
{
975
    return wrap_err([&]() {
976
        auto manager = SyncManager::create(*config);
977
        return new realm_sync_manager_t(std::move(manager));
978
    });
979
}
980

981
RLM_API void realm_sync_manager_set_route(const realm_sync_manager_t* manager, const char* route, bool is_verified)
982
{
983
    REALM_ASSERT(manager);
984
    (*manager)->set_sync_route(route, is_verified);
985
}
986
#endif // #!REALM_APP_SERVICES
987

988
#if REALM_APP_SERVICES
989

990
namespace {
991
template <typename T>
992
util::Optional<T> convert_to_optional(T data)
993
{
×
994
    return data ? util::Optional<T>(data) : util::Optional<T>();
×
995
}
×
996

997
template <typename T>
998
util::Optional<T> convert_to_optional_bson(realm_string_t doc)
999
{
×
1000
    if (doc.data == nullptr || doc.size == 0) {
×
1001
        return util::Optional<T>();
×
1002
    }
×
1003
    return util::Optional<T>(static_cast<T>(bson::parse({doc.data, doc.size})));
×
1004
}
×
1005

1006
template <typename T>
1007
T convert_to_bson(realm_string_t doc)
1008
{
×
1009
    auto res = convert_to_optional_bson<T>(doc);
×
1010
    return res ? *res : T();
×
1011
}
×
1012

1013
MongoCollection::FindOptions to_mongodb_collection_find_options(const realm_mongodb_find_options_t* options)
1014
{
×
1015
    MongoCollection::FindOptions mongodb_options;
×
1016
    mongodb_options.projection_bson = convert_to_optional_bson<bson::BsonDocument>(options->projection_bson);
×
1017
    mongodb_options.sort_bson = convert_to_optional_bson<bson::BsonDocument>(options->sort_bson);
×
1018
    mongodb_options.limit = convert_to_optional(options->limit);
×
1019
    return mongodb_options;
×
1020
}
×
1021

1022
MongoCollection::FindOneAndModifyOptions
1023
to_mongodb_collection_find_one_and_modify_options(const realm_mongodb_find_one_and_modify_options_t* options)
1024
{
×
1025
    MongoCollection::FindOneAndModifyOptions mongodb_options;
×
1026
    mongodb_options.projection_bson = convert_to_optional_bson<bson::BsonDocument>(options->projection_bson);
×
1027
    mongodb_options.sort_bson = convert_to_optional_bson<bson::BsonDocument>(options->sort_bson);
×
1028
    mongodb_options.upsert = options->upsert;
×
1029
    mongodb_options.return_new_document = options->return_new_document;
×
1030
    return mongodb_options;
×
1031
}
×
1032

1033
void handle_mongodb_collection_result(util::Optional<bson::Bson> bson, util::Optional<AppError> app_error,
1034
                                      UserdataPtr data, realm_mongodb_callback_t callback)
1035
{
×
1036
    if (app_error) {
×
1037
        auto error = to_capi(*app_error);
×
1038
        callback(data.get(), {nullptr, 0}, &error);
×
1039
    }
×
1040
    else if (bson) {
×
1041
        const auto& bson_data = bson->to_string();
×
1042
        callback(data.get(), {bson_data.c_str(), bson_data.size()}, nullptr);
×
1043
    }
×
1044
}
×
1045
} // anonymous namespace
1046

1047
RLM_API realm_mongodb_collection_t* realm_mongo_collection_get(realm_user_t* user, const char* service,
1048
                                                               const char* database, const char* collection)
1049
{
×
1050
    REALM_ASSERT(user);
×
1051
    REALM_ASSERT(service);
×
1052
    REALM_ASSERT(database);
×
1053
    REALM_ASSERT(collection);
×
1054
    return with_app_user(user, [&](auto& user) {
×
1055
        auto col = user->mongo_client(service).db(database).collection(collection);
×
1056
        return new realm_mongodb_collection_t(col);
×
1057
    });
×
1058
}
×
1059

1060
RLM_API bool realm_mongo_collection_find(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
1061
                                         const realm_mongodb_find_options_t* options, realm_userdata_t data,
1062
                                         realm_free_userdata_func_t delete_data, realm_mongodb_callback_t callback)
1063
{
×
1064
    REALM_ASSERT(collection);
×
1065
    REALM_ASSERT(options);
×
1066
    return wrap_err([&] {
×
1067
        collection->find_bson(convert_to_bson<bson::BsonDocument>(filter_ejson),
×
1068
                              to_mongodb_collection_find_options(options),
×
1069
                              [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
1070
                                  handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
1071
                              });
×
1072
        return true;
×
1073
    });
×
1074
}
×
1075

1076
RLM_API bool realm_mongo_collection_find_one(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
1077
                                             const realm_mongodb_find_options_t* options, realm_userdata_t data,
1078
                                             realm_free_userdata_func_t delete_data,
1079
                                             realm_mongodb_callback_t callback)
1080
{
×
1081
    REALM_ASSERT(collection);
×
1082
    REALM_ASSERT(options);
×
1083
    return wrap_err([&] {
×
1084
        collection->find_one_bson(
×
1085
            convert_to_bson<bson::BsonDocument>(filter_ejson), to_mongodb_collection_find_options(options),
×
1086
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
1087
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
1088
            });
×
1089
        return true;
×
1090
    });
×
1091
}
×
1092

1093
RLM_API bool realm_mongo_collection_aggregate(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
1094
                                              realm_userdata_t data, realm_free_userdata_func_t delete_data,
1095
                                              realm_mongodb_callback_t callback)
1096
{
×
1097
    REALM_ASSERT(collection);
×
1098
    return wrap_err([&] {
×
1099
        collection->aggregate_bson(
×
1100
            convert_to_bson<bson::BsonArray>(filter_ejson),
×
1101
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
1102
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
1103
            });
×
1104
        return true;
×
1105
    });
×
1106
}
×
1107

1108
RLM_API bool realm_mongo_collection_count(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
1109
                                          int64_t limit, realm_userdata_t data,
1110
                                          realm_free_userdata_func_t delete_data, realm_mongodb_callback_t callback)
1111
{
×
1112
    REALM_ASSERT(collection);
×
1113
    return wrap_err([&] {
×
1114
        collection->count_bson(convert_to_bson<bson::BsonDocument>(filter_ejson), limit,
×
1115
                               [=](util::Optional<bson::Bson> bson, util::Optional<app::AppError> app_error) {
×
1116
                                   handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
1117
                               });
×
1118
        return true;
×
1119
    });
×
1120
}
×
1121

1122
RLM_API bool realm_mongo_collection_insert_one(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
1123
                                               realm_userdata_t data, realm_free_userdata_func_t delete_data,
1124
                                               realm_mongodb_callback_t callback)
1125
{
×
1126
    REALM_ASSERT(collection);
×
1127
    return wrap_err([&] {
×
1128
        collection->insert_one_bson(
×
1129
            convert_to_bson<bson::BsonDocument>(filter_ejson),
×
1130
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
1131
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
1132
            });
×
1133
        return true;
×
1134
    });
×
1135
}
×
1136

1137
RLM_API bool realm_mongo_collection_insert_many(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
1138
                                                realm_userdata_t data, realm_free_userdata_func_t delete_data,
1139
                                                realm_mongodb_callback_t callback)
1140
{
×
1141
    REALM_ASSERT(collection);
×
1142
    return wrap_err([&] {
×
1143
        collection->insert_many_bson(
×
1144
            convert_to_bson<bson::BsonArray>(filter_ejson),
×
1145
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
1146
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
1147
            });
×
1148
        return true;
×
1149
    });
×
1150
}
×
1151

1152
RLM_API bool realm_mongo_collection_delete_one(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
1153
                                               realm_userdata_t data, realm_free_userdata_func_t delete_data,
1154
                                               realm_mongodb_callback_t callback)
1155
{
×
1156
    REALM_ASSERT(collection);
×
1157
    return wrap_err([&] {
×
1158
        collection->delete_one_bson(
×
1159
            convert_to_bson<bson::BsonDocument>(filter_ejson),
×
1160
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
1161
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
1162
            });
×
1163
        return true;
×
1164
    });
×
1165
}
×
1166

1167
RLM_API bool realm_mongo_collection_delete_many(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
1168
                                                realm_userdata_t data, realm_free_userdata_func_t delete_data,
1169
                                                realm_mongodb_callback_t callback)
1170
{
×
1171
    REALM_ASSERT(collection);
×
1172
    return wrap_err([&] {
×
1173
        collection->delete_many_bson(
×
1174
            convert_to_bson<bson::BsonDocument>(filter_ejson),
×
1175
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
1176
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
1177
            });
×
1178
        return true;
×
1179
    });
×
1180
}
×
1181

1182
RLM_API bool realm_mongo_collection_update_one(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
1183
                                               realm_string_t update_ejson, bool upsert, realm_userdata_t data,
1184
                                               realm_free_userdata_func_t delete_data,
1185
                                               realm_mongodb_callback_t callback)
1186
{
×
1187
    REALM_ASSERT(collection);
×
1188
    return wrap_err([&] {
×
1189
        const auto& bson_filter = convert_to_bson<bson::BsonDocument>(filter_ejson);
×
1190
        const auto& bson_update = convert_to_bson<bson::BsonDocument>(update_ejson);
×
1191
        collection->update_one_bson(
×
1192
            bson_filter, bson_update, upsert,
×
1193
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
1194
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
1195
            });
×
1196
        return true;
×
1197
    });
×
1198
}
×
1199

1200
RLM_API bool realm_mongo_collection_update_many(realm_mongodb_collection_t* collection, realm_string_t filter_ejson,
1201
                                                realm_string_t update_ejson, bool upsert, realm_userdata_t data,
1202
                                                realm_free_userdata_func_t delete_data,
1203
                                                realm_mongodb_callback_t callback)
1204
{
×
1205
    REALM_ASSERT(collection);
×
1206
    return wrap_err([&] {
×
1207
        const auto& bson_filter = convert_to_bson<bson::BsonDocument>(filter_ejson);
×
1208
        const auto& bson_update = convert_to_bson<bson::BsonDocument>(update_ejson);
×
1209
        collection->update_many_bson(
×
1210
            bson_filter, bson_update, upsert,
×
1211
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
1212
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
1213
            });
×
1214
        return true;
×
1215
    });
×
1216
}
×
1217

1218
RLM_API bool realm_mongo_collection_find_one_and_update(realm_mongodb_collection_t* collection,
1219
                                                        realm_string_t filter_ejson, realm_string_t update_ejson,
1220
                                                        const realm_mongodb_find_one_and_modify_options_t* options,
1221
                                                        realm_userdata_t data, realm_free_userdata_func_t delete_data,
1222
                                                        realm_mongodb_callback_t callback)
1223
{
×
1224
    REALM_ASSERT(collection);
×
1225
    return wrap_err([&] {
×
1226
        const auto& bson_filter = convert_to_bson<bson::BsonDocument>(filter_ejson);
×
1227
        const auto& bson_update = convert_to_bson<bson::BsonDocument>(update_ejson);
×
1228
        collection->find_one_and_update_bson(
×
1229
            bson_filter, bson_update, to_mongodb_collection_find_one_and_modify_options(options),
×
1230
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
1231
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
1232
            });
×
1233
        return true;
×
1234
    });
×
1235
}
×
1236

1237
RLM_API bool realm_mongo_collection_find_one_and_replace(
1238
    realm_mongodb_collection_t* collection, realm_string_t filter_ejson, realm_string_t replacement_ejson,
1239
    const realm_mongodb_find_one_and_modify_options_t* options, realm_userdata_t data,
1240
    realm_free_userdata_func_t delete_data, realm_mongodb_callback_t callback)
1241
{
×
1242
    REALM_ASSERT(collection);
×
1243
    return wrap_err([&] {
×
1244
        const auto& filter_bson = convert_to_bson<bson::BsonDocument>(filter_ejson);
×
1245
        const auto& replacement_bson = convert_to_bson<bson::BsonDocument>(replacement_ejson);
×
1246
        collection->find_one_and_replace_bson(
×
1247
            filter_bson, replacement_bson, to_mongodb_collection_find_one_and_modify_options(options),
×
1248
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
1249
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
1250
            });
×
1251
        return true;
×
1252
    });
×
1253
}
×
1254

1255
RLM_API bool realm_mongo_collection_find_one_and_delete(realm_mongodb_collection_t* collection,
1256
                                                        realm_string_t filter_ejson,
1257
                                                        const realm_mongodb_find_one_and_modify_options_t* options,
1258
                                                        realm_userdata_t data, realm_free_userdata_func_t delete_data,
1259
                                                        realm_mongodb_callback_t callback)
1260
{
×
1261
    REALM_ASSERT(collection);
×
1262
    return wrap_err([&] {
×
1263
        const auto& bson_filter = convert_to_bson<bson::BsonDocument>(filter_ejson);
×
1264
        collection->find_one_and_delete_bson(
×
1265
            bson_filter, to_mongodb_collection_find_one_and_modify_options(options),
×
1266
            [=](util::Optional<bson::Bson> bson, util::Optional<AppError> app_error) {
×
1267
                handle_mongodb_collection_result(bson, app_error, {data, delete_data}, callback);
×
1268
            });
×
1269
        return true;
×
1270
    });
×
1271
}
×
1272

1273
#endif // REALM_APP_SERVICES
1274

1275
} // namespace realm::c_api
1276

1277
#if REALM_APP_SERVICES
1278
// definitions outside the c_api namespace
1279
realm_app_user_subscription_token::~realm_app_user_subscription_token()
1280
{
2✔
1281
    user->unsubscribe(token);
2✔
1282
}
2✔
1283
#endif
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

© 2026 Coveralls, Inc