using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using InABox.Clients; using InABox.Core; namespace InABox.Rpc { public class RpcClient : BaseClient where TEntity : Entity, new() { private IRpcClientTransport _transport; private ConcurrentDictionary _events = new ConcurrentDictionary(); private ConcurrentDictionary _responses = new ConcurrentDictionary(); private const int DefaultRequestTimeout = 5 * 60 * 1000; // 5 minutes public RpcClient(IRpcClientTransport transport) { _transport = transport; _transport.OnMessage += Transport_Message; } ~RpcClient() { _transport.OnMessage -= Transport_Message; } #region TransportManagement public override bool IsConnected() => _transport?.IsConnected() == true; private void Transport_Message(IRpcTransport transport, RpcTransportMessageArgs e) { if (e.Message != null && e.Message.Command == "Push") { var push = Serialization.ReadBinary(e.Message.Payload, BinarySerializationSettings.Latest); ClientFactory.PushHandlers.Push(push.Type, push.Object); } } #endregion #region Client Interface public override DatabaseInfo Info() { var result = _transport.Send(new RpcInfoParameters()); return result.Info; } private static string[]? _types; public override IEnumerable SupportedTypes() { _types ??= CoreUtils.Entities .Where(x => x.GetInterfaces().Contains(typeof(IPersistent))) .Select(x => x.EntityName().Replace(".", "_")) .ToArray(); return _types; } #region Validate & 2FA protected override IValidationData DoValidate(string userid, string password, Guid session = default) { var parameters = new RpcValidateParameters() { UserID = userid, Password = password, PIN = "", UsePIN = false, SessionID = session, Platform = ClientFactory.Platform, Version = ClientFactory.Version }; return _transport.Send(parameters); } protected override IValidationData DoValidate(string pin, Guid session = default) { var ticks = DateTime.Now.ToUniversalTime().Ticks.ToString(); var parameters = new RpcValidateParameters() { UserID = Encryption.Encrypt(ticks, "wCq9rryEJEuHIifYrxRjxg", true), Password = Encryption.Encrypt(ticks, "7mhvLnqMwkCAzN+zNGlyyg", true), PIN = pin, UsePIN = true, SessionID = session, Platform = ClientFactory.Platform, Version = ClientFactory.Version }; return _transport.Send(parameters); } protected override IValidationData DoValidate(Guid session = default) { var parameters = new RpcValidateParameters() { UserID = "", Password = "", PIN = "", UsePIN = false, SessionID = session, Platform = ClientFactory.Platform, Version = ClientFactory.Version }; return _transport.Send(parameters); } protected override bool DoCheck2FA(string code, Guid? session) { var parameters = new RpcCheck2FAParameters() { Code = code, SessionId = session ?? ClientFactory.SessionID, }; var result = _transport.Send(parameters); return result.Valid; } #endregion protected override CoreTable DoQuery(Filter? filter, Columns? columns, SortOrder? sort = null, CoreRange? range = null) { var parameters = new RpcQueryParameters() { Queries = new RpcQueryDefinition[] { new RpcQueryDefinition() { Key = typeof(TEntity).EntityName().Split('.').Last(), Type = typeof(TEntity), Filter = filter, Columns = columns, Sort = sort, Range = range } } }; var result = _transport.Send(parameters); return result.Tables[0].Table; } protected override TEntity[] DoLoad(Filter? filter = null, SortOrder? sort = null, CoreRange? range = null) { return DoQuery(filter, null, sort, range)?.ToArray() ?? new TEntity[] { }; } protected override Dictionary DoQueryMultiple(Dictionary queries) { var result = new Dictionary(); var parameters = new RpcQueryParameters() { Queries = queries.Select(kvp => new RpcQueryDefinition() { Key = kvp.Key, Type = kvp.Value.Type, Filter = kvp.Value.Filter, Columns = kvp.Value.Columns, Sort = kvp.Value.SortOrder, Range = kvp.Value.Range } ).ToArray() }; var response = _transport.Send(parameters); foreach (var key in response.Tables) result[key.Key] = key.Table; return result; } protected override void DoSave(TEntity entity, string auditnote) { DoSave(new TEntity[] { entity }, auditnote); } protected override void DoSave(IEnumerable entities, string auditnote) { var items = entities.AsArray(); var parameters = new RpcSaveParameters() { Type = typeof(TEntity), Items = items, AuditNote = auditnote }; var result = _transport.Send(parameters); for (int i = 0; i < result.Deltas.Length; i++) { items[i].SetObserving(false); foreach (var (key, value) in result.Deltas[i]) { if (CoreUtils.TryGetProperty(key, out var property)) CoreUtils.SetPropertyValue(items[i], key, CoreUtils.ChangeType(value, property.PropertyType)); } items[i].CommitChanges(); items[i].SetObserving(true); } } protected override void DoDelete(TEntity entity, string auditnote) { DoDelete(new TEntity[] { entity }, auditnote); } protected override void DoDelete(IEnumerable entities, string auditnote) { var parameters = new RpcDeleteParameters() { Type = typeof(TEntity), IDs = entities.Select(x => x.ID).ToArray(), AuditNote = auditnote }; _transport.Send(parameters); } protected override bool DoPing() { try { if (!_transport.IsConnected()) _transport.Connect(); if (_transport.IsConnected()) { _transport.Send(new RpcPingParameters()); return true; } return false; } catch (Exception) { return false; } } public override string Version() { return _transport.Send(new RpcVersionParameters()).Version; } public override byte[]? Installer() { return _transport.Send(new RpcInstallerParameters()).Installer; } public override string ReleaseNotes() { return _transport.Send(new RpcReleaseNotesParameters()).ReleaseNotes; } #endregion } }