/// <inheritdoc/> public override async Task Handle(UpdateDailyOutflow command) { var root = await _repository.Find <Wallet>(command.Target); if (root == null) { throw new ArgumentNullException(nameof(command.Address)); } var coin = await _repository.Find <Coin>(root.Coin); if (coin == null) { throw new ArgumentNullException(nameof(root.Coin)); } var txResults = await GetTransactions(command); if (txResults == null) { return; } var outflows = new Dictionary <Instant, (Instant time, double amount)>(); foreach (var tx in txResults) { var dateTime = Instant.FromUnixTimeMilliseconds(tx.ReceiveTime).InUtc().LocalDateTime; var date = new LocalDate(dateTime.Year, dateTime.Month, dateTime.Day); var instant = date.AtMidnight().InUtc().ToInstant(); var amount = tx.Amount; if (tx.To == command.Target) { amount *= -1; } if (outflows.ContainsKey(instant)) { amount += outflows[instant].amount; } outflows[instant] = (Instant.FromUnixTimeMilliseconds(tx.ReceiveTime), amount); } ICommand changeBalanceCommand = null; var idx = 0; foreach (var v in outflows) { changeBalanceCommand = new RetroactiveCommand <ChangeWalletBalance>(new ChangeWalletBalance(command.Address, new Quantity(-v.Value.amount, coin.Asset), $"Out{command.Index}_{idx}"), v.Value.time); await _balanceHandler.Handle(changeBalanceCommand); ++idx; } command.EventType = changeBalanceCommand?.EventType; }
/// <inheritdoc/> public override async Task Handle(UpdateDailyMining command) { var root = await _repository.Find <Wallet>(command.Target); if (root == null) { throw new ArgumentNullException(nameof(command.Address)); } var coin = await _repository.Find <Coin>(root.Coin); if (coin == null) { throw new ArgumentNullException(nameof(root.Coin)); } var minedBlocks = await GetMinedBlocks(command); if (minedBlocks == null) { return; } var blocks = new Dictionary <Instant, (Instant time, double amount)>(); foreach (var block in minedBlocks) { var dateTime = Instant.FromUnixTimeMilliseconds(block.Timestamp).InUtc().LocalDateTime; var date = new LocalDate(dateTime.Year, dateTime.Month, dateTime.Day); var instant = date.AtMidnight().InUtc().ToInstant(); var amount = block.Feereward; if (blocks.ContainsKey(instant)) { amount += blocks[instant].amount; } blocks[instant] = (Instant.FromUnixTimeMilliseconds(block.Timestamp), amount); } ICommand mineCommand = null; var idx = 0; foreach (var v in blocks) { mineCommand = new RetroactiveCommand <MineCoin>(new MineCoin(command.Address, new Quantity(v.Value.amount, coin.Asset), $"Mining{command.Index}_{idx}"), v.Value.time); await _mineHandler.Handle(mineCommand); ++idx; } command.EventType = mineCommand?.EventType; }
/// <inheritdoc/> public override async Task Handle(UpdateQuote command) { var root = await _repository.Find <AssetPair>(command.Target); if (root == null) { throw new ArgumentNullException(nameof(AssetPair)); } if (root.QuoteDates.Any(d => d.InUtc().Year == command.Timestamp.InUtc().Year&& d.InUtc().Month == command.Timestamp.InUtc().Month&& d.InUtc().Day == command.Timestamp.InUtc().Day)) { throw new InvalidOperationException( $"Quote already added for {command.Timestamp.InUtc().ToString("yyyy-MM-dd", new DateTimeFormatInfo())}"); } ICommandHandler handler = null; ICommand commandT = null; if (root.ForAsset.AssetType == Asset.Type.Currency && root.DomAsset.AssetType == Asset.Type.Currency) { commandT = new UpdateQuote <Api.Fx.JsonResult>(command.Target) { MessageId = command.MessageId }; handler = _handlers.SingleOrDefault(h => h.CanHandle(commandT)); } else if (root.ForAsset.AssetType == Asset.Type.Coin && root.DomAsset.AssetType == Asset.Type.Currency) { commandT = new UpdateQuote <Api.Coin.JsonResult>(command.Target) { MessageId = command.MessageId }; handler = _handlers.SingleOrDefault(h => h.CanHandle(commandT)); } if (handler == null) { throw new InvalidOperationException($"Automatic quote retrieval for {root.ForAsset.Ticker}{root.DomAsset.Ticker} not supported"); } await handler.Handle(commandT); command.EventType = commandT.EventType; }
/// <inheritdoc /> public virtual async Task Handle(TCommand command) { if (command == null) { throw new ArgumentNullException(typeof(TCommand).GetFriendlyName()); } var root = await _repository.Find <TRoot>(command.Target, ComputeHash); if (root == null) { throw new ArgumentNullException(typeof(TRoot).GetFriendlyName()); } if (command.Timestamp < root.Timestamp) { throw new InvalidOperationException( $"{typeof(TCommand).Name} command ({command.Timestamp}) updating the past of the aggregate {typeof(TRoot).Name}:{command.Target} ({root.Timestamp}) "); } Act(root, command); if (command.UseTimestamp) { root.TimestampEvents(command.Timestamp); } var events = root.GetUncommittedEvents().OfType <Event>().ToList(); var eventType = events.Select(e => e.MessageType).SingleOrDefault(); if (eventType == null) { throw new InvalidOperationException("More than one event type produced by a command"); } foreach (var e in events) { e.CommandId = command.MessageId; e.AncestorId = command.MessageId; e.CorrelationId = command.CorrelationId; } command.EventType = eventType; await _repository.Save(root); }
private async Task Handle(IEvent e) { // _log.Trace($"{typeof(TSaga).Name}.When({e.EventType}[{e.Stream}])"); _log.Trace($"{e.MessageType}", typeof(TSaga).GetFriendlyName()); try { var emptySaga = new TSaga(); var id = emptySaga.SagaId(e); var isInitializer = emptySaga.IsInitializer(e); TSaga saga; if (isInitializer) { saga = await _repository.GetOrAdd <TSaga>(id); } else { saga = await _repository.Find <TSaga>(id); } if (saga == null) { return; } var saveEvent = ((Event)e).Copy(); saga.When(saveEvent); var commands = saga.GetUncommittedCommands().OfType <Command>(); foreach (var c in commands) { c.AncestorId = e.MessageId; c.CorrelationId = id; } await _repository.Save(saga); } catch (Exception exception) { _log.Errors.Add(exception); } }
/// <summary> /// Repeated repository access until the aggregate root is found or timeout is reached /// </summary> /// <param name="repository">Aggregate root repository</param> /// <param name="id">Aggregate root id</param> /// <param name="predicate">Stop condition</param> /// <param name="timeout">Execution timeout</param> /// <param name="delay">Delay between repeating the bus calls</param> /// <typeparam name="T">Aggregate root type</typeparam> /// <returns>Task representing the asynchronous repeated find</returns> public static async Task <T> FindUntil <T>(this IEsRepository <IAggregate> repository, string id, Func <T, bool> predicate = null, TimeSpan timeout = default(TimeSpan), TimeSpan delay = default(TimeSpan)) where T : class, IAggregate, new() { return(await RetryUntil(async() => await repository.Find <T>(id))); }