Hi all,
I started with the .net maui cbl realm example app and quickly worked towards refactoring that data service into standalone, dependency-injection friendly project so I could test with a “CBL repository service” using xunit and then bring it back over to a .net maui application and/or wherever.
I am working from an “offline first” pattern, creating a local CouchbaseLite database and only (when I get it working ) synchronizing to a Capella hosted app service when needed/requested.
The local version of the db is running and passing basic CRUD tests, but I’m stuck implementing the replication (‘synchronization’) piece.
I have a Capella app with an endpoint named lf and a bucket named lf, that has a scope named data with a collection in it called tasks.
These screens are probably more descriptive:
Failing Replicator Setup
Finally, I have an “AuthenticateAsync” method (see below) in my “UserRepostory” class where I am trying (and failing) to get the Capella replicator working set up so that my local db will sync to the remote Capella hosted instance. The code in AuthenticateAsync fails at await completionSource.Task.WaitAsync(_options.ConnectionTimeout);
with “{“Authentication error: CouchbaseLiteException (WebSocketDomain / 404): Collection ‘_default’ is not found on the remote server.”}”
But I’m not trying to connect to _default collection I’d appreciate any guidance, including examples of configuring Replicator for .net when the database is presumed local and “offline first”. Am I breaking naming convetions?
You can see all the logging as I tried to work my through other errors. The code makes me think I should be able to “see” tasks collection, but something about my setup or code is making it expect or route to “_default” collection. (?)
Migrating (quickly) from Mong$#. Glad to be on CBL and Capella, but very new.
Thanks,
Dave G
public async Task<User> AuthenticateAsync(string username, string password)
{
if (string.IsNullOrEmpty(username))
throw new ArgumentException("Username cannot be null or empty", nameof(username));
if (string.IsNullOrEmpty(password))
throw new ArgumentException("Password cannot be null or empty", nameof(password));
_logger.LogInformation($"Starting authentication for user: {username}");
try
{
if (_database == null)
{
throw new InvalidOperationException("Database not initialized");
}
_logger.LogInformation("Accessing collection 'tasks' in scope 'data'...");
var collection = _database.GetCollection("tasks", "data");
if (collection == null)
{
_logger.LogError("Could not access collection 'tasks' in scope 'data'");
throw new InvalidOperationException("Required collection not found");
}
_logger.LogInformation($"Got collection: Name={collection.Name}, Scope={collection.Scope.Name}");
var authenticator = new BasicAuthenticator(username, password);
var targetEndpoint = new URLEndpoint(new Uri(_options.EndpointUrl));
_logger.LogInformation("Creating replicator configuration");
// Create specific collection-to-collection mapping
var collectionConfig = new CollectionConfiguration();
var collections = new Dictionary<string, CollectionConfiguration>
{
{ "data.tasks", collectionConfig } // Explicitly map to data.tasks
};
var config = new ReplicatorConfiguration(_database, targetEndpoint)
{
Authenticator = authenticator,
ReplicatorType = ReplicatorType.PushAndPull,
Continuous = false
};
// Add our specific collection
config.AddCollection(collection, collectionConfig);
_logger.LogInformation("Created replicator configuration with explicit collection mapping");
using var testReplicator = new Replicator(config);
var completionSource = new TaskCompletionSource<bool>();
testReplicator.AddChangeListener((sender, args) =>
{
if (args.Status.Error != null)
{
var errorMsg = $"Authentication error: {args.Status.Error.Message}";
_logger.LogError(errorMsg);
_logger.LogError($"Detailed status: Activity={args.Status.Activity}, Progress={args.Status.Progress}");
completionSource.TrySetException(new UnauthorizedAccessException(errorMsg));
}
else if (args.Status.Activity == ReplicatorActivityLevel.Stopped)
{
_logger.LogInformation("Authentication successful");
completionSource.TrySetResult(true);
}
else
{
_logger.LogInformation($"Replicator status: Activity={args.Status.Activity}, Progress={args.Status.Progress}");
}
});
_logger.LogInformation("Starting replicator");
testReplicator.Start();
await completionSource.Task.WaitAsync(_options.ConnectionTimeout);
_currentUser = new User(username, password);
return _currentUser;
}
catch (Exception ex) when (ex is not UnauthorizedAccessException)
{
_logger.LogError(ex, "Authentication failed");
throw new InvalidOperationException("Authentication service unavailable", ex);
}
}