Skip to content

Commit

Permalink
Followup to #6922 refactoring after_realm_open into `exclude_from_i…
Browse files Browse the repository at this point in the history
…cloud_backup` (#6927)

* Avoid including realm core headers in platform.hpp

* Add tests

* Update contrib guide and script to reference integration tests

* Add note in the changelog
  • Loading branch information
kraenhansen authored Nov 12, 2024
1 parent d5e901b commit 8c1142c
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 69 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* None

### Enhancements
* Added `excludeFromIcloudBackup` option to the `Realm` constructor to exclude the realm files from iCloud backup. ([#4139](https://github.com/realm/realm-js/issues/4139))
* Added `excludeFromIcloudBackup` option to the `Realm` constructor to exclude the realm files from iCloud backup. ([#4139](https://github.com/realm/realm-js/issues/4139) and [#6927](https://github.com/realm/realm-js/pull/6927))
```typescript
const realm = new Realm({
schema: [
Expand Down
39 changes: 25 additions & 14 deletions contrib/guide-testing-exclude-icloud-backup.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
# Guide: Testing Exclude iCloud Backup

## Prerequisites

- macOS
- iOS Simulator

## Testing

Ensure you have booted a simulator and execute the integration tests on iOS:

```sh
MOCHA_REMOTE_GREP='icloud' npm run test:ios --workspace @realm/react-native-test-app-tests
```

In the command above, we're explicitly grepping for the icloud backup tests.

To verify if a file has been successfully excluded from iCloud backup, you need to check the file's attributes.
We provide an easy script to do so:

```sh
./contrib/scripts/check-exclude-icloud-backup.sh
```

## Testing in your own app

Before starting the testing process, you need to configure your Realm database to either include or exclude files from iCloud backup. This is done by setting the `excludeFromIcloudBackup` property in your Realm configuration. Here is an example of how to set this property:

```javascript
Expand All @@ -16,20 +40,7 @@ const realm = new Realm(realmConfig);

Make sure to replace the schema and path with your actual Realm schema and desired file path. Once you have configured this property, you can proceed with the following steps to test if the exclusion from iCloud backup is working correctly.

## Prerequisites

- macOS
- iOS Simulator

## Testing

To verify if a file has been successfully excluded from iCloud backup, you need to check the file's attributes. We provide an easy script to do so. Ensure you have booted a simulator with an app using Realm. From the root of the project, run:

```sh
contrib/scripts/check-exclude-icloud-backup.sh <com.your.app.bundle>
```

If the script doesn't work, you can also check it manually. First, get the path of the Realm files from the simulator's Documents folder by running:
First, get the path of the Realm files from the simulator's Documents folder by running:

```sh
open `xcrun simctl get_app_container booted com.your.app.bundleId data`/Documents
Expand Down
16 changes: 5 additions & 11 deletions contrib/scripts/check-exclude-icloud-backup.sh
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
#!/bin/bash

# Check if appbundle parameter is provided
if [ -z "$1" ]; then
echo "Usage: $0 <com.app.bundle.id>"
exit 1
fi

appbundle=$1
TEST_APP_BUNDLE_ID=com.microsoft.ReactTestApp

# Check if a simulator is booted
booted_simulators=$(xcrun simctl list | grep "Booted")
Expand All @@ -25,15 +19,15 @@ if [ "$booted_count" -gt 1 ]; then
fi

# Extract the name of the booted simulator
booted_simulator=$(echo "$booted_simulator" | xargs)
booted_simulator=$(echo "$booted_simulators" | xargs)
echo -e "Running script on simulator: $booted_simulator\n"

# Get the app container path
app_container_path=$(xcrun simctl get_app_container booted "$appbundle" data 2>/dev/null)
app_container_path=$(xcrun simctl get_app_container booted "$TEST_APP_BUNDLE_ID" data 2>/dev/null)

# Check if the command was successful
if [ $? -ne 0 ] || [ -z "$app_container_path" ]; then
echo "Failed to get app container path for $appbundle"
echo "Failed to get app container path for $TEST_APP_BUNDLE_ID"
exit 1
fi

Expand All @@ -47,7 +41,7 @@ if [ ! -d "$documents_path" ]; then
fi

# Run xattr on all files in the directory
for file in "$documents_path"/*; do
for file in "$documents_path"/icloud-backup-tests/*.realm; do
if [ -e "$file" ]; then
filename=$(basename "$file")
attrs=$(xattr "$file" 2>/dev/null)
Expand Down
1 change: 1 addition & 0 deletions integration-tests/tests/src/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import "./tests/counter";
import "./tests/dictionary";
import "./tests/dynamic-schema-updates";
import "./tests/enums";
import "./tests/exclude-from-icloud-backup";
import "./tests/iterators";
import "./tests/linking-objects";
import "./tests/list";
Expand Down
53 changes: 53 additions & 0 deletions integration-tests/tests/src/tests/exclude-from-icloud-backup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2024 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

import Realm from "realm";

const BASE_PATH = "icloud-backup-tests";

describe("icloud backup", () => {
// This is in a separate suite to avoid
// See contrib/guide-testing-exclude-icloud-backup.md to verify the result of the test
it("excludes", () => {
const realm = new Realm({
path: `${BASE_PATH}/excluded.realm`,
schema: [],
excludeFromIcloudBackup: true,
});
realm.close();
});

it("removes exclusion", () => {
const path = `${BASE_PATH}/reincluded.realm`;
{
const realm = new Realm({
path,
schema: [],
excludeFromIcloudBackup: true,
});
realm.close();
}
{
const realm = new Realm({
path,
excludeFromIcloudBackup: false,
});
realm.close();
}
});
});
2 changes: 1 addition & 1 deletion packages/realm/bindgen/js_opt_in_spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,8 @@ classes:
- remove_realm_files_from_directory
- remove_file
- remove_directory
- exclude_from_icloud_backup
- get_cpu_arch
- after_realm_open

WeakSyncSession:
methods:
Expand Down
2 changes: 1 addition & 1 deletion packages/realm/bindgen/js_spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ classes:
remove_realm_files_from_directory: '(directory: const std::string&)'
remove_file: '(path: const std::string&)'
remove_directory: '(path: const std::string&)'
exclude_from_icloud_backup: '(path: const std::string&, value: bool)'
get_cpu_arch: () -> std::string
after_realm_open: '(realm: SharedRealm, exclude_from_icloud_backup: bool)'
# print: (const char* fmt, ...) # can't expose varargs directly. Could expose a fixed overload.

WeakSyncSession:
Expand Down
7 changes: 5 additions & 2 deletions packages/realm/binding/android/src/main/cpp/platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ void JsPlatformHelpers::remove_file(const std::string& path)
fs::remove(path);
}

void JsPlatformHelpers::exclude_from_icloud_backup(const std::string&, bool)
{
// no-op
}

void JsPlatformHelpers::print(const char* fmt, ...)
{
va_list vl;
Expand All @@ -131,8 +136,6 @@ void JsPlatformHelpers::print(const char* fmt, ...)
va_end(vl);
}

void JsPlatformHelpers::after_realm_open(SharedRealm, bool) {}

std::string JsPlatformHelpers::get_cpu_arch()
{
#if defined(__arm__)
Expand Down
47 changes: 17 additions & 30 deletions packages/realm/binding/apple/platform.mm
Original file line number Diff line number Diff line change
Expand Up @@ -35,32 +35,6 @@
return error.localizedDescription;
}

static void RLMCheckSkipBackupAttributeToItemAtPath(std::string_view path, bool exclude_from_icloud_backup) {
NSNumber *current;

[[NSURL fileURLWithPath:@(path.data())]
getResourceValue:&current
forKey:NSURLIsExcludedFromBackupKey
error:nil];

if (current.boolValue != exclude_from_icloud_backup) {
[[NSURL fileURLWithPath:@(path.data())]
setResourceValue:@(exclude_from_icloud_backup)
forKey:NSURLIsExcludedFromBackupKey
error:nil];

}
}

static void RLMCheckSkipBackupAttributeToRealmFilesAtPath(std::string path, bool exclude_from_icloud_backup) {
const std::vector<std::string> extensions = {"", ".lock", ".note",
".management"};

for (const auto& ext : extensions) {
RLMCheckSkipBackupAttributeToItemAtPath(path + ext, exclude_from_icloud_backup);
}
}

static std::string s_default_realm_directory;

namespace realm {
Expand Down Expand Up @@ -184,15 +158,28 @@ static void RLMCheckSkipBackupAttributeToRealmFilesAtPath(std::string path, bool
}
}

void JsPlatformHelpers::after_realm_open(const SharedRealm realm, bool exclude_from_icloud_backup) {
RLMCheckSkipBackupAttributeToRealmFilesAtPath(realm->config().path, exclude_from_icloud_backup);
}

void JsPlatformHelpers::remove_directory(const std::string &path)
{
remove_file(path); // works for directories too
}

void JsPlatformHelpers::exclude_from_icloud_backup(const std::string& path, bool value) {
NSNumber *current;

[[NSURL fileURLWithPath:@(path.data())]
getResourceValue:&current
forKey:NSURLIsExcludedFromBackupKey
error:nil];

if (current.boolValue != value) {
[[NSURL fileURLWithPath:@(path.data())]
setResourceValue:@(value)
forKey:NSURLIsExcludedFromBackupKey
error:nil];

}
}

void JsPlatformHelpers::print(const char *fmt, ...) {
va_list vl;
va_start(vl, fmt);
Expand Down
10 changes: 5 additions & 5 deletions packages/realm/binding/node/platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ void JsPlatformHelpers::remove_file(const std::string& path)
}
}

void JsPlatformHelpers::exclude_from_icloud_backup(const std::string&, bool)
{
// no-op
}

void JsPlatformHelpers::print(const char* fmt, ...)
{
va_list vl;
Expand All @@ -219,11 +224,6 @@ void JsPlatformHelpers::print(const char* fmt, ...)
va_end(vl);
}

void JsPlatformHelpers::after_realm_open(SharedRealm, bool)
{
// no-op
}

// this should never be called
std::string JsPlatformHelpers::get_cpu_arch()
{
Expand Down
5 changes: 2 additions & 3 deletions packages/realm/binding/platform.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
#pragma once

#include <string>
#include <realm/object-store/shared_realm.hpp>

namespace realm {
//
Expand Down Expand Up @@ -56,7 +55,7 @@ class JsPlatformHelpers {
// print something
static void print(const char* fmt, ...);

// runs after the realm has been opened
static void after_realm_open(const SharedRealm realm, const bool exclude_from_icloud_backup = false);
// excludes the path from icloud backup on iOS and no-op on other platforms
static void exclude_from_icloud_backup(const std::string& path, bool value);
};
} // namespace realm
10 changes: 9 additions & 1 deletion packages/realm/src/Realm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,15 @@ export class Realm {
this.schemaExtras = schemaExtras || {};
}

binding.JsPlatformHelpers.afterRealmOpen(this.internal, config.excludeFromIcloudBackup ?? false);
// Optionally: Exclude or include Realm files from iCloud backup
const { excludeFromIcloudBackup } = config;
if (typeof excludeFromIcloudBackup === "boolean") {
const realmPath = this.internal.config.path;
for (const fileNameSuffix of ["", ".lock", ".note", ".management"]) {
const filePath = realmPath + fileNameSuffix;
binding.JsPlatformHelpers.excludeFromIcloudBackup(filePath, excludeFromIcloudBackup);
}
}

Object.defineProperty(this, "classes", {
enumerable: false,
Expand Down

0 comments on commit 8c1142c

Please sign in to comment.