From ced8c15efb1d5df9aaff7fae0f890aaebddd7c67 Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Fri, 29 Mar 2024 13:24:04 +0000 Subject: [PATCH] DRAFT: nazarovsa/persistence (#2) Reviewed-on: https://gitea.musk.fun/nocr/text-matcher/pulls/2 Co-authored-by: Sergey Nazarov Co-committed-by: Sergey Nazarov --- Directory.Packages.props | 7 ++ Nocr.TextMatcher.sln | 23 +++++++ .../Nocr.TextMatcher.Api.Contracts.csproj | 4 ++ ...xtMatchData.cs => TextSubscriptionData.cs} | 6 +- .../TextMatches/ITextMatchesController.cs | 27 -------- .../ITextSubscriptionsController.cs | 27 ++++++++ ...st.cs => CreateTextSubscriptionRequest.cs} | 6 +- .../WebRoutes.cs | 2 +- src/Nocr.TextMatcher.AppServices/Constants.cs | 11 ++++ .../Nocr.TextMatcher.AppServices.csproj | 2 + .../ServiceCollectionExtensions.cs | 6 +- .../Repositories/ITextMatchRepository.cs | 16 ----- .../ITextSubscriptionRepository.cs | 16 +++++ .../InMemoryTextMatchRepository.cs | 61 ----------------- .../TextSubscriptionRepository.cs | 48 ++++++++++++++ ...ervice.cs => ITextSubscriptionsService.cs} | 12 ++-- ...Service.cs => TextSubscriptionsService.cs} | 41 ++++++------ .../MessageReceivedHandler.cs | 21 +++--- ...hCreated.cs => TextSubscriptionCreated.cs} | 2 +- ...hMatched.cs => TextSubscriptionMatched.cs} | 6 +- .../Nocr.TextMatcher.Contracts.csproj | 7 ++ .../TextSubscriptionRule.cs} | 4 +- .../Options/RebusRabbitMqOptions.cs | 17 +++++ .../Nocr.TextMatcher.Domain.csproj | 12 ++++ .../TextSubscriptions/TextSubscription.cs} | 26 ++++---- .../Controllers/TextMatchController.cs | 57 ---------------- .../TextSubscriptionsController.cs | 57 ++++++++++++++++ .../Infrastructure/Startup.cs | 17 ++++- .../Nocr.TextMatcher.Host.csproj | 1 + .../appsettings.Development.json | 3 + src/Nocr.TextMatcher.Host/appsettings.json | 5 +- .../DesignTimeTextMatcherContextFactory.cs | 22 +++++++ ...0240328201810_InitialMigration.Designer.cs | 65 +++++++++++++++++++ .../20240328201810_InitialMigration.cs | 47 ++++++++++++++ .../TextMatcherContextModelSnapshot.cs | 62 ++++++++++++++++++ .../Nocr.TextMatcher.Persistence.csproj | 31 +++++++++ .../TextMatcherContext.cs | 25 +++++++ .../TextSubscriptionConfiguration.cs | 15 +++++ .../appsettings.json | 5 ++ ...MatchTests.cs => TextSubscriptionTests.cs} | 12 ++-- 40 files changed, 600 insertions(+), 234 deletions(-) rename src/Nocr.TextMatcher.Api.Contracts/TextMatches/Dto/{TextMatchData.cs => TextSubscriptionData.cs} (85%) delete mode 100644 src/Nocr.TextMatcher.Api.Contracts/TextMatches/ITextMatchesController.cs create mode 100644 src/Nocr.TextMatcher.Api.Contracts/TextMatches/ITextSubscriptionsController.cs rename src/Nocr.TextMatcher.Api.Contracts/TextMatches/Requests/{CreateTextMatchRequest.cs => CreateTextSubscriptionRequest.cs} (81%) create mode 100644 src/Nocr.TextMatcher.AppServices/Constants.cs delete mode 100644 src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/ITextMatchRepository.cs create mode 100644 src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/ITextSubscriptionRepository.cs delete mode 100644 src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/InMemoryTextMatchRepository.cs create mode 100644 src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/TextSubscriptionRepository.cs rename src/Nocr.TextMatcher.AppServices/TextMatches/Services/{ITextMatchService.cs => ITextSubscriptionsService.cs} (55%) rename src/Nocr.TextMatcher.AppServices/TextMatches/Services/{TextMatchService.cs => TextSubscriptionsService.cs} (65%) rename src/Nocr.TextMatcher.AppServices/{TextMatchers => TextSubscriptions}/MessageReceivedHandler.cs (71%) rename src/Nocr.TextMatcher.Async.Api.Contracts/{TextMatchCreated.cs => TextSubscriptionCreated.cs} (85%) rename src/Nocr.TextMatcher.Async.Api.Contracts/{TextMatchMatched.cs => TextSubscriptionMatched.cs} (89%) create mode 100644 src/Nocr.TextMatcher.Contracts/Nocr.TextMatcher.Contracts.csproj rename src/{Nocr.TextMatcher.Api.Contracts/TextMatches/TextMatchRule.cs => Nocr.TextMatcher.Contracts/TextSubscriptionRule.cs} (65%) create mode 100644 src/Nocr.TextMatcher.Domain/Nocr.TextMatcher.Domain.csproj rename src/{Nocr.TextMatcher.AppServices/TextMatches/TextMatch.cs => Nocr.TextMatcher.Domain/TextSubscriptions/TextSubscription.cs} (80%) delete mode 100644 src/Nocr.TextMatcher.Host/Controllers/TextMatchController.cs create mode 100644 src/Nocr.TextMatcher.Host/Controllers/TextSubscriptionsController.cs create mode 100644 src/Nocr.TextMatcher.Persistence/DesignTimeTextMatcherContextFactory.cs create mode 100644 src/Nocr.TextMatcher.Persistence/Migrations/20240328201810_InitialMigration.Designer.cs create mode 100644 src/Nocr.TextMatcher.Persistence/Migrations/20240328201810_InitialMigration.cs create mode 100644 src/Nocr.TextMatcher.Persistence/Migrations/TextMatcherContextModelSnapshot.cs create mode 100644 src/Nocr.TextMatcher.Persistence/Nocr.TextMatcher.Persistence.csproj create mode 100644 src/Nocr.TextMatcher.Persistence/TextMatcherContext.cs create mode 100644 src/Nocr.TextMatcher.Persistence/TextSubscriptionConfiguration.cs create mode 100644 src/Nocr.TextMatcher.Persistence/appsettings.json rename tests/Nocr.TextMatcher.AppServices.UnitTests/{TextMatchTests.cs => TextSubscriptionTests.cs} (72%) diff --git a/Directory.Packages.props b/Directory.Packages.props index 6b05077..7efbb5e 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -24,8 +24,15 @@ + + + + + + + diff --git a/Nocr.TextMatcher.sln b/Nocr.TextMatcher.sln index ae093f3..1e5c82a 100644 --- a/Nocr.TextMatcher.sln +++ b/Nocr.TextMatcher.sln @@ -22,6 +22,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Tests", "_Tests", "{6E4D9F EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nocr.TextMatcher.AppServices.UnitTests", "tests\Nocr.TextMatcher.AppServices.UnitTests\Nocr.TextMatcher.AppServices.UnitTests.csproj", "{B721E055-84AF-44C6-973D-33241FD2EA7C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nocr.TextMatcher.Contracts", "src\Nocr.TextMatcher.Contracts\Nocr.TextMatcher.Contracts.csproj", "{D6EC25FA-B8B9-4B21-BC4B-16E604B16E02}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nocr.TextMatcher.Persistence", "src\Nocr.TextMatcher.Persistence\Nocr.TextMatcher.Persistence.csproj", "{D578EE54-B55A-4B45-859D-7F343C73EEF5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Contracts", "Contracts", "{0B8E28B3-EECC-4981-A87F-6D74C4F23371}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nocr.TextMatcher.Domain", "src\Nocr.TextMatcher.Domain\Nocr.TextMatcher.Domain.csproj", "{301BBDEA-ACF8-404D-83FA-AA26A8153D35}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -52,8 +60,23 @@ Global {B721E055-84AF-44C6-973D-33241FD2EA7C}.Debug|Any CPU.Build.0 = Debug|Any CPU {B721E055-84AF-44C6-973D-33241FD2EA7C}.Release|Any CPU.ActiveCfg = Release|Any CPU {B721E055-84AF-44C6-973D-33241FD2EA7C}.Release|Any CPU.Build.0 = Release|Any CPU + {D6EC25FA-B8B9-4B21-BC4B-16E604B16E02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6EC25FA-B8B9-4B21-BC4B-16E604B16E02}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D6EC25FA-B8B9-4B21-BC4B-16E604B16E02}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D6EC25FA-B8B9-4B21-BC4B-16E604B16E02}.Release|Any CPU.Build.0 = Release|Any CPU + {D578EE54-B55A-4B45-859D-7F343C73EEF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D578EE54-B55A-4B45-859D-7F343C73EEF5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D578EE54-B55A-4B45-859D-7F343C73EEF5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D578EE54-B55A-4B45-859D-7F343C73EEF5}.Release|Any CPU.Build.0 = Release|Any CPU + {301BBDEA-ACF8-404D-83FA-AA26A8153D35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {301BBDEA-ACF8-404D-83FA-AA26A8153D35}.Debug|Any CPU.Build.0 = Debug|Any CPU + {301BBDEA-ACF8-404D-83FA-AA26A8153D35}.Release|Any CPU.ActiveCfg = Release|Any CPU + {301BBDEA-ACF8-404D-83FA-AA26A8153D35}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {B721E055-84AF-44C6-973D-33241FD2EA7C} = {6E4D9F75-861F-4C00-A5C8-00D1BEE5A659} + {4028666B-FAE8-4DB9-8D10-B00F5F31BDCC} = {0B8E28B3-EECC-4981-A87F-6D74C4F23371} + {A6332064-40EE-498A-826D-638BC295A185} = {0B8E28B3-EECC-4981-A87F-6D74C4F23371} + {D6EC25FA-B8B9-4B21-BC4B-16E604B16E02} = {0B8E28B3-EECC-4981-A87F-6D74C4F23371} EndGlobalSection EndGlobal diff --git a/src/Nocr.TextMatcher.Api.Contracts/Nocr.TextMatcher.Api.Contracts.csproj b/src/Nocr.TextMatcher.Api.Contracts/Nocr.TextMatcher.Api.Contracts.csproj index 27549fd..b576723 100644 --- a/src/Nocr.TextMatcher.Api.Contracts/Nocr.TextMatcher.Api.Contracts.csproj +++ b/src/Nocr.TextMatcher.Api.Contracts/Nocr.TextMatcher.Api.Contracts.csproj @@ -8,4 +8,8 @@ + + + + diff --git a/src/Nocr.TextMatcher.Api.Contracts/TextMatches/Dto/TextMatchData.cs b/src/Nocr.TextMatcher.Api.Contracts/TextMatches/Dto/TextSubscriptionData.cs similarity index 85% rename from src/Nocr.TextMatcher.Api.Contracts/TextMatches/Dto/TextMatchData.cs rename to src/Nocr.TextMatcher.Api.Contracts/TextMatches/Dto/TextSubscriptionData.cs index 2d3aa8d..e5a0663 100644 --- a/src/Nocr.TextMatcher.Api.Contracts/TextMatches/Dto/TextMatchData.cs +++ b/src/Nocr.TextMatcher.Api.Contracts/TextMatches/Dto/TextSubscriptionData.cs @@ -1,6 +1,8 @@ +using Nocr.TextMatcher.Contracts; + namespace Nocr.TextMatcher.Api.Contracts.TextMatches.Dto; -public sealed class TextMatchData +public sealed class TextSubscriptionData { public long Id { get; set; } @@ -27,7 +29,7 @@ public sealed class TextMatchData /// /// Тип матча /// - public TextMatchRule Rule { get; set; } + public TextSubscriptionRule Rule { get; set; } /// /// Дата создания diff --git a/src/Nocr.TextMatcher.Api.Contracts/TextMatches/ITextMatchesController.cs b/src/Nocr.TextMatcher.Api.Contracts/TextMatches/ITextMatchesController.cs deleted file mode 100644 index 7038899..0000000 --- a/src/Nocr.TextMatcher.Api.Contracts/TextMatches/ITextMatchesController.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Nocr.TextMatcher.Api.Contracts.TextMatches.Dto; -using Nocr.TextMatcher.Api.Contracts.TextMatches.Requests; -using RestEase; - -namespace Nocr.TextMatcher.Api.Contracts.TextMatches; - -[BasePath(WebRoutes.TextMatches.Path)] -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/ITextSubscriptionsController.cs b/src/Nocr.TextMatcher.Api.Contracts/TextMatches/ITextSubscriptionsController.cs new file mode 100644 index 0000000..3fa2563 --- /dev/null +++ b/src/Nocr.TextMatcher.Api.Contracts/TextMatches/ITextSubscriptionsController.cs @@ -0,0 +1,27 @@ +using Nocr.TextMatcher.Api.Contracts.TextMatches.Dto; +using Nocr.TextMatcher.Api.Contracts.TextMatches.Requests; +using RestEase; + +namespace Nocr.TextMatcher.Api.Contracts.TextMatches; + +[BasePath(WebRoutes.TextSubscriptions.Path)] +public interface ITextSubscriptionsController +{ + [Post] + Task Create([Body] CreateTextSubscriptionRequest request, CancellationToken cancellationToken = default); + + [Get(WebRoutes.TextSubscriptions.ById)] + Task GetById([Path] long id, CancellationToken cancellationToken = default); + + [Get(WebRoutes.TextSubscriptions.ByUserId)] + Task GetByUserId([Path] long userId, CancellationToken cancellationToken = default); + + [Delete(WebRoutes.TextSubscriptions.ById)] + public Task Delete([Path] long id, CancellationToken cancellationToken = default); + + [Patch(WebRoutes.TextSubscriptions.Activate)] + Task Activate([Path] long id, CancellationToken cancellationToken = default); + + [Patch(WebRoutes.TextSubscriptions.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/CreateTextSubscriptionRequest.cs similarity index 81% rename from src/Nocr.TextMatcher.Api.Contracts/TextMatches/Requests/CreateTextMatchRequest.cs rename to src/Nocr.TextMatcher.Api.Contracts/TextMatches/Requests/CreateTextSubscriptionRequest.cs index bd54c88..560dc6f 100644 --- a/src/Nocr.TextMatcher.Api.Contracts/TextMatches/Requests/CreateTextMatchRequest.cs +++ b/src/Nocr.TextMatcher.Api.Contracts/TextMatches/Requests/CreateTextSubscriptionRequest.cs @@ -1,6 +1,8 @@ +using Nocr.TextMatcher.Contracts; + namespace Nocr.TextMatcher.Api.Contracts.TextMatches.Requests; -public class CreateTextMatchRequest +public class CreateTextSubscriptionRequest { /// /// Идентификатор пользователя в системе @@ -20,5 +22,5 @@ public class CreateTextMatchRequest /// /// Тип совпадения /// - public TextMatchRule Rule { get; set; } + public TextSubscriptionRule Rule { get; set; } } \ No newline at end of file diff --git a/src/Nocr.TextMatcher.Api.Contracts/WebRoutes.cs b/src/Nocr.TextMatcher.Api.Contracts/WebRoutes.cs index 1294b0b..e49518d 100644 --- a/src/Nocr.TextMatcher.Api.Contracts/WebRoutes.cs +++ b/src/Nocr.TextMatcher.Api.Contracts/WebRoutes.cs @@ -4,7 +4,7 @@ public static class WebRoutes { public const string BasePath = "/api"; - public static class TextMatches + public static class TextSubscriptions { public const string Path = BasePath + "/" + "text-matches"; diff --git a/src/Nocr.TextMatcher.AppServices/Constants.cs b/src/Nocr.TextMatcher.AppServices/Constants.cs new file mode 100644 index 0000000..f347657 --- /dev/null +++ b/src/Nocr.TextMatcher.AppServices/Constants.cs @@ -0,0 +1,11 @@ +namespace Nocr.TextMatcher.AppServices; + +public static class Constants +{ + public static class RoutingKeys + { + public const string MatchedSubscriptions = "nocr.text.matcher.matched"; + + public const string Subscriptions = "nocr.text.matcher.subscriptions"; + } +} \ No newline at end of file diff --git a/src/Nocr.TextMatcher.AppServices/Nocr.TextMatcher.AppServices.csproj b/src/Nocr.TextMatcher.AppServices/Nocr.TextMatcher.AppServices.csproj index 90cdda5..0145c78 100644 --- a/src/Nocr.TextMatcher.AppServices/Nocr.TextMatcher.AppServices.csproj +++ b/src/Nocr.TextMatcher.AppServices/Nocr.TextMatcher.AppServices.csproj @@ -15,6 +15,8 @@ + + diff --git a/src/Nocr.TextMatcher.AppServices/ServiceCollectionExtensions.cs b/src/Nocr.TextMatcher.AppServices/ServiceCollectionExtensions.cs index 66c5ca5..a555b2a 100644 --- a/src/Nocr.TextMatcher.AppServices/ServiceCollectionExtensions.cs +++ b/src/Nocr.TextMatcher.AppServices/ServiceCollectionExtensions.cs @@ -1,7 +1,7 @@ using Microsoft.Extensions.DependencyInjection; -using Nocr.TextMatcher.AppServices.TextMatchers; using Nocr.TextMatcher.AppServices.TextMatches.Repositories; using Nocr.TextMatcher.AppServices.TextMatches.Services; +using Nocr.TextMatcher.AppServices.TextSubscriptions; using Rebus.Config; namespace Nocr.TextMatcher.AppServices; @@ -15,8 +15,8 @@ public static class ServiceCollectionExtensions // Add registrations here services.AddRebusHandler(); - services.AddScoped(); - services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); return services; } diff --git a/src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/ITextMatchRepository.cs b/src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/ITextMatchRepository.cs deleted file mode 100644 index d0f661b..0000000 --- a/src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/ITextMatchRepository.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Nocr.TextMatcher.AppServices.TextMatches.Repositories; - -public interface ITextMatchRepository -{ - Task Create(TextMatch textMatch, CancellationToken cancellationToken = default); - - Task Delete(long id, CancellationToken cancellationToken = default); - - 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/ITextSubscriptionRepository.cs b/src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/ITextSubscriptionRepository.cs new file mode 100644 index 0000000..876238a --- /dev/null +++ b/src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/ITextSubscriptionRepository.cs @@ -0,0 +1,16 @@ +namespace Nocr.TextMatcher.AppServices.TextMatches.Repositories; + +public interface ITextSubscriptionRepository +{ + Task Create(TextSubscription textSubscription, CancellationToken cancellationToken = default); + + Task Delete(long id, CancellationToken cancellationToken = default); + + Task> Get(CancellationToken cancellationToken = default); + + Task> GetByUserId(long userId, CancellationToken cancellationToken = default); + + Task GetById(long id, CancellationToken cancellationToken = default); + + Task Update(TextSubscription subscription, 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 deleted file mode 100644 index d022b61..0000000 --- a/src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/InMemoryTextMatchRepository.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Nocr.TextMatcher.Api.Contracts.TextMatches; - -namespace Nocr.TextMatcher.AppServices.TextMatches.Repositories; - -public sealed class InMemoryTextMatchRepository : ITextMatchRepository -{ - private long _id = 0; - - private List _textMatches = new List(); - - public InMemoryTextMatchRepository() - { - var seed = new[] - { - TextMatch.Initialize(1, "baraholka_tbi", "телевизор", TextMatchRule.Full, DateTimeOffset.UtcNow), - TextMatch.Initialize(1, "baraholka_tbi", "macbook mac", TextMatchRule.AnyWord, DateTimeOffset.UtcNow), - TextMatch.Initialize(1, "baraholka_tbi", "гитар", TextMatchRule.Full, DateTimeOffset.UtcNow), - TextMatch.Initialize(1, "baraholka_tbi", "обувь ботинки туфли", TextMatchRule.AnyWord, DateTimeOffset.UtcNow), - TextMatch.Initialize(1, "baraholka_tbi", "одежда платья брюки рубашка рубашки", TextMatchRule.AnyWord, DateTimeOffset.UtcNow), - }; - - _textMatches.AddRange(seed); - } - - public Task Create(TextMatch textMatch, CancellationToken cancellationToken = default) - { - var id = Interlocked.Increment(ref _id); - textMatch.Id = id; - _textMatches.Add(textMatch); - - return Task.FromResult(id); - } - - public Task Delete(long id, CancellationToken cancellationToken = default) - { - var index = _textMatches.FindIndex(x => x.Id == id); - _textMatches.RemoveAt(index); - - return Task.CompletedTask; - } - - public Task> Get(CancellationToken cancellationToken = default) - { - return Task.FromResult>(_textMatches); - } - - public Task> GetByUserId(long userId, CancellationToken cancellationToken = default) - { - 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/Repositories/TextSubscriptionRepository.cs b/src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/TextSubscriptionRepository.cs new file mode 100644 index 0000000..a70f85b --- /dev/null +++ b/src/Nocr.TextMatcher.AppServices/TextMatches/Repositories/TextSubscriptionRepository.cs @@ -0,0 +1,48 @@ +using Microsoft.EntityFrameworkCore; +using Nocr.TextMatcher.Persistence; + +namespace Nocr.TextMatcher.AppServices.TextMatches.Repositories; + +public sealed class TextSubscriptionRepository : ITextSubscriptionRepository +{ + private readonly TextMatcherContext _db; + + public TextSubscriptionRepository(TextMatcherContext db) + { + _db = db ?? throw new ArgumentNullException(nameof(db)); + } + + public async Task Create(TextSubscription textSubscription, CancellationToken cancellationToken = default) + { + await _db.TextSubscriptions.AddAsync(textSubscription, cancellationToken); + await _db.SaveChangesAsync(cancellationToken); + + return textSubscription.Id; + } + + public async Task Delete(long id, CancellationToken cancellationToken = default) + { + await _db.TextSubscriptions.Where(x => x.Id == id).ExecuteDeleteAsync(cancellationToken); + } + + public async Task> Get(CancellationToken cancellationToken = default) + { + return await _db.TextSubscriptions.AsNoTracking().ToListAsync(cancellationToken); + } + + public async Task> GetByUserId(long userId, CancellationToken cancellationToken = default) + { + return await _db.TextSubscriptions.Where(x => x.UserId == userId).AsNoTracking().ToListAsync(cancellationToken); + } + + public async Task GetById(long id, CancellationToken cancellationToken = default) + { + return await _db.TextSubscriptions.FirstOrDefaultAsync(x => x.Id == id, cancellationToken: cancellationToken); + } + + public async Task Update(TextSubscription subscription, CancellationToken cancellationToken = default) + { + _db.TextSubscriptions.Update(subscription); + await _db.SaveChangesAsync(cancellationToken); + } +} \ No newline at end of file diff --git a/src/Nocr.TextMatcher.AppServices/TextMatches/Services/ITextMatchService.cs b/src/Nocr.TextMatcher.AppServices/TextMatches/Services/ITextSubscriptionsService.cs similarity index 55% rename from src/Nocr.TextMatcher.AppServices/TextMatches/Services/ITextMatchService.cs rename to src/Nocr.TextMatcher.AppServices/TextMatches/Services/ITextSubscriptionsService.cs index d286a4d..4eb256e 100644 --- a/src/Nocr.TextMatcher.AppServices/TextMatches/Services/ITextMatchService.cs +++ b/src/Nocr.TextMatcher.AppServices/TextMatches/Services/ITextSubscriptionsService.cs @@ -1,14 +1,14 @@ -using Nocr.TextMatcher.Api.Contracts.TextMatches; using Nocr.TextMatcher.Api.Contracts.TextMatches.Dto; +using Nocr.TextMatcher.Contracts; namespace Nocr.TextMatcher.AppServices.TextMatches.Services; -public interface ITextMatchService +public interface ITextSubscriptionsService { - Task Create(long userId, string chatUsername, string template, TextMatchRule rule, + Task Create(long userId, string chatUsername, string template, TextSubscriptionRule rule, CancellationToken cancellationToken = default); - Task GetById(long id, CancellationToken cancellationToken = default); + Task GetById(long id, CancellationToken cancellationToken = default); Task Delete(long id, CancellationToken cancellationToken = default); @@ -16,7 +16,7 @@ public interface ITextMatchService Task Disable(long id, CancellationToken cancellationToken = default); - Task> Get(CancellationToken cancellationToken = default); + Task> Get(CancellationToken cancellationToken = default); - Task> GetByUserId(long userId, 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/TextSubscriptionsService.cs similarity index 65% rename from src/Nocr.TextMatcher.AppServices/TextMatches/Services/TextMatchService.cs rename to src/Nocr.TextMatcher.AppServices/TextMatches/Services/TextSubscriptionsService.cs index cb2b137..8fba6a9 100644 --- a/src/Nocr.TextMatcher.AppServices/TextMatches/Services/TextMatchService.cs +++ b/src/Nocr.TextMatcher.AppServices/TextMatches/Services/TextSubscriptionsService.cs @@ -1,19 +1,19 @@ -using Nocr.TextMatcher.Api.Contracts.TextMatches; using Nocr.TextMatcher.Api.Contracts.TextMatches.Dto; using Nocr.TextMatcher.AppServices.TextMatches.Repositories; using Nocr.TextMatcher.Async.Api.Contracts; +using Nocr.TextMatcher.Contracts; using Nocr.TextMatcher.Core.Dates; using Rebus.Bus; namespace Nocr.TextMatcher.AppServices.TextMatches.Services; -public sealed class TextMatchService : ITextMatchService +public sealed class TextSubscriptionsService : ITextSubscriptionsService { private readonly IBus _bus; - private readonly ITextMatchRepository _repository; + private readonly ITextSubscriptionRepository _repository; private readonly ICurrentDateProvider _dateProvider; - public TextMatchService(IBus bus, ITextMatchRepository repository, + public TextSubscriptionsService(IBus bus, ITextSubscriptionRepository repository, ICurrentDateProvider dateProvider) { _bus = bus ?? throw new ArgumentNullException(nameof(bus)); @@ -21,24 +21,23 @@ public sealed class TextMatchService : ITextMatchService _dateProvider = dateProvider ?? throw new ArgumentNullException(nameof(dateProvider)); } - public async Task Create(long userId, string chatUsername, string template, TextMatchRule rule, + public async Task Create(long userId, string chatUsername, string template, TextSubscriptionRule rule, CancellationToken cancellationToken = default) { - var textMatch = TextMatch.Initialize(userId, chatUsername, template, rule, _dateProvider.UtcNow); + var textMatch = TextSubscription.Initialize(userId, chatUsername, template, rule, _dateProvider.UtcNow); await _repository.Create(textMatch, cancellationToken); - var @event = new TextMatchCreated + var @event = new TextSubscriptionCreated { ChatUsername = textMatch.ChatUsername }; - // TODO: - await _bus.Advanced.Topics.Publish("nocr.text.matcher.matches", @event); + await _bus.Advanced.Topics.Publish(Constants.RoutingKeys.Subscriptions, @event); return textMatch.Id; } - public async Task GetById(long id, CancellationToken cancellationToken) + public async Task GetById(long id, CancellationToken cancellationToken) { var textMatch = await _repository.GetById(id, cancellationToken); if (textMatch == null) @@ -78,31 +77,31 @@ public sealed class TextMatchService : ITextMatchService await _repository.Update(textMatch, cancellationToken); } - public async Task> Get(CancellationToken cancellationToken = default) + public async Task> Get(CancellationToken cancellationToken = default) { var matches = await _repository.Get(cancellationToken); return matches.Select(MapToTextMatchData).ToArray(); } - public async Task> GetByUserId(long userId, + public async Task> GetByUserId(long userId, CancellationToken cancellationToken = default) { var matches = await _repository.GetByUserId(userId, cancellationToken); return matches.Select(MapToTextMatchData).ToArray(); } - private TextMatchData MapToTextMatchData(TextMatch textMatch) + private TextSubscriptionData MapToTextMatchData(TextSubscription textSubscription) { - return new TextMatchData + return new TextSubscriptionData { - Id = textMatch.Id, - ChatUsername = textMatch.ChatUsername, - Active = textMatch.Active, - Template = textMatch.Template, - UserId = textMatch.UserId, - Rule = textMatch.Rule, - CreatedDateTime = textMatch.CreatedDateTime + Id = textSubscription.Id, + ChatUsername = textSubscription.ChatUsername, + Active = textSubscription.Active, + Template = textSubscription.Template, + UserId = textSubscription.UserId, + Rule = textSubscription.Rule, + CreatedDateTime = textSubscription.CreatedDateTime }; } } \ No newline at end of file diff --git a/src/Nocr.TextMatcher.AppServices/TextMatchers/MessageReceivedHandler.cs b/src/Nocr.TextMatcher.AppServices/TextSubscriptions/MessageReceivedHandler.cs similarity index 71% rename from src/Nocr.TextMatcher.AppServices/TextMatchers/MessageReceivedHandler.cs rename to src/Nocr.TextMatcher.AppServices/TextSubscriptions/MessageReceivedHandler.cs index 6f7046d..31cd827 100644 --- a/src/Nocr.TextMatcher.AppServices/TextMatchers/MessageReceivedHandler.cs +++ b/src/Nocr.TextMatcher.AppServices/TextSubscriptions/MessageReceivedHandler.cs @@ -6,23 +6,23 @@ using Nocr.TextMatcher.Core.Dates; using Rebus.Bus; using Rebus.Handlers; -namespace Nocr.TextMatcher.AppServices.TextMatchers; +namespace Nocr.TextMatcher.AppServices.TextSubscriptions; public sealed class MessageReceivedHandler : IHandleMessages { private readonly ILogger _logger; private readonly IBus _bus; - private readonly ITextMatchRepository _textMatchService; + private readonly ITextSubscriptionRepository _textSubscriptionService; private readonly ICurrentDateProvider _dateProvider; public MessageReceivedHandler(ILogger logger, IBus bus, - ITextMatchRepository textMatchRepository, + ITextSubscriptionRepository textSubscriptionRepository, ICurrentDateProvider dateProvider) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _bus = bus ?? throw new ArgumentNullException(nameof(bus)); - _textMatchService = textMatchRepository ?? throw new ArgumentNullException(nameof(textMatchRepository)); + _textSubscriptionService = textSubscriptionRepository ?? throw new ArgumentNullException(nameof(textSubscriptionRepository)); _dateProvider = dateProvider ?? throw new ArgumentNullException(nameof(dateProvider)); } @@ -30,17 +30,17 @@ public sealed class MessageReceivedHandler : IHandleMessages { _logger.LogInformation("Received message: {@Message}", message); - var matches = await _textMatchService.Get(); + var matches = await _textSubscriptionService.Get(); foreach (var match in matches.Where(x => x.Active)) { if (match.IsMatches(message.ChatUsername, message.Text)) { _logger.LogInformation("Message {@Message} matched {@Match}", message, match); - var @event = new TextMatchMatched + var @event = new TextSubscriptionMatched { - MatchId = match.Id, - MatchUserId = match.UserId, + SubscriptionId = match.Id, + SubscriptionUserId = match.UserId, ChatUsername = match.ChatUsername, Rule = (int)match.Rule, Template = match.Template, @@ -49,9 +49,8 @@ public sealed class MessageReceivedHandler : IHandleMessages OccuredDateTime = message.OccuredDateTime, PublishedDateTime = _dateProvider.UtcNow }; - - // TODO: - await _bus.Advanced.Topics.Publish("nocr.text.matcher.matched", @event); + + await _bus.Advanced.Topics.Publish(Constants.RoutingKeys.MatchedSubscriptions, @event); } } } diff --git a/src/Nocr.TextMatcher.Async.Api.Contracts/TextMatchCreated.cs b/src/Nocr.TextMatcher.Async.Api.Contracts/TextSubscriptionCreated.cs similarity index 85% rename from src/Nocr.TextMatcher.Async.Api.Contracts/TextMatchCreated.cs rename to src/Nocr.TextMatcher.Async.Api.Contracts/TextSubscriptionCreated.cs index e1870db..077f0ec 100644 --- a/src/Nocr.TextMatcher.Async.Api.Contracts/TextMatchCreated.cs +++ b/src/Nocr.TextMatcher.Async.Api.Contracts/TextSubscriptionCreated.cs @@ -1,6 +1,6 @@ namespace Nocr.TextMatcher.Async.Api.Contracts; -public class TextMatchCreated : IEvent +public class TextSubscriptionCreated : IEvent { public Guid Id => Guid.NewGuid(); diff --git a/src/Nocr.TextMatcher.Async.Api.Contracts/TextMatchMatched.cs b/src/Nocr.TextMatcher.Async.Api.Contracts/TextSubscriptionMatched.cs similarity index 89% rename from src/Nocr.TextMatcher.Async.Api.Contracts/TextMatchMatched.cs rename to src/Nocr.TextMatcher.Async.Api.Contracts/TextSubscriptionMatched.cs index 37cd9b0..e921fc3 100644 --- a/src/Nocr.TextMatcher.Async.Api.Contracts/TextMatchMatched.cs +++ b/src/Nocr.TextMatcher.Async.Api.Contracts/TextSubscriptionMatched.cs @@ -1,18 +1,18 @@ namespace Nocr.TextMatcher.Async.Api.Contracts; -public class TextMatchMatched : IEvent +public class TextSubscriptionMatched : IEvent { public Guid Id => Guid.NewGuid(); /// /// Идентификатор матча /// - public long MatchId { get; set; } + public long SubscriptionId { get; set; } /// /// Идентификатор владельца матча /// - public long MatchUserId { get; set; } + public long SubscriptionUserId { get; set; } /// /// Username чата diff --git a/src/Nocr.TextMatcher.Contracts/Nocr.TextMatcher.Contracts.csproj b/src/Nocr.TextMatcher.Contracts/Nocr.TextMatcher.Contracts.csproj new file mode 100644 index 0000000..ae01cf7 --- /dev/null +++ b/src/Nocr.TextMatcher.Contracts/Nocr.TextMatcher.Contracts.csproj @@ -0,0 +1,7 @@ + + + + true + + + diff --git a/src/Nocr.TextMatcher.Api.Contracts/TextMatches/TextMatchRule.cs b/src/Nocr.TextMatcher.Contracts/TextSubscriptionRule.cs similarity index 65% rename from src/Nocr.TextMatcher.Api.Contracts/TextMatches/TextMatchRule.cs rename to src/Nocr.TextMatcher.Contracts/TextSubscriptionRule.cs index 28666c7..70273ba 100644 --- a/src/Nocr.TextMatcher.Api.Contracts/TextMatches/TextMatchRule.cs +++ b/src/Nocr.TextMatcher.Contracts/TextSubscriptionRule.cs @@ -1,6 +1,6 @@ -namespace Nocr.TextMatcher.Api.Contracts.TextMatches; +namespace Nocr.TextMatcher.Contracts; -public enum TextMatchRule +public enum TextSubscriptionRule { // Substring Full = 1, diff --git a/src/Nocr.TextMatcher.Core/Options/RebusRabbitMqOptions.cs b/src/Nocr.TextMatcher.Core/Options/RebusRabbitMqOptions.cs index 2894977..e99f9d2 100644 --- a/src/Nocr.TextMatcher.Core/Options/RebusRabbitMqOptions.cs +++ b/src/Nocr.TextMatcher.Core/Options/RebusRabbitMqOptions.cs @@ -2,11 +2,28 @@ namespace Nocr.TextMatcher.Core.Options; public sealed class RebusRabbitMqOptions { + /// + /// Строка подключение + /// public string ConnectionString { get; set; } + /// + /// Имя входящей очереди + /// public string InputQueueName { get; set; } + /// + /// Имя direct exchange + /// public string DirectExchangeName { get; set; } + /// + /// Имя topics exchange + /// public string TopicsExchangeName { get; set; } + + /// + /// Список подписок + /// + public string[] Subscriptions { get; set; } = Array.Empty(); } \ No newline at end of file diff --git a/src/Nocr.TextMatcher.Domain/Nocr.TextMatcher.Domain.csproj b/src/Nocr.TextMatcher.Domain/Nocr.TextMatcher.Domain.csproj new file mode 100644 index 0000000..2c71e7c --- /dev/null +++ b/src/Nocr.TextMatcher.Domain/Nocr.TextMatcher.Domain.csproj @@ -0,0 +1,12 @@ + + + + + false + + + + + + + diff --git a/src/Nocr.TextMatcher.AppServices/TextMatches/TextMatch.cs b/src/Nocr.TextMatcher.Domain/TextSubscriptions/TextSubscription.cs similarity index 80% rename from src/Nocr.TextMatcher.AppServices/TextMatches/TextMatch.cs rename to src/Nocr.TextMatcher.Domain/TextSubscriptions/TextSubscription.cs index 3d3b87c..c0a0ae6 100644 --- a/src/Nocr.TextMatcher.AppServices/TextMatches/TextMatch.cs +++ b/src/Nocr.TextMatcher.Domain/TextSubscriptions/TextSubscription.cs @@ -1,9 +1,9 @@ using System.Text.RegularExpressions; -using Nocr.TextMatcher.Api.Contracts.TextMatches; +using Nocr.TextMatcher.Contracts; namespace Nocr.TextMatcher.AppServices.TextMatches; -public sealed class TextMatch +public sealed class TextSubscription { public long Id { get; set; } @@ -13,16 +13,16 @@ public sealed class TextMatch public string Template { get; private set; } - public TextMatchRule Rule { get; private set; } + public TextSubscriptionRule Rule { get; private set; } public bool Active { get; private set; } public DateTimeOffset CreatedDateTime { get; private set; } - private TextMatch(long userId, + private TextSubscription(long userId, string chatUsername, string template, - TextMatchRule rule, + TextSubscriptionRule rule, DateTimeOffset createdDateTime, bool active) { @@ -34,10 +34,10 @@ public sealed class TextMatch Active = active; } - public static TextMatch Initialize(long userId, + public static TextSubscription Initialize(long userId, string chatUsername, string template, - TextMatchRule rule, + TextSubscriptionRule rule, DateTimeOffset createdDateTime) { if (userId <= 0) @@ -52,14 +52,14 @@ public sealed class TextMatch if (chatUsername.StartsWith("@")) throw new ArgumentException("Chat username should be without @", nameof(chatUsername)); - return new TextMatch(userId, chatUsername, template, rule, createdDateTime, true); + return new TextSubscription(userId, chatUsername, template, rule, createdDateTime, true); } public void Disable() { if (!Active) { - throw new InvalidOperationException("Failed to disable inactive match"); + throw new InvalidOperationException("Failed to disable inactive subscription"); } Active = false; @@ -69,7 +69,7 @@ public sealed class TextMatch { if (Active) { - throw new InvalidOperationException("Failed to activate inactive match"); + throw new InvalidOperationException("Failed to activate inactive subscription"); } Active = true; @@ -82,12 +82,12 @@ public sealed class TextMatch switch (Rule) { - case TextMatchRule.Full: + case TextSubscriptionRule.Full: return text.Contains(Template, StringComparison.OrdinalIgnoreCase); - case TextMatchRule.AnyWord: + case TextSubscriptionRule.AnyWord: var anyWords = Regex.Split(Template, @"\s+"); return anyWords.Any(word => text.Contains(word, StringComparison.OrdinalIgnoreCase)); - case TextMatchRule.AllWords: + case TextSubscriptionRule.AllWords: var allWords = Regex.Split(Template, @"\s+"); return allWords.All(word => text.Contains(word, StringComparison.OrdinalIgnoreCase)); default: diff --git a/src/Nocr.TextMatcher.Host/Controllers/TextMatchController.cs b/src/Nocr.TextMatcher.Host/Controllers/TextMatchController.cs deleted file mode 100644 index bb05431..0000000 --- a/src/Nocr.TextMatcher.Host/Controllers/TextMatchController.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Nocr.TextMatcher.Api.Contracts; -using Nocr.TextMatcher.Api.Contracts.TextMatches.Dto; -using Nocr.TextMatcher.Api.Contracts.TextMatches.Requests; -using Nocr.TextMatcher.AppServices.TextMatches.Services; - -namespace Nocr.TextMatcher.Host.Controllers; - -[ApiController] -[Route(WebRoutes.TextMatches.Path)] -public class TextMatchController : ControllerBase -{ - private readonly ITextMatchService _textMatchService; - - public TextMatchController(ITextMatchService textMatchService) - { - _textMatchService = textMatchService ?? throw new ArgumentNullException(nameof(textMatchService)); - } - - [HttpPost] - public Task Create([FromBody] CreateTextMatchRequest request, CancellationToken cancellationToken = default) - { - 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) - { - 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 diff --git a/src/Nocr.TextMatcher.Host/Controllers/TextSubscriptionsController.cs b/src/Nocr.TextMatcher.Host/Controllers/TextSubscriptionsController.cs new file mode 100644 index 0000000..4257dab --- /dev/null +++ b/src/Nocr.TextMatcher.Host/Controllers/TextSubscriptionsController.cs @@ -0,0 +1,57 @@ +using Microsoft.AspNetCore.Mvc; +using Nocr.TextMatcher.Api.Contracts; +using Nocr.TextMatcher.Api.Contracts.TextMatches.Dto; +using Nocr.TextMatcher.Api.Contracts.TextMatches.Requests; +using Nocr.TextMatcher.AppServices.TextMatches.Services; + +namespace Nocr.TextMatcher.Host.Controllers; + +[ApiController] +[Route(WebRoutes.TextSubscriptions.Path)] +public class TextSubscriptionsController : ControllerBase +{ + private readonly ITextSubscriptionsService _textSubscriptionsService; + + public TextSubscriptionsController(ITextSubscriptionsService textSubscriptionsService) + { + _textSubscriptionsService = textSubscriptionsService ?? throw new ArgumentNullException(nameof(textSubscriptionsService)); + } + + [HttpPost] + public Task Create([FromBody] CreateTextSubscriptionRequest request, CancellationToken cancellationToken = default) + { + return _textSubscriptionsService.Create(request.UserId, request.ChatUsername, request.Template, request.Rule, cancellationToken); + } + + [HttpGet(WebRoutes.TextSubscriptions.ById)] + public Task GetById([FromRoute] long id, CancellationToken cancellationToken = default) + { + return _textSubscriptionsService.GetById(id, cancellationToken); + } + + + [HttpDelete(WebRoutes.TextSubscriptions.ById)] + public Task Delete([FromRoute] long id, CancellationToken cancellationToken = default) + { + return _textSubscriptionsService.Delete(id, cancellationToken); + } + + [HttpGet(WebRoutes.TextSubscriptions.ByUserId)] + public Task> GetByUserId([FromRoute] long userId, + CancellationToken cancellationToken = default) + { + return _textSubscriptionsService.GetByUserId(userId, cancellationToken); + } + + [HttpPatch(WebRoutes.TextSubscriptions.Activate)] + public Task Activate([FromRoute] long id, CancellationToken cancellationToken = default) + { + return _textSubscriptionsService.Activate(id, cancellationToken); + } + + [HttpPatch(WebRoutes.TextSubscriptions.Disable)] + public Task Disable([FromRoute] long id, CancellationToken cancellationToken = default) + { + return _textSubscriptionsService.Disable(id, cancellationToken); + } +} \ No newline at end of file diff --git a/src/Nocr.TextMatcher.Host/Infrastructure/Startup.cs b/src/Nocr.TextMatcher.Host/Infrastructure/Startup.cs index 9ce51d3..38cc9cb 100644 --- a/src/Nocr.TextMatcher.Host/Infrastructure/Startup.cs +++ b/src/Nocr.TextMatcher.Host/Infrastructure/Startup.cs @@ -1,7 +1,9 @@ +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using Nocr.TextMatcher.AppServices; using Nocr.TextMatcher.Core.Dates; using Nocr.TextMatcher.Core.Options; +using Nocr.TextMatcher.Persistence; using Rebus.Bus; using Rebus.Config; using Rebus.Routing.TypeBased; @@ -28,6 +30,15 @@ public class Startup services.AddSwaggerGen(); services.AddAppServices(); + services.AddDbContext( + (ctx, context) => + { + context.UseMySql(Configuration.GetConnectionString(nameof(TextMatcherContext)), + new MariaDbServerVersion(MariaDbServerVersion.LatestSupportedServerVersion), + builder => builder.MigrationsAssembly(typeof(TextMatcherContext).Assembly.FullName)) + .UseLoggerFactory(ctx.GetRequiredService()); + } + ); services.Configure(Configuration.GetSection(nameof(RebusRabbitMqOptions))); services.AddRebus((builder, ctx) => @@ -54,7 +65,11 @@ public class Startup app.UseRouting(); app.UseEndpoints(builder => builder.MapControllers()); + var rabbitMqOptions = app.ApplicationServices.GetRequiredService>(); var bus = app.ApplicationServices.GetRequiredService(); - bus.Advanced.Topics.Subscribe("nocr.telegram.listener").GetAwaiter().GetResult(); + foreach (var subscription in rabbitMqOptions.Value.Subscriptions) + { + bus.Advanced.Topics.Subscribe(subscription).GetAwaiter().GetResult(); + } } } \ No newline at end of file diff --git a/src/Nocr.TextMatcher.Host/Nocr.TextMatcher.Host.csproj b/src/Nocr.TextMatcher.Host/Nocr.TextMatcher.Host.csproj index ba69f55..fe87c10 100644 --- a/src/Nocr.TextMatcher.Host/Nocr.TextMatcher.Host.csproj +++ b/src/Nocr.TextMatcher.Host/Nocr.TextMatcher.Host.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Nocr.TextMatcher.Host/appsettings.Development.json b/src/Nocr.TextMatcher.Host/appsettings.Development.json index 2d26fac..5615168 100644 --- a/src/Nocr.TextMatcher.Host/appsettings.Development.json +++ b/src/Nocr.TextMatcher.Host/appsettings.Development.json @@ -11,5 +11,8 @@ }, "RebusRabbitMqOptions": { "ConnectionString": "amqp://admin:admin@localhost:5672/" + }, + "ConnectionStrings": { + "TextMatcherContext": "server=localhost;port=3306;database=nocr_text_matcher;uid=root;pwd=toor" } } diff --git a/src/Nocr.TextMatcher.Host/appsettings.json b/src/Nocr.TextMatcher.Host/appsettings.json index fd2a17d..1b55ccf 100644 --- a/src/Nocr.TextMatcher.Host/appsettings.json +++ b/src/Nocr.TextMatcher.Host/appsettings.json @@ -13,6 +13,9 @@ "RebusRabbitMqOptions": { "InputQueueName": "nocr.text.matcher.queue", "DirectExchangeName": "nocr.direct", - "TopicsExchangeName": "nocr.topics" + "TopicsExchangeName": "nocr.topics", + "Subscriptions": [ + "nocr.telegram.listener" + ] } } diff --git a/src/Nocr.TextMatcher.Persistence/DesignTimeTextMatcherContextFactory.cs b/src/Nocr.TextMatcher.Persistence/DesignTimeTextMatcherContextFactory.cs new file mode 100644 index 0000000..a419121 --- /dev/null +++ b/src/Nocr.TextMatcher.Persistence/DesignTimeTextMatcherContextFactory.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; + +namespace Nocr.TextMatcher.Persistence; + +public class DesignTimeTextMatcherContextFactory : IDesignTimeDbContextFactory +{ + public TextMatcherContext CreateDbContext(string[] args) + { + var optionsBuilder = new DbContextOptionsBuilder(); + var configuration = new ConfigurationBuilder() + .AddJsonFile("appsettings.json") + .Build(); + + var connectionString = configuration.GetConnectionString("MariaLocal"); + optionsBuilder.UseMySql(connectionString, + new MariaDbServerVersion(MariaDbServerVersion.LatestSupportedServerVersion)); + + return new TextMatcherContext(optionsBuilder.Options); + } +} \ No newline at end of file diff --git a/src/Nocr.TextMatcher.Persistence/Migrations/20240328201810_InitialMigration.Designer.cs b/src/Nocr.TextMatcher.Persistence/Migrations/20240328201810_InitialMigration.Designer.cs new file mode 100644 index 0000000..e443aa6 --- /dev/null +++ b/src/Nocr.TextMatcher.Persistence/Migrations/20240328201810_InitialMigration.Designer.cs @@ -0,0 +1,65 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Nocr.TextMatcher.Persistence; + +#nullable disable + +namespace Nocr.TextMatcher.Persistence.Migrations +{ + [DbContext(typeof(TextMatcherContext))] + [Migration("20240328201810_InitialMigration")] + partial class InitialMigration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Nocr.TextMatcher.AppServices.TextMatches.TextSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("ChatUsername") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("varchar(1024)"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("Rule") + .HasColumnType("int"); + + b.Property("Template") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("varchar(1024)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("TextSubscriptions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Nocr.TextMatcher.Persistence/Migrations/20240328201810_InitialMigration.cs b/src/Nocr.TextMatcher.Persistence/Migrations/20240328201810_InitialMigration.cs new file mode 100644 index 0000000..7d7822b --- /dev/null +++ b/src/Nocr.TextMatcher.Persistence/Migrations/20240328201810_InitialMigration.cs @@ -0,0 +1,47 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Nocr.TextMatcher.Persistence.Migrations +{ + /// + public partial class InitialMigration : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "TextSubscriptions", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "bigint", nullable: false), + ChatUsername = table.Column(type: "varchar(1024)", maxLength: 1024, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Template = table.Column(type: "varchar(1024)", maxLength: 1024, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Rule = table.Column(type: "int", nullable: false), + Active = table.Column(type: "tinyint(1)", nullable: false), + CreatedDateTime = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_TextSubscriptions", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "TextSubscriptions"); + } + } +} diff --git a/src/Nocr.TextMatcher.Persistence/Migrations/TextMatcherContextModelSnapshot.cs b/src/Nocr.TextMatcher.Persistence/Migrations/TextMatcherContextModelSnapshot.cs new file mode 100644 index 0000000..5dced80 --- /dev/null +++ b/src/Nocr.TextMatcher.Persistence/Migrations/TextMatcherContextModelSnapshot.cs @@ -0,0 +1,62 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Nocr.TextMatcher.Persistence; + +#nullable disable + +namespace Nocr.TextMatcher.Persistence.Migrations +{ + [DbContext(typeof(TextMatcherContext))] + partial class TextMatcherContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Nocr.TextMatcher.AppServices.TextMatches.TextSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("ChatUsername") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("varchar(1024)"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("Rule") + .HasColumnType("int"); + + b.Property("Template") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("varchar(1024)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("TextSubscriptions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Nocr.TextMatcher.Persistence/Nocr.TextMatcher.Persistence.csproj b/src/Nocr.TextMatcher.Persistence/Nocr.TextMatcher.Persistence.csproj new file mode 100644 index 0000000..4ccab86 --- /dev/null +++ b/src/Nocr.TextMatcher.Persistence/Nocr.TextMatcher.Persistence.csproj @@ -0,0 +1,31 @@ + + + + net8.0 + enable + enable + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + Always + + + + diff --git a/src/Nocr.TextMatcher.Persistence/TextMatcherContext.cs b/src/Nocr.TextMatcher.Persistence/TextMatcherContext.cs new file mode 100644 index 0000000..150e604 --- /dev/null +++ b/src/Nocr.TextMatcher.Persistence/TextMatcherContext.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore; +using Nocr.TextMatcher.AppServices.TextMatches; + +namespace Nocr.TextMatcher.Persistence; + +public class TextMatcherContext : DbContext +{ + public DbSet TextSubscriptions { get; set; } + + public TextMatcherContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.ApplyConfiguration(new TextSubscriptionConfiguration()); + + base.OnModelCreating(modelBuilder); + } +} \ No newline at end of file diff --git a/src/Nocr.TextMatcher.Persistence/TextSubscriptionConfiguration.cs b/src/Nocr.TextMatcher.Persistence/TextSubscriptionConfiguration.cs new file mode 100644 index 0000000..6de47ec --- /dev/null +++ b/src/Nocr.TextMatcher.Persistence/TextSubscriptionConfiguration.cs @@ -0,0 +1,15 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nocr.TextMatcher.AppServices.TextMatches; + +namespace Nocr.TextMatcher.Persistence; + +public class TextSubscriptionConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(x => x.Id); + builder.Property(x=> x.ChatUsername).IsRequired().HasMaxLength(1024); + builder.Property(x => x.Template).IsRequired().HasMaxLength(1024); + } +} \ No newline at end of file diff --git a/src/Nocr.TextMatcher.Persistence/appsettings.json b/src/Nocr.TextMatcher.Persistence/appsettings.json new file mode 100644 index 0000000..78c0e88 --- /dev/null +++ b/src/Nocr.TextMatcher.Persistence/appsettings.json @@ -0,0 +1,5 @@ +{ + "ConnectionStrings": { + "MariaLocal": "server=localhost;port=3306;database=nocr_text_matcher;uid=root;pwd=toor" + } +} \ No newline at end of file diff --git a/tests/Nocr.TextMatcher.AppServices.UnitTests/TextMatchTests.cs b/tests/Nocr.TextMatcher.AppServices.UnitTests/TextSubscriptionTests.cs similarity index 72% rename from tests/Nocr.TextMatcher.AppServices.UnitTests/TextMatchTests.cs rename to tests/Nocr.TextMatcher.AppServices.UnitTests/TextSubscriptionTests.cs index 19928b4..11b0d74 100644 --- a/tests/Nocr.TextMatcher.AppServices.UnitTests/TextMatchTests.cs +++ b/tests/Nocr.TextMatcher.AppServices.UnitTests/TextSubscriptionTests.cs @@ -1,10 +1,10 @@ -using Nocr.TextMatcher.Api.Contracts.TextMatches; using Nocr.TextMatcher.AppServices.TextMatches; +using Nocr.TextMatcher.Contracts; using Xunit; namespace Nocr.TextMatcher.AppServices.UnitTests; -public class TextMatchTests +public class TextSubscriptionTests { private const long UserId = 1; @@ -14,7 +14,7 @@ public class TextMatchTests public void IsMatches_SameChatId_FullRule_MatchesText() { // Arrange - var match = TextMatch.Initialize(UserId, "Барахолка", "велосипед", TextMatchRule.Full, CreatedDateTime); + var match = TextSubscription.Initialize(UserId, "Барахолка", "велосипед", TextSubscriptionRule.Full, CreatedDateTime); var text = "Продам снежный велосипед 100 лари. Гудаури."; // Act @@ -28,7 +28,7 @@ public class TextMatchTests public void IsMatches_SameChatId_AnyWord_MatchesText() { // Arrange - var match = TextMatch.Initialize(UserId, "Барахолка", "iphone айфон", TextMatchRule.AnyWord, + var match = TextSubscription.Initialize(UserId, "Барахолка", "iphone айфон", TextSubscriptionRule.AnyWord, CreatedDateTime); var text = "Продам айфон велосипед 100 лари. Гудаури."; @@ -43,7 +43,7 @@ public class TextMatchTests public void IsMatches_SameChatId_AllWords_MatchesText() { // Arrange - var match = TextMatch.Initialize(UserId, "Барахолка", "iphone айфон", TextMatchRule.AnyWord, + var match = TextSubscription.Initialize(UserId, "Барахолка", "iphone айфон", TextSubscriptionRule.AnyWord, CreatedDateTime); var text = "Гомарджоба. Продам iphone (айфон) 1000 лари. Гудаури."; @@ -58,7 +58,7 @@ public class TextMatchTests public void IsMatches_DifferentChatIdAndUserName_NotMatchesText() { // Arrange - var match = TextMatch.Initialize(UserId, "Барахолка", "iphone", TextMatchRule.Full, CreatedDateTime); + var match = TextSubscription.Initialize(UserId, "Барахолка", "iphone", TextSubscriptionRule.Full, CreatedDateTime); // Act var result = match.IsMatches(string.Empty, "iphone");