namespace AIStudio.Tools.Services;
///
/// A watcher here is whatever an indexer uses to notice that one of its data sources changed, a file
/// system watcher for local files, an interval for mailboxes. The indexers own them; this service
/// only decides when they run.
///
public sealed partial class DataSourceEmbeddingService
{
///
/// Lets every indexer track changes to its data sources, or stops all tracking.
///
///
/// Tracking runs only while local data sources refresh on their own, and not before the startup
/// hash check is done.
///
private void RefreshWatchers()
{
if (!settingsManager.ConfigurationData.App.DataSourceIndexing.AutomaticRefresh)
{
this.RemoveAllWatchers();
return;
}
if (Volatile.Read(ref this.startupHashCheckCompleted) == 0)
{
logger.LogDebug("File watchers are not activated yet because the startup persisted hash check has not completed.");
this.RemoveAllWatchers();
return;
}
var supportedSources = this.GetConfiguredIndexedSources();
foreach (var indexer in this.indexers)
indexer.TrackChanges(supportedSources.Where(indexer.Supports).ToList(), this.RequestRunAsync);
}
private void RemoveWatcher(string dataSourceId)
{
foreach (var indexer in this.indexers)
indexer.StopTracking(dataSourceId);
}
private void RemoveAllWatchers()
{
foreach (var indexer in this.indexers)
indexer.StopTrackingAll();
}
private void DisposeWatchers()
{
foreach (var indexer in this.indexers)
indexer.Dispose();
}
///
/// Queues the run an indexer asked for after it noticed a change, or because its interval came round.
///
/// The id of the data source which changed.
/// Why the indexer asks.
private async Task RequestRunAsync(string dataSourceId, DataSourceEmbeddingRefreshMode refreshMode)
{
if (!this.TryGetConfiguredIndexedSource(dataSourceId, out var dataSource))
return;
if (this.statuses.TryGetValue(dataSourceId, out var status) && IsWaitingForSignIn(status))
{
logger.LogDebug("Skipped the requested run of data source '{DataSourceId}' because its sign-in failed and waits for the user. RefreshMode={RefreshMode}.", dataSourceId, refreshMode);
return;
}
await this.QueueDataSourceAsync(dataSource, true, refreshMode);
}
///
/// Whether a data source waits for the user since its sign-in failed.
///
///
/// Then only the user starts its next run, by saving a new password or with a retry. The indexer
/// would not sign in on any other run either, but every one of them would make the row of the
/// data source change from failed to queued and back, every interval anew.
///
/// The current status of the data source.
internal static bool IsWaitingForSignIn(DataSourceEmbeddingStatus status) => status.Attention is DataSourceAttention.AUTH_FAILED;
}