blob: 92361bd7dfe3e6a350aa62811f364d1145c5e44a [file]
using System;
using System.Threading.Tasks;
using Casbin.Persist;
using Casbin.Watcher.Redis.Entities;
using StackExchange.Redis;
namespace Casbin.Watcher.Redis;
public class RedisWatcher : IWatcher
{
private readonly IConnectionMultiplexer _connection;
private readonly RedisChannel _channel;
private readonly IWatcherOptions _options;
private ISubscriber _subscriber;
private Action _callback;
private Func<Task> _asyncCallback;
private Action<IPolicyChangeMessage> _callbackWithMessage;
private Func<IPolicyChangeMessage, Task> _asyncCallbackWithMessage;
public RedisWatcher(string addr = "localhost", IWatcherOptions options = null)
{
_options = options ?? new WatcherOptions();
Id = _options.LocalId ?? Guid.NewGuid().ToString();
_channel = new RedisChannel(_options.Channel ?? "/casbin", RedisChannel.PatternMode.Literal);
_connection = ConnectionMultiplexer.Connect(addr);
if (_options.Async)
SubscribeAsync();
else
Subscribe();
}
~RedisWatcher() => Close();
public string Id { get; private set; }
#region IFullWatcher
public virtual void SetUpdateCallback(Action callback)
{
_callback = callback;
}
public virtual void SetUpdateCallback(Func<Task> callback)
{
_asyncCallback = callback;
}
public virtual void Update()
{
var message = new Message
{
Operation = PolicyOperation.SavePolicy,
Id = Id,
};
_subscriber.Publish(_channel, message.ToRedisValue());
}
public virtual async Task UpdateAsync()
{
var message = new Message
{
Operation = PolicyOperation.SavePolicy,
Id = Id,
};
await _subscriber.PublishAsync(_channel, message.ToRedisValue());
}
#endregion
#region IIncrementalWatcher
public virtual void SetUpdateCallback(Action<IPolicyChangeMessage> callback)
{
_callbackWithMessage = callback;
}
public virtual void SetUpdateCallback(Func<IPolicyChangeMessage, Task> callback)
{
_asyncCallbackWithMessage = callback;
}
public virtual void Update(IPolicyChangeMessage policyMessage)
{
var message = new Message
{
Id = Id,
Operation = policyMessage.Operation,
Section = policyMessage.Section,
PolicyType = policyMessage.PolicyType,
FieldIndex = policyMessage.FieldIndex,
Values = policyMessage.Values,
NewValues = policyMessage.NewValues,
ValuesList = policyMessage.ValuesList,
NewValuesList = policyMessage.NewValuesList
};
_subscriber.Publish(_channel, message.ToRedisValue());
}
public virtual async Task UpdateAsync(IPolicyChangeMessage policyMessage)
{
var message = new Message
{
Id = Id,
Operation = policyMessage.Operation,
Section = policyMessage.Section,
PolicyType = policyMessage.PolicyType,
FieldIndex = policyMessage.FieldIndex,
Values = policyMessage.Values,
NewValues = policyMessage.NewValues,
ValuesList = policyMessage.ValuesList,
NewValuesList = policyMessage.NewValuesList
};
await _subscriber.PublishAsync(_channel, message.ToRedisValue());
}
#endregion
#region IReadOnlyWatcher
public virtual void Close()
{
_subscriber?.UnsubscribeAll();
_connection?.Close();
}
public virtual async Task CloseAsync()
{
await _subscriber?.UnsubscribeAllAsync();
await _connection?.CloseAsync();
}
#endregion
private void Subscribe()
{
_subscriber = _connection.GetSubscriber();
_subscriber.Subscribe(_channel, (RedisChannel _, RedisValue value) =>
{
var message = value.ToMessage();
var isSelf = message.Id == Id;
if (!(isSelf && _options.IgnoreSelf))
{
try
{
if (_callbackWithMessage != null)
{
_callbackWithMessage.Invoke(message);
}
else
{
_callback?.Invoke();
}
}
catch {}
}
});
}
private void SubscribeAsync()
{
_subscriber = _connection.GetSubscriber();
_subscriber.Subscribe(_channel, (RedisChannel _, RedisValue value) =>
{
var message = value.ToMessage();
var isSelf = message.Id == Id;
if (!(isSelf && _options.IgnoreSelf))
{
try
{
if (_asyncCallbackWithMessage != null)
{
_asyncCallbackWithMessage.Invoke(message);
}
else
{
_asyncCallback?.Invoke();
}
}
catch {}
}
});
}
}