From 367b13e17530367aac5e00de0badeac6e4c03c14 Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Tue, 26 Mar 2024 15:18:12 +0300 Subject: [PATCH] Update sync contracts --- .../TextMatches/Dto/TextMatchData.cs | 4 +- .../TextMatches/ITextMatchesController.cs | 12 ++++ .../Requests/CreateTextMatchRequest.cs | 2 - .../WebRoutes.cs | 6 ++ .../TextMatchers/MessageReceivedHandler.cs | 18 +++-- .../Repositories/ITextMatchRepository.cs | 4 ++ .../InMemoryTextMatchRepository.cs | 11 ++- .../TextMatches/Services/ITextMatchService.cs | 14 ++-- .../TextMatches/Services/TextMatchService.cs | 71 +++++++++++++++---- .../TextMatches/TextMatch.cs | 43 +++++++---- .../TextMatchMatched.cs | 8 +-- .../Controllers/TextMatchController.cs | 31 +++++++- 12 files changed, 170 insertions(+), 54 deletions(-) diff --git a/src/Nocr.TextMatcher.Api.Contracts/TextMatches/Dto/TextMatchData.cs b/src/Nocr.TextMatcher.Api.Contracts/TextMatches/Dto/TextMatchData.cs index b6ca302..e273d7b 100644 --- a/src/Nocr.TextMatcher.Api.Contracts/TextMatches/Dto/TextMatchData.cs +++ b/src/Nocr.TextMatcher.Api.Contracts/TextMatches/Dto/TextMatchData.cs @@ -6,12 +6,12 @@ public sealed class TextMatchData public long UserId { get; set; } - public long? ChatId { get; set; } - public string ChatUsername { get; set; } public string Template { get; set; } + public bool Active { get; set; } + public TextMatchRule Rule { get; set; } public DateTimeOffset CreatedDateTime { get; set; } diff --git a/src/Nocr.TextMatcher.Api.Contracts/TextMatches/ITextMatchesController.cs b/src/Nocr.TextMatcher.Api.Contracts/TextMatches/ITextMatchesController.cs index 4ad4e22..1e83ed2 100644 --- a/src/Nocr.TextMatcher.Api.Contracts/TextMatches/ITextMatchesController.cs +++ b/src/Nocr.TextMatcher.Api.Contracts/TextMatches/ITextMatchesController.cs @@ -10,6 +10,18 @@ public interface ITextMatchesController [Post] Task Create([Body] CreateTextMatchRequest request, CancellationToken cancellationToken = default); + [Get(WebRoutes.TextMatches.ById)] + Task GetById([Path] long id, CancellationToken cancellationToken = default); + [Get(WebRoutes.TextMatches.ByUserId)] Task GetByUserId([Path] long userId, CancellationToken cancellationToken = default); + + [Delete(WebRoutes.TextMatches.ById)] + public Task Delete([Path] long id, CancellationToken cancellationToken = default); + + [Patch(WebRoutes.TextMatches.Activate)] + Task Activate([Path] long id, CancellationToken cancellationToken = default); + + [Patch(WebRoutes.TextMatches.Disable)] + Task Disable([Path] long id, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/Nocr.TextMatcher.Api.Contracts/TextMatches/Requests/CreateTextMatchRequest.cs b/src/Nocr.TextMatcher.Api.Contracts/TextMatches/Requests/CreateTextMatchRequest.cs index 3ea7758..cc82677 100644 --- a/src/Nocr.TextMatcher.Api.Contracts/TextMatches/Requests/CreateTextMatchRequest.cs +++ b/src/Nocr.TextMatcher.Api.Contracts/TextMatches/Requests/CreateTextMatchRequest.cs @@ -4,8 +4,6 @@ public class CreateTextMatchRequest { public long UserId { get; set; } - public long? ChatId { get; set; } - public string ChatUsername { get; set; } public string Template { get; set; } diff --git a/src/Nocr.TextMatcher.Api.Contracts/WebRoutes.cs b/src/Nocr.TextMatcher.Api.Contracts/WebRoutes.cs index a6d9e88..1294b0b 100644 --- a/src/Nocr.TextMatcher.Api.Contracts/WebRoutes.cs +++ b/src/Nocr.TextMatcher.Api.Contracts/WebRoutes.cs @@ -9,5 +9,11 @@ public static class WebRoutes public const string Path = BasePath + "/" + "text-matches"; public const string ByUserId = "by-user-id/{userId}"; + + public const string Activate = ById + "/" + "activate"; + + public const string Disable = ById + "/" + "disable"; + + public const string ById = "{id}"; } } \ No newline at end of file diff --git a/src/Nocr.TextMatcher.AppServices/TextMatchers/MessageReceivedHandler.cs b/src/Nocr.TextMatcher.AppServices/TextMatchers/MessageReceivedHandler.cs index e3f634b..3e8c7a0 100644 --- a/src/Nocr.TextMatcher.AppServices/TextMatchers/MessageReceivedHandler.cs +++ b/src/Nocr.TextMatcher.AppServices/TextMatchers/MessageReceivedHandler.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.Logging; using Nocr.TelegramListener.Async.Api.Contracts.Events; -using Nocr.TextMatcher.AppServices.TextMatches.Services; +using Nocr.TextMatcher.AppServices.TextMatches.Repositories; using Nocr.TextMatcher.Async.Api.Contracts; using Nocr.TextMatcher.Core.Dates; using Rebus.Bus; @@ -12,17 +12,17 @@ public sealed class MessageReceivedHandler : IHandleMessages { private readonly ILogger _logger; private readonly IBus _bus; - private readonly ITextMatchService _textMatchService; + private readonly ITextMatchRepository _textMatchService; private readonly ICurrentDateProvider _dateProvider; public MessageReceivedHandler(ILogger logger, IBus bus, - ITextMatchService textMatchService, + ITextMatchRepository textMatchRepository, ICurrentDateProvider dateProvider) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _bus = bus ?? throw new ArgumentNullException(nameof(bus)); - _textMatchService = textMatchService ?? throw new ArgumentNullException(nameof(textMatchService)); + _textMatchService = textMatchRepository ?? throw new ArgumentNullException(nameof(textMatchRepository)); _dateProvider = dateProvider ?? throw new ArgumentNullException(nameof(dateProvider)); } @@ -32,17 +32,15 @@ public sealed class MessageReceivedHandler : IHandleMessages var matches = await _textMatchService.Get(); - foreach (var match in matches) + foreach (var match in matches.Where(x => x.Active)) { - if (match.IsMatches(message.ChatId, message.ChatUsername, message.Text)) + if (match.IsMatches(message.ChatUsername, message.Text)) { _logger.LogInformation("Message {@Message} matched {@Match}", message, match); var @event = new TextMatchMatched { - ChatId = message.ChatId, - ChatUsername = message.ChatUsername, - Text = message.Text, - UserId = message.From, + MatchId = match.Id, + MatchUserId = match.UserId, OccuredDateTime = message.OccuredDateTime, PublishedDateTime = _dateProvider.UtcNow }; diff --git a/src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/ITextMatchRepository.cs b/src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/ITextMatchRepository.cs index 227262a..d0f661b 100644 --- a/src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/ITextMatchRepository.cs +++ b/src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/ITextMatchRepository.cs @@ -9,4 +9,8 @@ public interface ITextMatchRepository Task> Get(CancellationToken cancellationToken = default); Task> GetByUserId(long userId, CancellationToken cancellationToken = default); + + Task GetById(long id, CancellationToken cancellationToken = default); + + Task Update(TextMatch textMatch, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/InMemoryTextMatchRepository.cs b/src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/InMemoryTextMatchRepository.cs index a87a9fc..d022b61 100644 --- a/src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/InMemoryTextMatchRepository.cs +++ b/src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/InMemoryTextMatchRepository.cs @@ -1,5 +1,4 @@ using Nocr.TextMatcher.Api.Contracts.TextMatches; -using Nocr.TextMatcher.Api.Contracts.TextMatches.Dto; namespace Nocr.TextMatcher.AppServices.TextMatches.Repositories; @@ -49,4 +48,14 @@ public sealed class InMemoryTextMatchRepository : ITextMatchRepository { return Task.FromResult>(_textMatches.Where(x => x.UserId == userId).ToArray()); } + + public Task GetById(long id, CancellationToken cancellationToken) + { + return Task.FromResult(_textMatches.FirstOrDefault(x => x.Id == id)); + } + + public Task Update(TextMatch textMatch, CancellationToken cancellationToken = default) + { + return Task.CompletedTask; + } } \ No newline at end of file diff --git a/src/Nocr.TextMatcher.AppServices/TextMatches/Services/ITextMatchService.cs b/src/Nocr.TextMatcher.AppServices/TextMatches/Services/ITextMatchService.cs index 1e7c450..d286a4d 100644 --- a/src/Nocr.TextMatcher.AppServices/TextMatches/Services/ITextMatchService.cs +++ b/src/Nocr.TextMatcher.AppServices/TextMatches/Services/ITextMatchService.cs @@ -5,12 +5,18 @@ namespace Nocr.TextMatcher.AppServices.TextMatches.Services; public interface ITextMatchService { - Task Create(long userId, string chatUsername, string template, TextMatchRule rule, long? chatId = null, + Task Create(long userId, string chatUsername, string template, TextMatchRule rule, CancellationToken cancellationToken = default); + Task GetById(long id, CancellationToken cancellationToken = default); + Task Delete(long id, CancellationToken cancellationToken = default); - Task> Get(CancellationToken cancellationToken = default); - - Task> GetByUserId(long userId, CancellationToken cancellationToken); + Task Activate(long id, CancellationToken cancellationToken = default); + + Task Disable(long id, CancellationToken cancellationToken = default); + + Task> Get(CancellationToken cancellationToken = default); + + Task> GetByUserId(long userId, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/Nocr.TextMatcher.AppServices/TextMatches/Services/TextMatchService.cs b/src/Nocr.TextMatcher.AppServices/TextMatches/Services/TextMatchService.cs index b94dec1..cb2b137 100644 --- a/src/Nocr.TextMatcher.AppServices/TextMatches/Services/TextMatchService.cs +++ b/src/Nocr.TextMatcher.AppServices/TextMatches/Services/TextMatchService.cs @@ -21,10 +21,10 @@ public sealed class TextMatchService : ITextMatchService _dateProvider = dateProvider ?? throw new ArgumentNullException(nameof(dateProvider)); } - public async Task Create(long userId, string chatUsername, string template, TextMatchRule rule, long? chatId = null, + public async Task Create(long userId, string chatUsername, string template, TextMatchRule rule, CancellationToken cancellationToken = default) { - var textMatch = TextMatch.Initialize(userId, chatUsername, template, rule, _dateProvider.UtcNow, chatId); + var textMatch = TextMatch.Initialize(userId, chatUsername, template, rule, _dateProvider.UtcNow); await _repository.Create(textMatch, cancellationToken); var @event = new TextMatchCreated @@ -38,28 +38,71 @@ public sealed class TextMatchService : ITextMatchService return textMatch.Id; } + public async Task GetById(long id, CancellationToken cancellationToken) + { + var textMatch = await _repository.GetById(id, cancellationToken); + if (textMatch == null) + return null; + + return MapToTextMatchData(textMatch); + } + public Task Delete(long id, CancellationToken cancellationToken = default) { return _repository.Delete(id, cancellationToken); } - public Task> Get(CancellationToken cancellationToken = default) + public async Task Activate(long id, CancellationToken cancellationToken = default) { - return _repository.Get(cancellationToken); + var textMatch = await _repository.GetById(id, cancellationToken); + if (textMatch == null) + { + throw new InvalidOperationException($"Match with id {id} not found"); + } + + textMatch.Activate(); + + await _repository.Update(textMatch, cancellationToken); } - public async Task> GetByUserId(long userId, CancellationToken cancellationToken = default) + public async Task Disable(long id, CancellationToken cancellationToken = default) + { + var textMatch = await _repository.GetById(id, cancellationToken); + if (textMatch == null) + { + throw new InvalidOperationException($"Match with id {id} not found"); + } + + textMatch.Disable(); + + await _repository.Update(textMatch, cancellationToken); + } + + public async Task> Get(CancellationToken cancellationToken = default) + { + var matches = await _repository.Get(cancellationToken); + + return matches.Select(MapToTextMatchData).ToArray(); + } + + public async Task> GetByUserId(long userId, + CancellationToken cancellationToken = default) { var matches = await _repository.GetByUserId(userId, cancellationToken); - return matches.Select(x => new TextMatchData + return matches.Select(MapToTextMatchData).ToArray(); + } + + private TextMatchData MapToTextMatchData(TextMatch textMatch) + { + return new TextMatchData { - Id = x.Id, - ChatId = x.ChatId, - ChatUsername = x.ChatUsername, - Template = x.Template, - UserId = x.UserId, - Rule = x.Rule, - CreatedDateTime = x.CreatedDateTime - }).ToArray(); + Id = textMatch.Id, + ChatUsername = textMatch.ChatUsername, + Active = textMatch.Active, + Template = textMatch.Template, + UserId = textMatch.UserId, + Rule = textMatch.Rule, + CreatedDateTime = textMatch.CreatedDateTime + }; } } \ No newline at end of file diff --git a/src/Nocr.TextMatcher.AppServices/TextMatches/TextMatch.cs b/src/Nocr.TextMatcher.AppServices/TextMatches/TextMatch.cs index fc2c735..82f9639 100644 --- a/src/Nocr.TextMatcher.AppServices/TextMatches/TextMatch.cs +++ b/src/Nocr.TextMatcher.AppServices/TextMatches/TextMatch.cs @@ -9,14 +9,14 @@ public sealed class TextMatch public long UserId { get; private set; } - public long? ChatId { get; private set; } - public string ChatUsername { get; private set; } public string Template { get; private set; } public TextMatchRule Rule { get; private set; } + public bool Active { get; private set; } + public DateTimeOffset CreatedDateTime { get; private set; } private TextMatch(long userId, @@ -24,41 +24,60 @@ public sealed class TextMatch string template, TextMatchRule rule, DateTimeOffset createdDateTime, - long? chatId) + bool active) { UserId = userId; ChatUsername = chatUsername; Template = template; Rule = rule; CreatedDateTime = createdDateTime; - ChatId = chatId; + Active = active; } public static TextMatch Initialize(long userId, string chatUsername, string template, TextMatchRule rule, - DateTimeOffset createdDateTime, - long? chatId = null) + DateTimeOffset createdDateTime) { if (userId <= 0) throw new ArgumentException("User id should be greater tha 0", nameof(userId)); - if (chatId is <= 0) - throw new ArgumentException("Chat id should be greater tha 0", nameof(chatId)); - if (string.IsNullOrWhiteSpace(template)) throw new ArgumentException("Template should not be empty", nameof(template)); if (string.IsNullOrWhiteSpace(chatUsername)) throw new ArgumentException("Chat username should not be empty", nameof(chatUsername)); - return new TextMatch(userId, chatUsername, template, rule, createdDateTime,chatId); + if (chatUsername.StartsWith("@")) + throw new ArgumentException("Chat username should be without @", nameof(chatUsername)); + + return new TextMatch(userId, chatUsername, template, rule, createdDateTime, true); } - public bool IsMatches(long chatId, string chatUsername, string text) + public void Disable() { - if (ChatId.HasValue && ChatId != chatId && !string.Equals(ChatUsername, chatUsername, StringComparison.OrdinalIgnoreCase)) + if (Active == false) + { + throw new InvalidOperationException("Failed to disable inactive match"); + } + + Active = false; + } + + public void Activate() + { + if (Active == true) + { + throw new InvalidOperationException("Failed to activate inactive match"); + } + + Active = true; + } + + public bool IsMatches(string chatUsername, string text) + { + if (!string.Equals(ChatUsername, chatUsername, StringComparison.OrdinalIgnoreCase)) return false; switch (Rule) diff --git a/src/Nocr.TextMatcher.Async.Api.Contracts/TextMatchMatched.cs b/src/Nocr.TextMatcher.Async.Api.Contracts/TextMatchMatched.cs index 949f400..24eba16 100644 --- a/src/Nocr.TextMatcher.Async.Api.Contracts/TextMatchMatched.cs +++ b/src/Nocr.TextMatcher.Async.Api.Contracts/TextMatchMatched.cs @@ -4,13 +4,9 @@ public class TextMatchMatched : IEvent { public Guid Id => Guid.NewGuid(); - public long UserId { get; set; } + public long MatchId { get; set; } - public long? ChatId { get; set; } - - public string ChatUsername { get; set; } = null!; - - public string Text { get; set; } + public long MatchUserId { get; set; } public DateTimeOffset OccuredDateTime { get; set; } diff --git a/src/Nocr.TextMatcher.Host/Controllers/TextMatchController.cs b/src/Nocr.TextMatcher.Host/Controllers/TextMatchController.cs index 3831395..bb05431 100644 --- a/src/Nocr.TextMatcher.Host/Controllers/TextMatchController.cs +++ b/src/Nocr.TextMatcher.Host/Controllers/TextMatchController.cs @@ -20,13 +20,38 @@ public class TextMatchController : ControllerBase [HttpPost] public Task Create([FromBody] CreateTextMatchRequest request, CancellationToken cancellationToken = default) { - return _textMatchService.Create(request.UserId, request.ChatUsername, request.Template, request.Rule, - request.ChatId, cancellationToken); + return _textMatchService.Create(request.UserId, request.ChatUsername, request.Template, request.Rule, cancellationToken); + } + + [HttpGet(WebRoutes.TextMatches.ById)] + public Task GetById([FromRoute] long id, CancellationToken cancellationToken = default) + { + return _textMatchService.GetById(id, cancellationToken); + } + + + [HttpDelete(WebRoutes.TextMatches.ById)] + public Task Delete([FromRoute] long id, CancellationToken cancellationToken = default) + { + return _textMatchService.Delete(id, cancellationToken); } [HttpGet(WebRoutes.TextMatches.ByUserId)] - public Task> GetByUserId([FromRoute] long userId, CancellationToken cancellationToken = default) + public Task> GetByUserId([FromRoute] long userId, + CancellationToken cancellationToken = default) { return _textMatchService.GetByUserId(userId, cancellationToken); } + + [HttpPatch(WebRoutes.TextMatches.Activate)] + public Task Activate([FromRoute] long id, CancellationToken cancellationToken = default) + { + return _textMatchService.Activate(id, cancellationToken); + } + + [HttpPatch(WebRoutes.TextMatches.Disable)] + public Task Disable([FromRoute] long id, CancellationToken cancellationToken = default) + { + return _textMatchService.Disable(id, cancellationToken); + } } \ No newline at end of file