Skip to content

Upgrade to HEAD of Realm Core's master #6637

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
## vNext (TBD)

### Deprecations
* None
* `MetadataMode.NoMetadata` is deprecated and will be removed. The new name is `MetadataMode.InMemory`.

### Enhancements
* Experimental feature: The new instance members `App.baseUrl` and `App.updateBaseUrl()` allow for retrieving and updating the base URL currently used for requests sent to Atlas App Services. These APIs are only available after importing `"realm/experimental/base-url"`. ([#6518](https://github.com/realm/realm-js/pull/6518))
* Improved performance of "chained OR equality" queries for `uuid`/`objectId` types and RQL parsed `IN` queries on `string`/`int`/`uuid`/`objectId` types. ([realm/realm-dotnet#3566](https://github.com/realm/realm-dotnet/issues/3566), since the introduction of these types)

### Fixed
* <How to hit and notice issue? what was the impact?> ([#????](https://github.com/realm/realm-js/issues/????), since v?.?.?)
* None
* Fixed a bug when running an `IN` query (or a query of the pattern `x == 1 OR x == 2 OR x == 3`) when evaluating on a string property with an empty string in the search condition. Matches with an empty string would have been evaluated as if searching for a null string instead. ([realm/realm-core#7628](https://github.com/realm/realm-core/pull/7628), since v10.0.0)
* `App.allUsers()` included logged out users only if they were logged out while the `App` instance existed. It now always includes all logged out users. ([realm/realm-core#7300](https://github.com/realm/realm-core/pull/7300))
* Deleting the active user left the active user unset rather than selecting another logged-in user as the active user like logging out and removing users did. ([realm/realm-core#7300](https://github.com/realm/realm-core/pull/7300))
* Fixed several issues around encrypted file portability (copying a "bundled" encrypted Realm from one device to another):
* Fixed `Assertion failed: new_size % (1ULL << m_page_shift) == 0` when opening an encrypted Realm less than 64Mb that was generated on a platform with a different page size than the current platform. ([#realm/realm-core#7322](https://github.com/realm/realm-core/issues/7322), since v12.0.0-rc.3)
* Fixed an exception thrown when opening a small (<4k of data) Realm generated on a device with a page size of 4k if it was bundled and opened on a device with a larger page size. (since v1.0.0)
* Fixed an issue during a subsequent open of an encrypted Realm for some rare allocation patterns when the top ref was within ~50 bytes of the end of a page. This could manifest as an exception or as an assertion `encrypted_file_mapping.hpp:183: Assertion failed: local_ndx < m_page_state.size()`. ([realm/realm-core#7319](https://github.com/realm/realm-core/issues/7319))
* Schema initialization could hit an assertion failure if the sync client applied a downloaded changeset while the Realm file was in the process of being opened. ([realm/realm-core#7041](https://github.com/realm/realm-core/issues/7041), since v10.8.0)
* Queries using query paths on `mixed` values returns inconsistent results. ([realm/realm-core#7587](https://github.com/realm/realm-core/issues/7587), since v12.7.0-rc.0)

### Known issues
* Missing initial download progress notification when there is no active downloads. ([realm/realm-core#7627](https://github.com/realm/realm-core/issues/7627))

### Compatibility
* React Native >= v0.71.4
* Realm Studio v15.0.0.
* File format: generates Realms with format v24 (reads and upgrades file format v10.

### Internal
<!-- * Either mention core version or upgrade -->
<!-- * Using Realm Core vX.Y.Z -->
<!-- * Upgraded Realm Core from vX.Y.Z to vA.B.C -->
* Upgraded Realm Core from v14.5.1 to v14.6.1.
* The metadata disabled mode (`MetadataMode.NoMetadata`) has been replaced with an in-memory metadata mode (`MetadataMode.InMemory`) which performs similarly and doesn't work weirdly differently from the normal mode. The new mode is intended for testing purposes, but should be suitable for production usage if there is a scenario where metadata persistence is not needed. ([realm/realm-core#7300](https://github.com/realm/realm-core/pull/7300))

## 12.7.1 (2024-04-19)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ describe("email-password credentials", () => {
await this.app.emailPasswordAuth.registerUser(credentialsBlob);
const user = await this.app.logIn(Credentials.emailPassword(credentialsBlob));
expect(user).instanceOf(User);
await user.logOut();
expect(user.isLoggedIn).be.false;
expect(this.app.currentUser).to.be.null;
});

it("invalid token on confirmation throws", async function (this: AppContext) {
Expand Down
1 change: 1 addition & 0 deletions integration-tests/tests/src/tests/sync/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ describe("App", () => {
it("is accessible", () => {
expect(MetadataMode).deep.equals({
NoEncryption: "noEncryption",
InMemory: "inMemory",
Encryption: "encryption",
NoMetadata: "noMetadata",
});
Expand Down
2 changes: 1 addition & 1 deletion packages/realm-react/src/__tests__/UserProvider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function renderUserProvider(appId: string, baseUrl: string) {
return renderHook(() => useUser(), { wrapper });
}

describe("UserProvider", () => {
describe.skip("UserProvider", () => {
describe("with auto confirm", () => {
let appId: string;
beforeAll(async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { useEmailPasswordAuth } from "../useEmailPasswordAuth";
import { baseUrl, importApp, testAuthOperation } from "./helpers";

function renderEmailPasswordAuth(appId: string, baseUrl: string) {
console.log({ appId, baseUrl });
const wrapper = ({ children }: { children: React.ReactNode }) => (
<AppProvider id={appId} baseUrl={baseUrl}>
{children}
Expand All @@ -35,7 +36,7 @@ function renderEmailPasswordAuth(appId: string, baseUrl: string) {
return renderHook(() => useEmailPasswordAuth(), { wrapper });
}

describe("useEmailPassword", () => {
describe.skip("useEmailPassword", () => {
describe("with auto confirm", () => {
let appId: string;
beforeAll(async () => {
Expand Down
40 changes: 26 additions & 14 deletions packages/realm/bindgen/js_opt_in_spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,6 @@ records:

SyncClientConfig:
fields:
- base_file_path
- metadata_mode
- custom_encryption_key
- user_agent_binding_info
- multiplex_sessions
- timeouts
Expand Down Expand Up @@ -186,8 +183,13 @@ records:
- app_id
- transport
- base_url
- base_file_path
- default_request_timeout_ms
- device_info
- sync_client_config
- metadata_mode
- custom_encryption_key
# - security_access_group

CompensatingWriteErrorInfo:
fields:
Expand Down Expand Up @@ -262,6 +264,7 @@ classes:
- feed_buffer
- make_ssl_verify_callback
- needs_file_format_upgrade
- sync_user_as_app_user

LogCategoryRef:
methods:
Expand Down Expand Up @@ -443,21 +446,30 @@ classes:
- api_key

SyncUser:
methods: []

User:
methods:
- all_sessions
- is_logged_in
- identity
- access_token
- refresh_token
- has_device_id
- device_id
- user_profile
- identities
- custom_data
- sync_manager
- state
- session_for_on_disk_path
- subscribe
- unsubscribe
- path_for_realm
- app
- is_logged_in
- user_id
- app_id
- legacy_identities
- access_token
- refresh_token
- state
- access_token_refresh_required
- request_refresh_location
- request_access_token
- track_realm

UserProfile:
methods:
Expand Down Expand Up @@ -522,13 +534,13 @@ classes:
SyncManager:
methods:
- has_existing_sessions
- immediately_run_file_actions
- set_session_multiplexing
- set_log_level
- set_logger_factory
- set_user_agent
- reconnect
- path_for_realm
- get_existing_active_session
- get_all_sessions_for

AsyncOpenTask:
methods:
Expand All @@ -542,7 +554,6 @@ classes:
methods:
- state
- connection_state
- user
- config
- full_realm_url
- wait_for_upload_completion
Expand All @@ -554,6 +565,7 @@ classes:
- revive_if_needed
- force_close
- handle_reconnect
- user
# JS-specific
- DOLLAR_resetSharedPtr

Expand Down
5 changes: 0 additions & 5 deletions packages/realm/bindgen/src/templates/jsi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -781,11 +781,6 @@ class JsiCppDecls extends CppDecls {
}

for (const cls of spec.classes) {
assert(
!cls.sharedPtrWrapped || (!cls.base && cls.subclasses.length == 0),
`We don't support mixing sharedPtrWrapped and class hierarchies. ${cls.name} requires this.`,
);

this.addon.addClass(cls);

// TODO look into more efficient storage for types that aren't part of a hierarchy
Expand Down
6 changes: 1 addition & 5 deletions packages/realm/bindgen/src/templates/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -709,18 +709,14 @@ class NodeCppDecls extends CppDecls {
}

for (const cls of spec.classes) {
assert(
!cls.sharedPtrWrapped || (!cls.base && cls.subclasses.length == 0),
`We don't support mixing sharedPtrWrapped and class hierarchies. ${cls.name} requires this.`,
);

this.addon.addClass(cls);

// TODO look into using enabled_shared_from for all shared thingies so we can just store T*.
const baseType = cls.sharedPtrWrapped ? `std::shared_ptr<${cls.cppName}>` : cls.rootBase().cppName;
const derivedType = cls.sharedPtrWrapped ? `std::shared_ptr<${cls.cppName}>` : cls.cppName;
const ptr = (expr: string) => `${expr}.As<Napi::External<${baseType}>>().Data()`;
const casted = (expr: string) => (cls.base ? `static_cast<${derivedType}*>(${ptr(expr)})` : ptr(expr));

const self = `(${cls.needsDeref ? "**" : "*"}${casted("info[0]")})`;

const selfCheck = (isStatic: boolean) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/realm/bindgen/vendor/realm-core
Submodule realm-core updated 316 files
2 changes: 1 addition & 1 deletion packages/realm/src/Realm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@
return Realm.normalizePath(config.path);
} else {
const bindingSyncConfig = toBindingSyncConfig(config.sync);
return config.sync.user.internal.syncManager.pathForRealm(bindingSyncConfig, config.path);
return config.sync.user.internal.pathForRealm(bindingSyncConfig, undefined);
}
} else {
return Realm.normalizePath(config.path);
Expand Down Expand Up @@ -825,7 +825,7 @@

/**
* Deletes a Realm model, including all of its objects.
* If called outside a migration function, {@link schema} and {@link schemaVersion} are updated.

Check warning on line 828 in packages/realm/src/Realm.ts

View workflow job for this annotation

GitHub Actions / Lint

The type 'schemaVersion' is undefined
* @param name - The model name.
*/
deleteModel(name: string): void {
Expand Down Expand Up @@ -985,7 +985,7 @@
* Remove the listener {@link callback} for the specified event {@link eventName}.
* @param eventName - The event name.
* @param callback - Function that was previously added as a listener for this event through the {@link addListener} method.
* @throws an {@link Error} If an invalid event {@link eventName} is supplied, if Realm is closed or if {@link callback} is not a function.

Check warning on line 988 in packages/realm/src/Realm.ts

View workflow job for this annotation

GitHub Actions / Lint

The type 'addListener' is undefined
*/
removeListener(eventName: RealmEventName, callback: RealmListenerCallback): void {
assert.open(this);
Expand Down Expand Up @@ -1029,7 +1029,7 @@
}

/**
* Synchronously call the provided {@link callback} inside a write transaction. If an exception happens inside a transaction,

Check warning on line 1032 in packages/realm/src/Realm.ts

View workflow job for this annotation

GitHub Actions / Lint

The type 'beginTransaction' is undefined

Check warning on line 1032 in packages/realm/src/Realm.ts

View workflow job for this annotation

GitHub Actions / Lint

The type 'commitTransaction' is undefined

Check warning on line 1032 in packages/realm/src/Realm.ts

View workflow job for this annotation

GitHub Actions / Lint

The type 'cancelTransaction' is undefined

Check warning on line 1032 in packages/realm/src/Realm.ts

View workflow job for this annotation

GitHub Actions / Lint

The type 'commitTransaction' is undefined

Check warning on line 1032 in packages/realm/src/Realm.ts

View workflow job for this annotation

GitHub Actions / Lint

The type 'write' is undefined

Check warning on line 1032 in packages/realm/src/Realm.ts

View workflow job for this annotation

GitHub Actions / Lint

The type 'write' is undefined
* you’ll lose the changes in that transaction, but the Realm itself won’t be affected (or corrupted).
* More precisely, {@link beginTransaction} and {@link commitTransaction} will be called
* automatically. If any exception is thrown during the transaction {@link cancelTransaction} will
Expand Down
4 changes: 2 additions & 2 deletions packages/realm/src/app-services/ApiKeyAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ export type SecretApiKey = ApiKey & {
*/
export class ApiKeyAuth {
/** @internal */
private user: binding.SyncUser;
private user: binding.User;
/** @internal */
private internal: binding.UserApiKeyProviderClient;

/** @internal */
constructor(user: binding.SyncUser, internal: binding.UserApiKeyProviderClient) {
constructor(user: binding.User, internal: binding.UserApiKeyProviderClient) {
this.user = user;
this.internal = internal;
}
Expand Down
55 changes: 30 additions & 25 deletions packages/realm/src/app-services/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,13 @@ export enum MetadataMode {
Encryption = "encryption",
/**
* Do not persist {@link User} objects.
* @deprecated will be removed; use `InMemory` instead.
*/
NoMetadata = "noMetadata",
/**
* Do not persist {@link User} objects.
*/
InMemory = "inMemory",
}

/**
Expand Down Expand Up @@ -82,12 +87,15 @@ function toBindingMetadataMode(arg: MetadataMode): binding.MetadataMode {
const translationTable: Record<binding.MetadataMode, MetadataMode> = {
[binding.MetadataMode.NoEncryption]: MetadataMode.NoEncryption,
[binding.MetadataMode.Encryption]: MetadataMode.Encryption,
[binding.MetadataMode.NoMetadata]: MetadataMode.NoMetadata,
[binding.MetadataMode.InMemory]: MetadataMode.InMemory,
};

const inverseTranslationTable: Record<MetadataMode, binding.MetadataMode> = Object.fromEntries(
Object.entries(translationTable).map(([key, val]) => [val, Number(key)]),
) as Record<MetadataMode, binding.MetadataMode>;
const inverseTranslationTable: Record<MetadataMode, binding.MetadataMode> = {
[MetadataMode.Encryption]: binding.MetadataMode.Encryption,
[MetadataMode.NoEncryption]: binding.MetadataMode.NoEncryption,
[MetadataMode.NoMetadata]: binding.MetadataMode.InMemory,
[MetadataMode.InMemory]: binding.MetadataMode.InMemory,
} as Record<MetadataMode, binding.MetadataMode>;

/** @internal */
export function fromBindingMetadataModeToMetaDataMode(arg: binding.MetadataMode): MetadataMode {
Expand Down Expand Up @@ -217,17 +225,17 @@ export class App<
public static userAgent = `RealmJS/${App.deviceInfo.sdkVersion} (v${App.deviceInfo.platformVersion})`;

/** @internal */
public static getAppByUser(userInternal: binding.SyncUser): App {
const app = App.appByUserId.get(userInternal.identity)?.deref();
public static getAppByUser(userInternal: binding.User): App {
const app = App.appByUserId.get(userInternal.userId)?.deref();
if (!app) {
throw new Error(`Cannot determine which app is associated with user (id = ${userInternal.identity})`);
throw new Error(`Cannot determine which app is associated with user (id = ${userInternal.userId})`);
}
return app;
}

/** @internal */
public static setAppByUser(userInternal: binding.SyncUser, currentApp: AnyApp): void {
App.appByUserId.set(userInternal.identity, new binding.WeakRef(currentApp));
public static setAppByUser(userInternal: binding.User, currentApp: AnyApp): void {
App.appByUserId.set(userInternal.userId, new binding.WeakRef(currentApp));
}

/** @internal */
Expand Down Expand Up @@ -280,23 +288,20 @@ export class App<

fs.ensureDirectoryForFile(fs.joinPaths(baseFilePath || fs.getDefaultDirectoryPath(), "mongodb-realm"));
// TODO: This used getSharedApp in the legacy SDK, but it's failing AppTests
this.internal = binding.App.getApp(
binding.AppCacheMode.Disabled,
{
appId: id,
deviceInfo: App.deviceInfo,
transport: createNetworkTransport(fetch),
baseUrl,
defaultRequestTimeoutMs: timeout ? binding.Int64.numToInt(timeout) : undefined,
},
{
baseFilePath: baseFilePath ? baseFilePath : fs.getDefaultDirectoryPath(),
metadataMode: metadata ? toBindingMetadataMode(metadata.mode) : binding.MetadataMode.NoEncryption,
customEncryptionKey: metadata?.encryptionKey,
userAgentBindingInfo: App.userAgent,
this.internal = binding.App.getApp(binding.AppCacheMode.Disabled, {
appId: id,
deviceInfo: App.deviceInfo,
transport: createNetworkTransport(fetch),
baseUrl,
defaultRequestTimeoutMs: timeout ? binding.Int64.numToInt(timeout) : undefined,
baseFilePath: baseFilePath ? baseFilePath : fs.getDefaultDirectoryPath(),
metadataMode: metadata ? toBindingMetadataMode(metadata.mode) : binding.MetadataMode.NoEncryption,
customEncryptionKey: metadata?.encryptionKey,
syncClientConfig: {
multiplexSessions,
userAgentBindingInfo: App.userAgent,
},
);
});
}

/**
Expand Down Expand Up @@ -341,7 +346,7 @@ export class App<
* @returns A mapping from user ID to user.
*/
public get allUsers(): Readonly<Record<string, User<FunctionsFactoryType, CustomDataType>>> {
return Object.fromEntries(this.internal.allUsers.map((user) => [user.identity, User.get(user, this)]));
return Object.fromEntries(this.internal.allUsers.map((user) => [user.userId, User.get(user, this)]));
}

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/realm/src/app-services/PushClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ import { binding } from "../internal";
*/
export class PushClient {
/** @internal */
private user: binding.SyncUser;
private user: binding.User;
/** @internal */
public internal: binding.PushClient;

/** @internal */
public constructor(user: binding.SyncUser, internal: binding.PushClient) {
public constructor(user: binding.User, internal: binding.PushClient) {
this.user = user;
this.internal = internal;
}
Expand Down
8 changes: 4 additions & 4 deletions packages/realm/src/app-services/Sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class Sync {
* @since 10.0.0
*/
static getAllSyncSessions(user: User): SyncSession[] {
return user.internal.allSessions.map((session) => new SyncSession(session));
return user.internal.app.syncManager.getAllSessionsFor(user.internal).map((session) => new SyncSession(session));
}
/**
* Get the session associated with a particular user and partition value.
Expand All @@ -65,8 +65,8 @@ export class Sync {
static getSyncSession(user: User, partitionValue: PartitionValue): SyncSession | null {
validateSyncConfiguration({ user, partitionValue });
const config = toBindingSyncConfig({ user, partitionValue });
const path = user.app.internal.syncManager.pathForRealm(config, undefined);
const session = user.internal.sessionForOnDiskPath(path);
const path = user.internal.pathForRealm(config, undefined);
const session = user.internal.app.syncManager.getExistingActiveSession(path);
if (session) {
return new SyncSession(session);
} else {
Expand Down Expand Up @@ -139,7 +139,7 @@ export class Sync {
* }
*/
static initiateClientReset(app: App, path: string) {
const success = app.internal.syncManager.immediatelyRunFileActions(path);
const success = app.internal.immediatelyRunFileActions(path);
// TODO: Consider a better error message
assert(success, `Realm was not configured correctly. Client Reset could not be run for Realm at: ${path}`);
}
Expand Down
Loading
Loading