Skip to content

Commit

Permalink
Update to latest Core (#3467)
Browse files Browse the repository at this point in the history
  • Loading branch information
nirinchev authored Nov 3, 2023
1 parent 10aaf6a commit b969719
Show file tree
Hide file tree
Showing 12 changed files with 85 additions and 86 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@

### Fixed
* Added an error that is raised when interface based Realm classes are used with a language version lower than 8.0. At the same time, removed the use of `not` in the generated code, so that it's compatible with a minumum C# version of 8.0. (Issue [#3265](https://github.com/realm/realm-dotnet/issues/3265))
* Logging into a single user using multiple auth providers created a separate SyncUser per auth provider. This mostly worked, but had some quirks:
- Sync sessions would not necessarily be associated with the specific SyncUser used to create them. As a result, querying a user for its sessions could give incorrect results, and logging one user out could close the wrong sessions.
- Existing local synchronized Realm files created using version of Realm from August - November 2020 would sometimes not be opened correctly and would instead be redownloaded.
- Removing one of the SyncUsers would delete all local Realm files for all SyncUsers for that user.
- Deleting the server-side user via one of the SyncUsers left the other SyncUsers in an invalid state.
- A SyncUser which was originally created via anonymous login and then linked to an identity would still be treated as an anonymous users and removed entirely on logout.
(Core 13.21.0)
* If a user was logged out while an access token refresh was in progress, the refresh completing would mark the user as logged in again and the user would be in an inconsistent state (Core 13.21.0).
* If querying over a geospatial dataset that had some objects with a type property set to something other than 'Point' (case insensitive) an exception would have been thrown. Instead of disrupting the query, those objects are now just ignored. (Core 13.21.0)
* Receiving a write_not_allowed error from the server would have led to a crash. (Core 13.22.0)
* Updating subscriptions did not trigger Realm autorefreshes, sometimes resulting in async refresh hanging until another write was performed by something else. (Core 13.23.1)
* Fix interprocess locking for concurrent realm file access resulting in a interprocess deadlock on FAT32/exFAT filesystems. (Core 13.23.1)

### Compatibility
* Realm Studio: 13.0.0 or later.
Expand Down
20 changes: 12 additions & 8 deletions Realm/Realm/Exceptions/Sync/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

using System;
using Realms.Sync.ErrorHandling;
using static System.Net.WebRequestMethods;

namespace Realms.Sync.Exceptions;

Expand Down Expand Up @@ -49,6 +48,11 @@ public enum ErrorCode
/// </summary>
BadChangeset = 1015,

/// <summary>
/// The client attempted to create a subscription which the server rejected.
/// </summary>
SubscriptionFailed = 1016,

/// <summary>
/// The client attempted to create a subscription for a query is invalid/malformed.
/// </summary>
Expand Down Expand Up @@ -107,6 +111,13 @@ public enum ErrorCode
/// </summary>
WrongSyncType = 1043,

/// <summary>
/// Client attempted a write that is disallowed by permissions, or modifies an
/// object outside the current query, and the server undid the modification.
/// </summary>
/// <seealso cref="CompensatingWriteException"/>
CompensatingWrite = 1033,

/// <summary>
/// Unrecognized error code. It usually indicates incompatibility between the App Services server and client SDK versions.
/// </summary>
Expand Down Expand Up @@ -197,13 +208,6 @@ public enum ErrorCode
[Obsolete("This error code is no longer reported")]
InitialSyncNotCompleted = -4,

/// <summary>
/// Client attempted a write that is disallowed by permissions, or modifies an
/// object outside the current query, and the server undid the modification.
/// </summary>
/// <seealso cref="CompensatingWriteException"/>
CompensatingWrite = 1033,

/// <summary>
/// An error sent by the server when its data structures used to track client progress
/// become corrupted.
Expand Down
26 changes: 9 additions & 17 deletions Realm/Realm/Handles/SubscriptionSetHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ internal class SubscriptionSetHandle : RealmHandle
private static class NativeMethods
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void StateWaitCallback(IntPtr task_completion_source, SubscriptionSetState new_state, PrimitiveValue message);
public delegate void StateWaitCallback(IntPtr task_completion_source, SubscriptionSetState new_state, StringValue message, ErrorCode error_code);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void GetSubscriptionCallback(IntPtr managed_callback, Native.Subscription subscription);
Expand Down Expand Up @@ -329,33 +329,25 @@ private static void OnGetSubscription(IntPtr managedCallbackPtr, Native.Subscrip
}

[MonoPInvokeCallback(typeof(NativeMethods.StateWaitCallback))]
private static void HandleStateWaitCallback(IntPtr taskCompletionSource, SubscriptionSetState state, PrimitiveValue message)
private static void HandleStateWaitCallback(IntPtr taskCompletionSource, SubscriptionSetState state, StringValue message, ErrorCode error_code)
{
var handle = GCHandle.FromIntPtr(taskCompletionSource);
var tcs = (TaskCompletionSource<SubscriptionSetState>)handle.Target!;

switch (message.Type)
switch (error_code)
{
case RealmValueType.Null:
case 0: // No error
tcs.TrySetResult(state);
break;
case RealmValueType.Int when message.AsInt() == -1:
case (ErrorCode)1027: // OperationAborted
tcs.TrySetException(new TaskCanceledException("The SubscriptionSet was closed before the wait could complete. This is likely because the Realm it belongs to was disposed."));
break;
case RealmValueType.String:
var messageString = message.AsString();
if (messageString == "Active SubscriptionSet without a SubscriptionStore")
{
tcs.TrySetException(new TaskCanceledException("The SubscriptionSet was closed before the wait could complete. This is likely because the Realm it belongs to was disposed."));
}
else
{
tcs.TrySetException(new SubscriptionException(messageString));
}

case ErrorCode.BadQuery:
case ErrorCode.SubscriptionFailed:
tcs.TrySetException(new SubscriptionException(message!));
break;
default:
tcs.TrySetException(new SubscriptionException($"An unexpected error occurred and the wrong error type was supplied: {message.Type}. Please file a new issue at https://github.com/realm/realm-dotnet/issues"));
tcs.TrySetException(new SessionException((string?)message ?? "Unknown error", error_code));
break;
}

Expand Down
10 changes: 0 additions & 10 deletions Realm/Realm/Handles/SyncUserHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ private static class NativeMethods
[DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_get_state", CallingConvention = CallingConvention.Cdecl)]
public static extern UserState get_state(SyncUserHandle user, out NativeException ex);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_get_auth_provider", CallingConvention = CallingConvention.Cdecl)]
public static extern Credentials.AuthProvider get_auth_provider(SyncUserHandle user, out NativeException ex);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncuser_get_profile_data", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr get_profile_data(SyncUserHandle user, UserProfileField field,
IntPtr buffer, IntPtr buffer_length, [MarshalAs(UnmanagedType.U1)] out bool isNull,
Expand Down Expand Up @@ -184,13 +181,6 @@ public UserState GetState()
return result;
}

public Credentials.AuthProvider GetProvider()
{
var result = NativeMethods.get_auth_provider(this, out var ex);
ex.ThrowIfNecessary();
return result;
}

public bool TryGetApp([MaybeNullWhen(false)] out AppHandle appHandle)
{
var result = NativeMethods.get_app(this, out var ex);
Expand Down
6 changes: 4 additions & 2 deletions Realm/Realm/Sync/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using MongoDB.Bson;
Expand Down Expand Up @@ -108,7 +109,8 @@ public string DeviceId
/// Gets a value indicating which <see cref="Credentials.AuthProvider"/> this user logged in with.
/// </summary>
/// <value>The <see cref="Credentials.AuthProvider"/> used to login the user.</value>
public Credentials.AuthProvider Provider => Handle.GetProvider();
[Obsolete("User.Provider wasn't working consistently and will be removed in a future version. You can get the provider of the user identity instead.")]
public Credentials.AuthProvider Provider => Identities.FirstOrDefault()?.Provider ?? Credentials.AuthProvider.Unknown;

/// <summary>
/// Gets the app with which this user is associated.
Expand Down Expand Up @@ -325,7 +327,7 @@ public async Task<User> LinkCredentialsAsync(Credentials credentials)
/// <returns>A string that represents the current object.</returns>
public override string ToString()
{
return $"User {Id}, State: {State}, Provider: {Provider}";
return $"User {Id}, State: {State}";
}

internal void RaiseChanged()
Expand Down
8 changes: 0 additions & 8 deletions Tests/Realm.Tests/Database/GeospatialTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,8 @@ public void LinqTests(GeoShapeBase shape, string[] expectedMatches)
[Test]
public void Filter_InvalidPropertyType_Throws()
{
PopulateCompanies();
_realm.Write(() =>
{
var company = _realm.All<Company>().First();

// We're making the point into a polygon, which is not supported
company.Location!.DynamicApi.Set("type", "Polygon");

_realm.Add(new ObjectWithInvalidGeoPoints
{
CoordinatesEmbedded = new()
Expand All @@ -93,8 +87,6 @@ public void Filter_InvalidPropertyType_Throws()
});
});

AssertInvalidGeoData<Company>(nameof(Company.Location), expectedError: "The only Geospatial type currently supported is 'point'");

AssertInvalidGeoData<ObjectWithInvalidGeoPoints>(nameof(ObjectWithInvalidGeoPoints.TopLevelGeoPoint), expectedError: "GEOWITHIN query can only operate on a link to an embedded class");
AssertInvalidGeoData<ObjectWithInvalidGeoPoints>(nameof(ObjectWithInvalidGeoPoints.TypeEmbedded));
AssertInvalidGeoData<ObjectWithInvalidGeoPoints>(nameof(ObjectWithInvalidGeoPoints.CoordinatesEmbedded));
Expand Down
58 changes: 38 additions & 20 deletions Tests/Realm.Tests/Sync/FlexibleSyncTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -671,8 +671,7 @@ public void SubscriptionSet_Remove_ByQuery_RemoveNamed()
});

Assert.That(realm.Subscriptions.Count, Is.EqualTo(2));
Assert.That(realm.Subscriptions[0].Name, Is.Null);
Assert.That(realm.Subscriptions[1].Name, Is.EqualTo("c"));
Assert.That(realm.Subscriptions.Select(o => o.Name), Is.EquivalentTo(new[] { null, "c" }));

realm.Subscriptions.Update(() =>
{
Expand Down Expand Up @@ -713,10 +712,7 @@ public void SubscriptionSet_Remove_ByQuery_RemoveNamed_False()
});

Assert.That(realm.Subscriptions.Count, Is.EqualTo(4));
Assert.That(realm.Subscriptions[0].Name, Is.EqualTo("a"));
Assert.That(realm.Subscriptions[1].Name, Is.EqualTo("b"));
Assert.That(realm.Subscriptions[2].Name, Is.Null);
Assert.That(realm.Subscriptions[3].Name, Is.EqualTo("c"));
Assert.That(realm.Subscriptions.Select(o => o.Name), Is.EquivalentTo(new[] { "a", "b", null, "c" }));

realm.Subscriptions.Update(() =>
{
Expand All @@ -728,9 +724,7 @@ public void SubscriptionSet_Remove_ByQuery_RemoveNamed_False()
});

Assert.That(realm.Subscriptions.Count, Is.EqualTo(3));
Assert.That(realm.Subscriptions[0].Name, Is.EqualTo("a"));
Assert.That(realm.Subscriptions[1].Name, Is.EqualTo("b"));
Assert.That(realm.Subscriptions[2].Name, Is.EqualTo("c"));
Assert.That(realm.Subscriptions.Select(o => o.Name), Is.EquivalentTo(new[] { "a", "b", "c" }));
}

[Test]
Expand Down Expand Up @@ -914,9 +908,7 @@ public void SubscriptionSet_RemoveAll_RemoveNamed_False()
});

Assert.That(realm.Subscriptions.Count, Is.EqualTo(3));
Assert.That(realm.Subscriptions[0].Name, Is.EqualTo("a"));
Assert.That(realm.Subscriptions[1].Name, Is.EqualTo("b"));
Assert.That(realm.Subscriptions[2].Name, Is.EqualTo("c"));
Assert.That(realm.Subscriptions.Select(o => o.Name), Is.EquivalentTo(new[] { "a", "b", "c" }));
}

[Test]
Expand Down Expand Up @@ -1925,7 +1917,6 @@ public void Integration_InitialSubscriptions_Unnamed([Values(true, false)] bool
}

var query = realm.All<SyncAllTypesObject>().ToArray().Select(o => o.DoubleProperty).ToArray();
Assert.That(query.Count(), Is.EqualTo(2));
Assert.That(query, Is.EquivalentTo(new[] { 1.5, 2.5 }));
}, timeout: 60_000);
}
Expand Down Expand Up @@ -1963,7 +1954,6 @@ public void Integration_InitialSubscriptions_Named([Values(true, false)] bool op
}

var query = realm.All<SyncAllTypesObject>().ToArray().Select(o => o.DoubleProperty).ToArray();
Assert.That(query.Count(), Is.EqualTo(1));
Assert.That(query, Is.EquivalentTo(new[] { 2.5 }));
});
}
Expand Down Expand Up @@ -2208,7 +2198,15 @@ public void Results_Subscribe_FirstTimeOnly_DoesntWaitForChanges([Values("abc",

writerRealm.Write(() =>
{
writerRealm.Add(new SyncAllTypesObject { DoubleProperty = 3.5, GuidProperty = testGuid, });
for (var i = 0; i < 10; i++)
{
writerRealm.Add(new SyncAllTypesObject
{
ByteArrayProperty = TestHelpers.GetBytes(100_000),
DoubleProperty = 3.5,
GuidProperty = testGuid
});
}
});

await WaitForUploadAsync(writerRealm);
Expand All @@ -2221,7 +2219,7 @@ public void Results_Subscribe_FirstTimeOnly_DoesntWaitForChanges([Values("abc",

// If we manually wait for the download, we should see the second object show up.
await WaitForDownloadAsync(realm);
Assert.That(query.Count(), Is.EqualTo(2));
Assert.That(query.Count(), Is.EqualTo(11));
});
}

Expand All @@ -2243,15 +2241,25 @@ public void Results_SubscribeNamed_FirstTimeOnly_DifferentQuery_WaitsForChanges(

writerRealm.Write(() =>
{
writerRealm.Add(new SyncAllTypesObject { DoubleProperty = 3.5, GuidProperty = testGuid, });
for (var i = 0; i < 10; i++)
{
writerRealm.Add(new SyncAllTypesObject
{
ByteArrayProperty = TestHelpers.GetBytes(100_000),
DoubleProperty = 3.5,
GuidProperty = testGuid
});
}
});

await WaitForUploadAsync(writerRealm);

// Resubscribe to a different query with same name with waitForSync.FirstTime. Even though
// we have a subscription with the same name, SubscribeAsync should wait for the new subscription.
await realm.All<SyncAllTypesObject>()
.Where(o => o.GuidProperty == testGuid && o.DoubleProperty > 2.0001)
.SubscribeAsync(new() { Name = "abc" });
Assert.That(query.Count(), Is.EqualTo(2));
Assert.That(query.Count(), Is.EqualTo(11));
});
}

Expand Down Expand Up @@ -2323,11 +2331,21 @@ public void Results_Subscribe_Always_WaitsForChanges([Values("abc", null)] strin

writerRealm.Write(() =>
{
writerRealm.Add(new SyncAllTypesObject { DoubleProperty = 3.5, GuidProperty = testGuid, });
for (var i = 0; i < 10; i++)
{
writerRealm.Add(new SyncAllTypesObject
{
ByteArrayProperty = TestHelpers.GetBytes(100_000),
DoubleProperty = 3.5,
GuidProperty = testGuid
});
}
});

await WaitForUploadAsync(writerRealm);

await query.SubscribeAsync(new() { Name = name }, WaitForSyncMode.Always);
Assert.That(query.Count(), Is.EqualTo(2));
Assert.That(query.Count(), Is.EqualTo(11));
});
}

Expand Down
9 changes: 3 additions & 6 deletions Tests/Realm.Tests/Sync/UserManagementTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,6 @@ public void EmailPasswordRegisterUser_Works()

Assert.That(user, Is.Not.Null);
Assert.That(user.State, Is.EqualTo(UserState.LoggedIn));
Assert.That(user.Provider, Is.EqualTo(Credentials.AuthProvider.EmailPassword));
Assert.That(user.AccessToken, Is.Not.Empty);
Assert.That(user.RefreshToken, Is.Not.Empty);

Expand Down Expand Up @@ -756,8 +755,8 @@ public void UserApiKeys_CanLoginWithGeneratedKey()

Assert.That(apiKeyUser.Id, Is.EqualTo(user.Id));

Assert.That(apiKeyUser.Provider, Is.EqualTo(Credentials.AuthProvider.ApiKey));
Assert.That(apiKeyUser.RefreshToken, Is.Not.EqualTo(user.RefreshToken));
Assert.That(apiKeyUser.Identities.Select(i => i.Provider), Does.Contain(Credentials.AuthProvider.ApiKey));
Assert.That(apiKeyUser.RefreshToken, Is.EqualTo(user.RefreshToken));
});
}

Expand All @@ -784,8 +783,7 @@ public void UserApiKeys_CanLoginWithReenabledKey()

Assert.That(apiKeyUser.Id, Is.EqualTo(user.Id));

Assert.That(apiKeyUser.Provider, Is.EqualTo(Credentials.AuthProvider.ApiKey));
Assert.That(apiKeyUser.RefreshToken, Is.Not.EqualTo(user.RefreshToken));
Assert.That(apiKeyUser.RefreshToken, Is.EqualTo(user.RefreshToken));
});
}

Expand Down Expand Up @@ -980,7 +978,6 @@ public void UserToStringOverride()
{
var user = GetFakeUser();
Assert.That(user.ToString(), Does.Contain(user.Id));
Assert.That(user.ToString(), Does.Contain(user.Provider.ToString()));
}

[Test, Ignore("test")]
Expand Down
2 changes: 1 addition & 1 deletion wrappers/realm-core
Submodule realm-core updated 217 files
3 changes: 1 addition & 2 deletions wrappers/src/app_cs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,7 @@ extern "C" {
id,
refresh_token,
access_token,
"testing",
"my-device-id"));
"testing"));
});
}

Expand Down
Loading

0 comments on commit b969719

Please sign in to comment.