Skip to content

Commit 430afad

Browse files
authoredMar 23, 2023
Expose baseFilePath on App configuration (#5572)
* Expose baseFilePath on App configuration * PR feedback

File tree

7 files changed

+72
-16
lines changed

7 files changed

+72
-16
lines changed
 

‎CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* None
55

66
### Enhancements
7-
* None
7+
* Added configuration option `App.baseFilePath` which controls where synced Realms and metadata is stored.
88

99
### Fixed
1010
* Fix type error when using `realm.create` in combination with class base models. (since v11.0.0)

‎docs/realm.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,8 @@ class Realm {
395395
* Realm database should be stored. For synced Realms, a relative path is used together with app id and
396396
* user id in order to avoid collisions with other apps or users. An absolute path is left untouched
397397
* and on some platforms (iOS and Android) the app might not have permissions to create or open
398-
* the file - permissions are not validated.
398+
* the file - permissions are not validated. If a relative path is specified, it is relative to
399+
* {@link Realm.App~AppConfiguration.baseFilePath}.
399400
* @property {string} [fifoFilesFallbackPath] - Opening a Realm creates a number of FIFO special files in order to
400401
* coordinate access to the Realm across threads and processes. If the Realm file is stored in a location
401402
* that does not allow the creation of FIFO special files (e.g. FAT32 filesystems), then the Realm cannot be opened.

‎docs/sync.js

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
* @property {string} id - The id of the Atlas App Services application.
2525
* @property {string} [baseUrl] - The base URL of the Atlas App Services server.
2626
* @property {number} [timeout] - General timeout (in millisecs) for requests.
27+
* @property {string} [baseFilePath] - Specify where synced Realms and metadata is stored. If not specified, the current work directory is used.
2728
* @property {Realm.App~LocalAppConfiguration} [app] - local app configuration
2829
*/
2930

‎integration-tests/tests/src/node/path.ts

+37-7
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@ import { expect } from "chai";
2020
import Realm, { BSON } from "realm";
2121
import path from "node:path";
2222
import os from "node:os";
23+
import { existsSync, rmSync } from "node:fs";
2324

2425
import { importAppBefore, authenticateUserBefore } from "../hooks";
26+
import { importApp } from "../utils/import-app";
2527

2628
const getAbsolutePath = () => os.tmpdir() + path.sep + new BSON.UUID().toHexString();
2729
const getRelativePath = () => "testFiles" + path.sep + new BSON.UUID().toHexString();
2830
const getPartitionValue = () => new BSON.UUID().toHexString();
2931

30-
const schema = {
32+
const Schema = {
3133
name: "MixedClass",
3234
primaryKey: "_id",
3335
properties: {
@@ -36,24 +38,52 @@ const schema = {
3638
},
3739
};
3840

41+
const FlexibleSchema = { ...Schema, properties: { ...Schema.properties, nonQueryable: "string?" } };
42+
3943
describe("path configuration (local)", function () {
4044
it("relative path", function () {
4145
const filename = getRelativePath();
42-
const realm = new Realm({ path: filename, schema: [schema] });
46+
const realm = new Realm({ path: filename, schema: [Schema] });
4347
expect(realm.path.endsWith(filename)).to.be.true;
4448
realm.close();
4549
Realm.deleteFile({ path: filename });
4650
});
4751

4852
it("absolute path", function () {
4953
const filename = getAbsolutePath();
50-
const realm = new Realm({ path: filename, schema: [schema] });
54+
const realm = new Realm({ path: filename, schema: [Schema] });
5155
expect(realm.path).to.equal(filename);
5256
realm.close();
5357
Realm.deleteFile({ path: filename });
5458
});
5559
});
5660

61+
describe.skipIf(environment.missingServer, `app configuration of root directory (flexible sync)`, async function () {
62+
const { appId, baseUrl } = await importApp("with-db-flx");
63+
64+
it("directory and file created where expected", async function () {
65+
const tmpdir = getAbsolutePath();
66+
expect(fs.exists(tmpdir)).to.be.false;
67+
68+
const app = new Realm.App({ id: appId, baseUrl, baseFilePath: tmpdir });
69+
const user = await app.logIn(Realm.Credentials.anonymous());
70+
71+
const realm = await Realm.open({
72+
schema: [FlexibleSchema],
73+
sync: {
74+
flexible: true,
75+
user,
76+
},
77+
});
78+
79+
expect(existsSync(tmpdir)).to.be.true;
80+
expect(realm.path.startsWith(tmpdir));
81+
82+
realm.close();
83+
rmSync(tmpdir, { recursive: true });
84+
});
85+
});
86+
5787
describe.skipIf(environment.missingServer, "path configuration (partition based sync)", function () {
5888
importAppBefore("with-db");
5989
authenticateUserBefore();
@@ -62,7 +92,7 @@ describe.skipIf(environment.missingServer, "path configuration (partition based
6292
const filename = getAbsolutePath();
6393
const realm = await Realm.open({
6494
path: filename,
65-
schema: [schema],
95+
schema: [Schema],
6696
sync: {
6797
partitionValue: getPartitionValue(),
6898
user: this.user,
@@ -77,7 +107,7 @@ describe.skipIf(environment.missingServer, "path configuration (partition based
77107
const filename = getRelativePath();
78108
const realm = await Realm.open({
79109
path: filename,
80-
schema: [schema],
110+
schema: [Schema],
81111
sync: {
82112
partitionValue: getPartitionValue(),
83113
user: this.user,
@@ -98,7 +128,7 @@ describe.skipIf(environment.skipFlexibleSync, "path configuration (flexible sync
98128
const filename = getAbsolutePath();
99129
const realm = await Realm.open({
100130
path: filename,
101-
schema: [schema],
131+
schema: [FlexibleSchema],
102132
sync: {
103133
flexible: true,
104134
user: this.user,
@@ -114,7 +144,7 @@ describe.skipIf(environment.skipFlexibleSync, "path configuration (flexible sync
114144
const filename = getRelativePath();
115145
const realm = await Realm.open({
116146
path: filename,
117-
schema: [schema],
147+
schema: [FlexibleSchema],
118148
sync: {
119149
flexible: true,
120150
user: this.user,

‎src/js_app.hpp

+14-7
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "js_network_transport.hpp"
3535
#include "js_email_password_auth.hpp"
3636
#include "realm/object-store/sync/subscribable.hpp"
37+
#include "realm/util/file.hpp"
3738

3839

3940
using SharedApp = std::shared_ptr<realm::app::App>;
@@ -170,6 +171,7 @@ void AppClass<T>::constructor(ContextType ctx, ObjectType this_object, Arguments
170171
static const String config_app = "app";
171172
static const String config_app_name = "name";
172173
static const String config_app_version = "version";
174+
static const String config_base_file_path = "baseFilePath";
173175

174176
args.validate_count(1);
175177

@@ -178,6 +180,11 @@ void AppClass<T>::constructor(ContextType ctx, ObjectType this_object, Arguments
178180
std::string id;
179181
realm::app::App::Config config;
180182

183+
SyncClientConfig client_config;
184+
client_config.metadata_mode = SyncManager::MetadataMode::NoEncryption;
185+
client_config.user_agent_binding_info = get_user_agent();
186+
client_config.base_file_path = default_realm_file_directory(); // this may be changed
187+
181188
if (Value::is_object(ctx, args[0])) {
182189
ObjectType config_object = Value::validated_to_object(ctx, args[0]);
183190

@@ -217,6 +224,11 @@ void AppClass<T>::constructor(ContextType ctx, ObjectType this_object, Arguments
217224
std::optional<std::string>(Value::validated_to_string(ctx, config_app_version_value, "version"));
218225
}
219226
}
227+
228+
ValueType base_file_path_value = Object::get_property(ctx, config_object, config_base_file_path);
229+
if (!Value::is_undefined(ctx, base_file_path_value)) {
230+
client_config.base_file_path = Value::validated_to_string(ctx, base_file_path_value);
231+
}
220232
}
221233
else if (Value::is_string(ctx, args[0])) {
222234
config.app_id = Value::validated_to_string(ctx, args[0]);
@@ -238,13 +250,8 @@ void AppClass<T>::constructor(ContextType ctx, ObjectType this_object, Arguments
238250
config.device_info.framework_name = framework_name;
239251
config.device_info.framework_version = framework_version;
240252

241-
auto realm_file_directory = default_realm_file_directory();
242-
ensure_directory_exists_for_file(realm_file_directory);
243-
244-
SyncClientConfig client_config;
245-
client_config.base_file_path = realm_file_directory;
246-
client_config.metadata_mode = SyncManager::MetadataMode::NoEncryption;
247-
client_config.user_agent_binding_info = get_user_agent();
253+
util::try_make_dir(client_config.base_file_path);
254+
set_default_realm_file_directory(client_config.base_file_path);
248255

249256
SharedApp app = app::App::get_shared_app(config, client_config);
250257

‎src/node/platform.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
#include "../platform.hpp"
2626

27+
static std::string s_default_realm_directory;
28+
2729
namespace realm {
2830

2931
class UVException : public std::runtime_error {
@@ -44,9 +46,19 @@ struct FileSystemRequest : uv_fs_t {
4446
}
4547
};
4648

49+
void set_default_realm_file_directory(std::string dir)
50+
{
51+
s_default_realm_directory = dir;
52+
}
53+
4754
// taken from Node.js: function Cwd in node.cc
4855
std::string default_realm_file_directory()
4956
{
57+
58+
if (!s_default_realm_directory.empty()) {
59+
return s_default_realm_directory;
60+
}
61+
5062
#ifdef _WIN32
5163
/* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */
5264
char buf[MAX_PATH * 4];

‎types/app.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,11 @@ declare namespace Realm {
355355
*/
356356
baseUrl?: string;
357357

358+
/**
359+
* An optional path to a directory where synced Realms are stored.
360+
*/
361+
baseFilePath?: string;
362+
358363
/**
359364
* This describes the local app, sent to the server when a user authenticates.
360365
* Specifying this will enable the server to respond differently to specific versions of specific apps.

0 commit comments

Comments
 (0)
Please sign in to comment.