diff --git a/.github/workflows/deploy-baas.yml b/.github/workflows/deploy-baas.yml
index 3008c6d486..0f772a9ba5 100755
--- a/.github/workflows/deploy-baas.yml
+++ b/.github/workflows/deploy-baas.yml
@@ -54,7 +54,7 @@ jobs:
apiKey: ${{ secrets.AtlasPublicKey}}
privateApiKey: ${{ secrets.AtlasPrivateKey }}
clusterName: ${{ steps.generate-cluster-name.outputs.clusterName }}-${{ matrix.differentiator }}
- clusterSize: M20
+ clusterSize: M5
deploy-apps:
name: Deploy Apps
needs: deploy-baas
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3d262b9873..0ebbb49f0f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,7 @@
## vNext (TBD)
### Enhancements
-* None
+* Streamlined some of the error codes reported in `SessionException`. A few error codes have been combined and some have been deprecated since they are no longer reported by the server. (Issue [#3295](https://github.com/realm/realm-dotnet/issues/3295))
### Fixed
* Fixed the message of the `MissingMemberException` being thrown when attempting to access a non-existent property with the dynamic API. (PR [#3432](https://github.com/realm/realm-dotnet/pull/3432))
diff --git a/Realm/Realm/Exceptions/Sync/ClientError.cs b/Realm/Realm/Exceptions/Sync/ClientError.cs
index 4697cc5710..6cf93d3133 100644
--- a/Realm/Realm/Exceptions/Sync/ClientError.cs
+++ b/Realm/Realm/Exceptions/Sync/ClientError.cs
@@ -26,12 +26,6 @@ internal enum ClientError
AutoClientResetFailed = 132,
}
- internal enum SessionErrorCategory : byte
- {
- ClientError = 0,
- SessionError = 1
- }
-
internal enum ServerRequestsAction
{
NoAction = 0,
diff --git a/Realm/Realm/Exceptions/Sync/ErrorCode.cs b/Realm/Realm/Exceptions/Sync/ErrorCode.cs
index fd1fc7cf0d..f78d79dfeb 100644
--- a/Realm/Realm/Exceptions/Sync/ErrorCode.cs
+++ b/Realm/Realm/Exceptions/Sync/ErrorCode.cs
@@ -16,125 +16,201 @@
//
////////////////////////////////////////////////////////////////////////////
-namespace Realms.Sync.Exceptions
+using System;
+using Realms.Sync.ErrorHandling;
+using static System.Net.WebRequestMethods;
+
+namespace Realms.Sync.Exceptions;
+
+///
+/// Error code enumeration, indicating the type of the session error.
+///
+///
+public enum ErrorCode
{
///
- /// Error code enumeration, indicating the type of the session error.
- ///
- ///
- public enum ErrorCode
- {
- ///
- /// Unrecognized error code. It usually indicates incompatibility between the authentication server and client SDK versions.
- ///
- Unknown = -1,
-
- ///
- /// Other session level error has occurred.
- ///
- OtherSessionError = 201,
-
- ///
- /// Path to Realm is invalid.
- ///
- IllegalRealmPath = 204,
-
- ///
- /// Permission to Realm has been denied.
- ///
- PermissionDenied = 206,
-
- ///
- /// The client file identifier is invalid.
- ///
- BadClientFileIdentifier = 208,
-
- ///
- /// The server version is invalid.
- ///
- BadServerVersion = 209,
-
- ///
- /// The client version is invalid.
- ///
- BadClientVersion = 210,
-
- ///
- /// Histories have diverged and cannot be merged.
- ///
- DivergingHistories = 211,
-
- ///
- /// The changeset is invalid.
- ///
- BadChangeset = 212,
-
- ///
- /// The client file is invalid.
- ///
- BadClientFile = 217,
-
- ///
- /// Client file has expired likely due to history compaction on the server.
- ///
- ClientFileExpired = 222,
-
- ///
- /// The user for this session doesn't match the user who originally created the file. This can happen
- /// if you explicitly specify the Realm file path in the configuration and you open the Realm first with
- /// user A, then with user B without changing the on-disk path.
- ///
- UserMismatch = 223,
-
- ///
- /// The server has received too many sessions from this client. This is typically a transient error
- /// but can also indicate that the client has too many Realms open at the same time.
- ///
- TooManySessions = 224,
-
- ///
- /// The client attempted to upload an invalid schema change - either an additive schema change
- /// when developer mode is off or a destructive schema change.
- ///
- InvalidSchemaChange = 225,
-
- ///
- /// The client attempted to create a subscription for a query is invalid/malformed.
- ///
- BadQuery = 226,
-
- ///
- /// The client attempted to create an object that already exists outside their view.
- ///
- ObjectAlreadyExists = 227,
-
- ///
- /// The server permissions for this file have changed since the last time it was used.
- ///
- ServerPermissionsChanged = 228,
-
- ///
- /// The client tried to synchronize before initial sync has completed. Please wait for
- /// the server process to complete and try again.
- ///
- InitialSyncNotCompleted = 229,
-
- ///
- /// Client attempted a write that is disallowed by permissions, or modifies an object
- /// outside the current query - requires client reset.
- ///
- WriteNotAllowed = 230,
-
- ///
- /// Client attempted a write that is disallowed by permissions, or modifies an
- /// object outside the current query, and the server undid the modification.
- ///
- CompensatingWrite = 231,
-
- ///
- /// An error sent by the server when its data structures used to track client progress
- /// become corrupted.
- ///
- BadProgress = 233,
- }
+ /// Unrecognized error code. It usually indicates incompatibility between the App Services server and client SDK versions.
+ ///
+ RuntimeError = 1000,
+
+ ///
+ /// The partition value specified by the user is not valid - i.e. its the wrong type or is encoded incorrectly.
+ ///
+ BadPartitionValue = 1029,
+
+ ///
+ /// A fundamental invariant in the communication between the client and the server was not upheld. This typically indicates
+ /// a bug in the synchronization layer and should be reported at https://github.com/realm/realm-core/issues.
+ ///
+ ProtocolInvariantFailed = 1038,
+
+ ///
+ /// The changeset is invalid.
+ ///
+ BadChangeset = 1015,
+
+ ///
+ /// The client attempted to create a subscription for a query is invalid/malformed.
+ ///
+ BadQuery = 1031,
+
+ ///
+ /// A client reset has occurred. This error code will only be reported via a and only
+ /// in the case manual client reset handling is required - either via or when
+ /// ManualResetFallback is invoked on one of the automatic client reset handlers.
+ ///
+ ///
+ ///
+ ClientReset = 1032,
+
+ ///
+ /// The client attempted to upload an invalid schema change - either an additive schema change
+ /// when developer mode is off or a destructive schema change.
+ ///
+ InvalidSchemaChange = 1036,
+
+ ///
+ /// Permission to Realm has been denied.
+ ///
+ PermissionDenied = 1037,
+
+ ///
+ /// The server permissions for this file have changed since the last time it was used.
+ ///
+ ServerPermissionsChanged = 1040,
+
+ ///
+ /// The user for this session doesn't match the user who originally created the file. This can happen
+ /// if you explicitly specify the Realm file path in the configuration and you open the Realm first with
+ /// user A, then with user B without changing the on-disk path.
+ ///
+ UserMismatch = 1041,
+
+ ///
+ /// Client attempted a write that is disallowed by permissions, or modifies an object
+ /// outside the current query - this will result in a .
+ ///
+ WriteNotAllowed = 1044,
+
+ ///
+ /// Automatic client reset has failed. This will only be reported via
+ /// when an automatic client reset handler was used but it failed to perform the client reset operation -
+ /// typically due to a breaking schema change in the server schema or due to an exception occurring in the
+ /// before or after client reset callbacks.
+ ///
+ AutoClientResetFailed = 1028,
+
+ ///
+ /// The wrong sync type was used to connect to the server. This means that you're using
+ /// to connect to an app configured for flexible sync or that you're using to connect
+ /// to an app configured to use partition sync.
+ ///
+ WrongSyncType = 1043,
+
+ ///
+ /// Unrecognized error code. It usually indicates incompatibility between the App Services server and client SDK versions.
+ ///
+ [Obsolete("Use RuntimeError instead.")]
+ Unknown = RuntimeError,
+
+ ///
+ /// Other session level error has occurred.
+ ///
+ ///
+ /// Sync error reporting has been simplified and some errors have been unified. See the obsoletion message for details on the new error code.
+ ///
+ [Obsolete("Use RuntimeError instead.")]
+ OtherSessionError = RuntimeError,
+
+ ///
+ /// Path to Realm is invalid.
+ ///
+ ///
+ /// Sync error reporting has been simplified and some errors have been unified. See the obsoletion message for details on the new error code.
+ ///
+ [Obsolete("Use BadPartitionValue instead")]
+ IllegalRealmPath = BadPartitionValue,
+
+ ///
+ /// The client file identifier is invalid.
+ ///
+ ///
+ [Obsolete("Use ClientReset instead")]
+ BadClientFileIdentifier = ClientReset,
+
+ ///
+ /// The server version is invalid.
+ ///
+ ///
+ /// Sync error reporting has been simplified and some errors have been unified. See the obsoletion message for details on the new error code.
+ ///
+ [Obsolete("Use ProtocolInvariantFailed instead")]
+ BadServerVersion = ProtocolInvariantFailed,
+
+ ///
+ /// The client version is invalid.
+ ///
+ ///
+ /// Sync error reporting has been simplified and some errors have been unified. See the obsoletion message for details on the new error code.
+ ///
+ [Obsolete("Use ProtocolInvariantFailed instead")]
+ BadClientVersion = ProtocolInvariantFailed,
+
+ ///
+ /// Histories have diverged and cannot be merged.
+ ///
+ ///
+ [Obsolete("Use ClientReset instead")]
+ DivergingHistories = ClientReset,
+
+ ///
+ /// The client file is invalid.
+ ///
+ ///
+ [Obsolete("Use ClientReset instead")]
+ BadClientFile = ClientReset,
+
+ ///
+ /// Client file has expired likely due to history compaction on the server.
+ ///
+ ///
+ [Obsolete("Use ClientReset instead")]
+ ClientFileExpired = ClientReset,
+
+ ///
+ /// The server has received too many sessions from this client. This is typically a transient error
+ /// but can also indicate that the client has too many Realms open at the same time.
+ ///
+ [Obsolete("This error code is no longer reported")]
+ TooManySessions = -2,
+
+ ///
+ /// The client attempted to create an object that already exists outside their view.
+ ///
+ [Obsolete("This error code is no longer reported")]
+ ObjectAlreadyExists = -3,
+
+ ///
+ /// The client tried to synchronize before initial sync has completed. Please wait for
+ /// the server process to complete and try again.
+ ///
+ [Obsolete("This error code is no longer reported")]
+ InitialSyncNotCompleted = -4,
+
+ ///
+ /// Client attempted a write that is disallowed by permissions, or modifies an
+ /// object outside the current query, and the server undid the modification.
+ ///
+ ///
+ CompensatingWrite = 1033,
+
+ ///
+ /// An error sent by the server when its data structures used to track client progress
+ /// become corrupted.
+ ///
+ ///
+ /// Sync error reporting has been simplified and some errors have been unified. See the obsoletion message for details on the new error code.
+ ///
+ [Obsolete("Use ProtocolInvariantFailed instead")]
+ BadProgress = ProtocolInvariantFailed,
}
diff --git a/Realm/Realm/Extensions/TestingExtensions.cs b/Realm/Realm/Extensions/TestingExtensions.cs
index ab270a54ec..8e9917138a 100644
--- a/Realm/Realm/Extensions/TestingExtensions.cs
+++ b/Realm/Realm/Extensions/TestingExtensions.cs
@@ -44,7 +44,7 @@ public static void SimulateError(this Session session, ErrorCode errorCode, stri
Argument.NotNull(session, nameof(session));
Argument.NotNull(message, nameof(message));
- session.ReportErrorForTesting((int)errorCode, SessionErrorCategory.SessionError, message, isFatal, ServerRequestsAction.ApplicationBug);
+ session.ReportErrorForTesting((int)errorCode, message, isFatal, ServerRequestsAction.ApplicationBug);
}
}
}
diff --git a/Realm/Realm/Handles/SessionHandle.cs b/Realm/Realm/Handles/SessionHandle.cs
index 463a25fe64..e2c958603d 100644
--- a/Realm/Realm/Handles/SessionHandle.cs
+++ b/Realm/Realm/Handles/SessionHandle.cs
@@ -102,7 +102,7 @@ public static extern ulong register_progress_notifier(SessionHandle session,
public static extern void wait(SessionHandle session, IntPtr task_completion_source, ProgressDirection direction, out NativeException ex);
[DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_report_error_for_testing", CallingConvention = CallingConvention.Cdecl)]
- public static extern void report_error_for_testing(SessionHandle session, int error_code, SessionErrorCategory error_category, [MarshalAs(UnmanagedType.LPWStr)] string message, IntPtr message_len, [MarshalAs(UnmanagedType.U1)] bool is_fatal, int action);
+ public static extern void report_error_for_testing(SessionHandle session, int error_code, [MarshalAs(UnmanagedType.LPWStr)] string message, IntPtr message_len, [MarshalAs(UnmanagedType.U1)] bool is_fatal, int action);
[DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_syncsession_stop", CallingConvention = CallingConvention.Cdecl)]
public static extern void stop(SessionHandle session, out NativeException ex);
@@ -246,9 +246,9 @@ public IntPtr GetRawPointer()
return NativeMethods.get_raw_pointer(this);
}
- public void ReportErrorForTesting(int errorCode, SessionErrorCategory errorCategory, string errorMessage, bool isFatal, ServerRequestsAction action)
+ public void ReportErrorForTesting(int errorCode, string errorMessage, bool isFatal, ServerRequestsAction action)
{
- NativeMethods.report_error_for_testing(this, errorCode, errorCategory, errorMessage, (IntPtr)errorMessage.Length, isFatal, (int)action);
+ NativeMethods.report_error_for_testing(this, errorCode, errorMessage, (IntPtr)errorMessage.Length, isFatal, (int)action);
}
public void Stop()
diff --git a/Realm/Realm/Sync/Session.cs b/Realm/Realm/Sync/Session.cs
index ac20ce3437..a07eb8496d 100644
--- a/Realm/Realm/Sync/Session.cs
+++ b/Realm/Realm/Sync/Session.cs
@@ -222,7 +222,8 @@ internal void CloseHandle(bool waitForShutdown = false)
}
}
- internal void ReportErrorForTesting(int errorCode, SessionErrorCategory sessionErrorCategory, string errorMessage, bool isFatal, ServerRequestsAction action) => Handle.ReportErrorForTesting(errorCode, sessionErrorCategory, errorMessage, isFatal, action);
+ internal void ReportErrorForTesting(int errorCode, string errorMessage, bool isFatal, ServerRequestsAction action)
+ => Handle.ReportErrorForTesting(errorCode, errorMessage, isFatal, action);
internal void RaisePropertyChanged(string propertyName)
{
diff --git a/Tests/Realm.Tests/Sync/AsymmetricObjectTests.cs b/Tests/Realm.Tests/Sync/AsymmetricObjectTests.cs
index 59c8c1cf89..a7fa3431d0 100644
--- a/Tests/Realm.Tests/Sync/AsymmetricObjectTests.cs
+++ b/Tests/Realm.Tests/Sync/AsymmetricObjectTests.cs
@@ -28,8 +28,6 @@
using Realms.Sync;
#if TEST_WEAVER
using TestAsymmetricObject = Realms.AsymmetricObject;
-using TestEmbeddedObject = Realms.EmbeddedObject;
-using TestRealmObject = Realms.RealmObject;
#else
using TestAsymmetricObject = Realms.IAsymmetricObject;
#endif
@@ -90,31 +88,12 @@ public class AsymmetricObjectTests : SyncTestBase
new object[] { "NullableGuidProperty", Guid.Parse("{C4EC8CEF-D62A-405E-83BB-B0A3D8DABB36}") }
};
- [Test]
- public void AddAsymmetricObjNotInSchema_Throws()
- {
- SyncTestHelpers.RunBaasTestAsync(async () =>
- {
- var flxConfig = await GetFLXIntegrationConfigAsync();
- using var realm = await GetRealmAsync(flxConfig);
-
- Assert.Throws(() =>
- {
- realm.Write(() =>
- {
- realm.Add(new BasicAsymmetricObject());
- });
- });
- });
- }
-
[Test]
public void AddCollectionOfAsymmetricObjs()
{
SyncTestHelpers.RunBaasTestAsync(async () =>
{
var flxConfig = await GetFLXIntegrationConfigAsync();
- flxConfig.Schema = new[] { typeof(BasicAsymmetricObject) };
using var realm = await GetRealmAsync(flxConfig);
var partitionLike = Guid.NewGuid().ToString();
@@ -148,7 +127,6 @@ public void AddCollection_WithSomeObjectsAlreadyAdded_Throws()
SyncTestHelpers.RunBaasTestAsync(async () =>
{
var flxConfig = await GetFLXIntegrationConfigAsync();
- flxConfig.Schema = new[] { typeof(BasicAsymmetricObject) };
using var realm = await GetRealmAsync(flxConfig);
var partitionLike = Guid.NewGuid().ToString();
@@ -183,7 +161,6 @@ public void AddHugeAsymmetricObj()
ObjectId id = default;
var flxConfig = await GetFLXIntegrationConfigAsync();
- flxConfig.Schema = new[] { typeof(AsymmetricObjectWithAllTypes) };
using var realm = await GetRealmAsync(flxConfig);
realm.Write(() =>
@@ -206,9 +183,7 @@ public void AccessAsymmetricObjAfterAddedToRealm_Throws()
SyncTestHelpers.RunBaasTestAsync(async () =>
{
var partitionLike = Guid.NewGuid().ToString();
- var flxConfig = await GetFLXIntegrationConfigAsync();
- flxConfig.Schema = new[] { typeof(BasicAsymmetricObject) };
- using var realm = await GetRealmAsync(flxConfig);
+ using var realm = await GetFLXIntegrationRealmAsync();
var asymmetribObj = new BasicAsymmetricObject
{
@@ -233,9 +208,7 @@ public void AddSameAsymmetricObjTwice_Throws()
{
SyncTestHelpers.RunBaasTestAsync(async () =>
{
- var flxConfig = await GetFLXIntegrationConfigAsync();
- flxConfig.Schema = new[] { typeof(BasicAsymmetricObject) };
- using var realm = await GetRealmAsync(flxConfig);
+ using var realm = await GetFLXIntegrationRealmAsync();
var partitionLike = Guid.NewGuid().ToString();
var asymmetricObj = new BasicAsymmetricObject
{
@@ -261,8 +234,6 @@ public void SetAndRemotelyReadValue(string propertyName, object propertyValue)
{
ObjectId id = default;
var flxConfig = await GetFLXIntegrationConfigAsync();
- flxConfig.Schema = new[] { typeof(AsymmetricObjectWithAllTypes) };
-
using var realm = await GetRealmAsync(flxConfig);
realm.Write(() =>
@@ -290,7 +261,6 @@ public void MixAddingObjectAsymmetricAndNot()
var partitionLike = Guid.NewGuid().ToString();
var id = new Random().Next();
var flxConfig = await GetFLXIntegrationConfigAsync();
- flxConfig.Schema = new[] { typeof(BasicAsymmetricObject), typeof(PrimaryKeyInt32Object) };
flxConfig.PopulateInitialSubscriptions = (realm) =>
{
@@ -346,15 +316,7 @@ public void EmbeddedObject_WhenParentAccessed_ReturnsParent()
{
SyncTestHelpers.RunBaasTestAsync(async () =>
{
- var flxConfig = await GetFLXIntegrationConfigAsync();
- flxConfig.Schema = new[]
- {
- typeof(AsymmetricObjectWithEmbeddedRecursiveObject),
- typeof(EmbeddedLevel1),
- typeof(EmbeddedLevel2),
- typeof(EmbeddedLevel3)
- };
- using var realm = await GetRealmAsync(flxConfig);
+ using var realm = await GetFLXIntegrationRealmAsync();
var parent = new AsymmetricObjectWithEmbeddedRecursiveObject
{
@@ -387,9 +349,7 @@ public void EmbeddedObject_WhenParentAccessedInList_ReturnsParent()
{
SyncTestHelpers.RunBaasTestAsync(async () =>
{
- var flxConfig = await GetFLXIntegrationConfigAsync();
- flxConfig.Schema = new[] { typeof(AsymmetricObjectWithEmbeddedListObject), typeof(EmbeddedIntPropertyObject) };
- using var realm = await GetRealmAsync(flxConfig);
+ using var realm = await GetFLXIntegrationRealmAsync();
var parent = new AsymmetricObjectWithEmbeddedListObject();
parent.EmbeddedListObject.Add(new EmbeddedIntPropertyObject());
@@ -408,9 +368,7 @@ public void EmbeddedObject_WhenParentAccessedInDictionary_ReturnsParent()
{
SyncTestHelpers.RunBaasTestAsync(async () =>
{
- var flxConfig = await GetFLXIntegrationConfigAsync();
- flxConfig.Schema = new[] { typeof(AsymmetricObjectWithEmbeddedDictionaryObject), typeof(EmbeddedIntPropertyObject) };
- using var realm = await GetRealmAsync(flxConfig);
+ using var realm = await GetFLXIntegrationRealmAsync();
var parent = new AsymmetricObjectWithEmbeddedDictionaryObject();
parent.EmbeddedDictionaryObject.Add("child", new EmbeddedIntPropertyObject());
@@ -452,9 +410,7 @@ public void NonEmbeddedObject_WhenParentAccessed_Throws()
{
SyncTestHelpers.RunBaasTestAsync(async () =>
{
- var flxConfig = await GetFLXIntegrationConfigAsync();
- flxConfig.Schema = new[] { typeof(BasicAsymmetricObject) };
- using var realm = await GetRealmAsync(flxConfig);
+ using var realm = await GetFLXIntegrationRealmAsync();
var topLevel = new BasicAsymmetricObject
{
@@ -627,7 +583,6 @@ public void DynamicAccess([Values(true, false)] bool isDynamic)
{
var flxConfig = await GetFLXIntegrationConfigAsync();
flxConfig.IsDynamic = isDynamic;
- flxConfig.Schema = new[] { typeof(AsymmetricObjectWithAllTypes) };
using var realm = await GetRealmAsync(flxConfig);
realm.Write(() =>
@@ -698,7 +653,6 @@ private static Task GetRemoteObjects(User user, string remoteFieldName,
private async Task GetRealmWithRealmValueSchemaAsync()
{
var flxConfig = await GetFLXIntegrationConfigAsync();
- flxConfig.Schema = new[] { typeof(RealmValueObject), typeof(BasicAsymmetricObject) };
flxConfig.PopulateInitialSubscriptions = (realm) =>
{
var query = realm.All();
diff --git a/Tests/Realm.Tests/Sync/FlexibleSyncTests.cs b/Tests/Realm.Tests/Sync/FlexibleSyncTests.cs
index efa2fb8603..999927d4d2 100644
--- a/Tests/Realm.Tests/Sync/FlexibleSyncTests.cs
+++ b/Tests/Realm.Tests/Sync/FlexibleSyncTests.cs
@@ -2211,6 +2211,8 @@ public void Results_Subscribe_FirstTimeOnly_DoesntWaitForChanges([Values("abc",
writerRealm.Add(new SyncAllTypesObject { DoubleProperty = 3.5, GuidProperty = testGuid, });
});
+ await WaitForUploadAsync(writerRealm);
+
// Resubscribe to the same query with waitForSync.FirstTime. Since
// we already have this subscription, SubscribeAsync should return
// immediately without waiting for the download to happen.
diff --git a/Tests/Realm.Tests/Sync/SessionTests.cs b/Tests/Realm.Tests/Sync/SessionTests.cs
index 48fbeb0a77..a415b73427 100644
--- a/Tests/Realm.Tests/Sync/SessionTests.cs
+++ b/Tests/Realm.Tests/Sync/SessionTests.cs
@@ -136,7 +136,7 @@ public void Session_ClientReset_ManualRecovery_InitiateClientReset(string appTyp
Assert.That(clientEx.Message, Does.Contain("Bad client file identifier"));
Assert.That(clientEx.InnerException, Is.Null);
- await TryInitiateClientReset(realm, clientEx, (int)ErrorCode.BadClientFileIdentifier);
+ await TryInitiateClientReset(realm, clientEx, ErrorCode.ClientReset);
});
}
@@ -169,7 +169,7 @@ void beforeCb(Realm _)
var clientEx = await errorTcs.Task.Timeout(20_000, "Expected client reset");
- await TryInitiateClientReset(realm, clientEx, (int)ClientError.AutoClientResetFailed);
+ await TryInitiateClientReset(realm, clientEx, ErrorCode.AutoClientResetFailed);
});
}
@@ -216,6 +216,8 @@ public void Session_AutomaticRecoveryFallsbackToDiscardLocal(string appType)
{
SyncTestHelpers.RunBaasTestAsync(async () =>
{
+ await DisableClientResetRecoveryOnServer(appType);
+
var automaticResetCalled = false;
var discardLocalResetCalled = false;
@@ -254,7 +256,6 @@ public void Session_AutomaticRecoveryFallsbackToDiscardLocal(string appType)
realm.Add(new ObjectWithPartitionValue(guid));
});
- await DisableClientResetRecoveryOnServer(appType);
await TriggerClientReset(realm);
await tcsAfterClientReset.Task.Timeout(20_000, detail: "Expected client reset");
@@ -410,8 +411,6 @@ public void SessionIntegrationTest_ClientResetHandlers_OutOfBoundArrayInsert_Add
config = GetIntegrationConfig(user);
}
- config.Schema = new[] { typeof(ObjectWithPartitionValue) };
-
return (config, guid);
}
@@ -731,7 +730,7 @@ public void Session_OnSessionError()
{
Assert.That(sender, Is.InstanceOf());
Assert.That(e, Is.InstanceOf());
- Assert.That(e.ErrorCode, Is.EqualTo(ErrorCode.TooManySessions));
+ Assert.That(e.ErrorCode, Is.EqualTo(ErrorCode.WriteNotAllowed));
Assert.That(e.Message, Is.EqualTo(errorMsg));
Assert.That(e.InnerException, Is.Null);
Assert.That(sessionErrorTriggered, Is.False);
@@ -741,7 +740,9 @@ public void Session_OnSessionError()
using var realm = await GetRealmAsync(config, waitForSync: true);
var session = GetSession(realm);
- session.SimulateError(ErrorCode.TooManySessions, errorMsg);
+
+ var protocolError = 230; // ProtocolError::write_not_allowed
+ session.SimulateError((ErrorCode)protocolError, errorMsg);
await tcs.Task;
@@ -1189,7 +1190,7 @@ public void Session_WhenDisposed_MethodsThrow()
Assert.Throws(() => _ = session.Equals(session));
Assert.Throws(() => _ = session.WaitForDownloadAsync());
Assert.Throws(() => _ = session.WaitForUploadAsync());
- Assert.Throws(() => session.ReportErrorForTesting(1, SessionErrorCategory.SessionError, "test", false, ServerRequestsAction.ApplicationBug));
+ Assert.Throws(() => session.ReportErrorForTesting(1, "test", false, ServerRequestsAction.ApplicationBug));
// Calling CloseHandle multiple times should be fine
session.CloseHandle();
@@ -1458,14 +1459,14 @@ private static ClientResetHandlerBase GetClientResetHandler(
return handler;
}
- private static async Task TryInitiateClientReset(Realm realm, ClientResetException ex, int expectedError)
+ private static async Task TryInitiateClientReset(Realm realm, ClientResetException ex, ErrorCode expectedError)
{
if (!realm.IsClosed)
{
realm.Dispose();
}
- Assert.That((int)ex.ErrorCode, Is.EqualTo(expectedError));
+ Assert.That(ex.ErrorCode, Is.EqualTo(expectedError));
Assert.That(File.Exists(realm.Config.DatabasePath), Is.True);
var didReset = false;
diff --git a/Tests/Realm.Tests/Sync/SyncTestBase.cs b/Tests/Realm.Tests/Sync/SyncTestBase.cs
index 39e5b94b2c..9af7590fbd 100644
--- a/Tests/Realm.Tests/Sync/SyncTestBase.cs
+++ b/Tests/Realm.Tests/Sync/SyncTestBase.cs
@@ -22,6 +22,7 @@
using System.Threading.Tasks;
using Baas;
using MongoDB.Bson;
+using Realms.Schema;
using Realms.Sync;
using Realms.Sync.Exceptions;
using static Realms.Tests.TestHelpers;
@@ -220,7 +221,36 @@ protected async Task GetRealmAsync(SyncConfigurationBase config, bool wai
private static T UpdateConfig(T config)
where T : SyncConfigurationBase
{
- config.Schema = new[] { typeof(HugeSyncObject), typeof(PrimaryKeyStringObject), typeof(ObjectIdPrimaryKeyWithValueObject), typeof(SyncCollectionsObject), typeof(IntPropertyObject), typeof(EmbeddedIntPropertyObject), typeof(SyncAllTypesObject) };
+ var schema = new RealmSchema.Builder()
+ {
+ typeof(HugeSyncObject),
+ typeof(PrimaryKeyStringObject),
+ typeof(ObjectIdPrimaryKeyWithValueObject),
+ typeof(SyncCollectionsObject),
+ typeof(IntPropertyObject),
+ typeof(EmbeddedIntPropertyObject),
+ typeof(SyncAllTypesObject),
+ typeof(ObjectWithPartitionValue),
+ };
+
+ if (config is FlexibleSyncConfiguration)
+ {
+ // We need to add all objects ever used by sync to the flx schema due to the way breaking schema changes work
+ // in dev mode. When a client connects with a subset of the server schema, they'll experience client reset as the
+ // server removes the missing tables and re-bootstraps.
+ schema.Add(typeof(BasicAsymmetricObject));
+ schema.Add(typeof(AsymmetricObjectWithAllTypes));
+ schema.Add(typeof(AsymmetricObjectWithEmbeddedRecursiveObject));
+ schema.Add(typeof(EmbeddedLevel1));
+ schema.Add(typeof(EmbeddedLevel2));
+ schema.Add(typeof(EmbeddedLevel3));
+ schema.Add(typeof(RealmValueObject));
+ schema.Add(typeof(AsymmetricObjectWithEmbeddedDictionaryObject));
+ schema.Add(typeof(AsymmetricObjectWithEmbeddedListObject));
+ schema.Add(typeof(PrimaryKeyInt32Object));
+ }
+
+ config.Schema = schema;
config.SessionStopPolicy = SessionStopPolicy.Immediately;
return config;
diff --git a/wrappers/realm-core b/wrappers/realm-core
index 03ba58ace5..673be45ce2 160000
--- a/wrappers/realm-core
+++ b/wrappers/realm-core
@@ -1 +1 @@
-Subproject commit 03ba58ace5d29685154a9287d1f914aabd9b4928
+Subproject commit 673be45ce266cef009ac5af58e8ff7327dd0d234
diff --git a/wrappers/src/error_handling.cpp b/wrappers/src/error_handling.cpp
index 2438ecb37b..7ac267441d 100644
--- a/wrappers/src/error_handling.cpp
+++ b/wrappers/src/error_handling.cpp
@@ -62,7 +62,7 @@ namespace realm {
catch (const SyncError& e) {
REALM_ASSERT_DEBUG(false);
- return NativeException(e);
+ return NativeException(e.status.code(), e.status.reason());
}
catch (const Exception& e) {
return NativeException(e);
diff --git a/wrappers/src/shared_realm_cs.cpp b/wrappers/src/shared_realm_cs.cpp
index ee687a90ed..53dc338c9e 100644
--- a/wrappers/src/shared_realm_cs.cpp
+++ b/wrappers/src/shared_realm_cs.cpp
@@ -178,7 +178,7 @@ Realm::Config get_shared_realm_config(Configuration configuration, std::optional
config.schema_mode = sync_configuration->schema_mode;
if (sync_configuration->is_flexible_sync) {
- config.sync_config = std::make_shared(*sync_configuration->user, realm::SyncConfig::FLXSyncEnabled{});
+ config.sync_config = std::make_shared(*sync_configuration->user, realm::SyncConfig::FLXSyncEnabled{});
}
else {
std::string partition(Utf16StringAccessor(sync_configuration->partition, sync_configuration->partition_len));
@@ -202,7 +202,7 @@ Realm::Config get_shared_realm_config(Configuration configuration, std::optional
}
realm_sync_error marshaled_error{
- error.get_system_error().value(),
+ error.status.code(),
to_capi(error.simple_message),
to_capi(error.logURL),
error.is_client_reset_requested(),
diff --git a/wrappers/src/sync_session_cs.cpp b/wrappers/src/sync_session_cs.cpp
index 96ecd8339b..c4f915a0c5 100644
--- a/wrappers/src/sync_session_cs.cpp
+++ b/wrappers/src/sync_session_cs.cpp
@@ -175,24 +175,13 @@ enum class SessionErrorCategory : uint8_t {
SessionError = 1
};
-REALM_EXPORT void realm_syncsession_report_error_for_testing(const SharedSyncSession& session, int err, SessionErrorCategory error_category, const uint16_t* message_buf, size_t message_len, bool is_fatal, int server_requests_action)
+REALM_EXPORT void realm_syncsession_report_error_for_testing(const SharedSyncSession& session, int err, const uint16_t* message_buf, size_t message_len, bool is_fatal, int server_requests_action)
{
Utf16StringAccessor message(message_buf, message_len);
std::error_code error_code;
- switch (error_category) {
- case SessionErrorCategory::ClientError:
- error_code = std::error_code(err, realm::sync::client_error_category());
- break;
- case SessionErrorCategory::SessionError:
- error_code = std::error_code(err, realm::sync::protocol_error_category());
- break;
- default:
- // in case a new category isn't handle, just don't trigger any error
- return;
- }
-
- sync::SessionErrorInfo error{ error_code, std::move(message), is_fatal };
+ sync::ProtocolErrorInfo protocol_error(err, message, is_fatal);
+ sync::SessionErrorInfo error(protocol_error);
error.server_requests_action = static_cast(server_requests_action);
SyncSession::OnlyForTesting::handle_error(*session, std::move(error));
diff --git a/wrappers/src/websocket_cs.cpp b/wrappers/src/websocket_cs.cpp
index 419c6e34a4..635c8b399c 100644
--- a/wrappers/src/websocket_cs.cpp
+++ b/wrappers/src/websocket_cs.cpp
@@ -187,7 +187,7 @@ extern "C" {
}
REALM_EXPORT void realm_websocket_observer_closed_handler(WebSocketObserver* observer, bool was_clean, websocket::WebSocketError error_code, realm_string_t reason) {
- observer->websocket_closed_handler(was_clean, Status(error_code, from_capi(reason)));
+ observer->websocket_closed_handler(was_clean, error_code, capi_to_std(reason));
}
}