DRAFT: nazarovsa/persistence (#2)
Reviewed-on: #2 Co-authored-by: Sergey Nazarov <insight.appdev@gmail.com> Co-committed-by: Sergey Nazarov <insight.appdev@gmail.com>
This commit is contained in:
parent
1f5cc40e54
commit
ced8c15efb
@ -24,8 +24,15 @@
|
||||
<PackageVersion Include="Rebus.Serilog" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Microsoft">
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="$(MicrosoftVersion)" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftVersion)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="EntityFramework">
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.3" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.3" />
|
||||
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Tests">
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
|
||||
|
||||
@ -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
|
||||
|
||||
@ -8,4 +8,8 @@
|
||||
<PackageReference Include="RestEase" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nocr.TextMatcher.Contracts\Nocr.TextMatcher.Contracts.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -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
|
||||
/// <summary>
|
||||
/// Тип матча
|
||||
/// </summary>
|
||||
public TextMatchRule Rule { get; set; }
|
||||
public TextSubscriptionRule Rule { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Дата создания
|
||||
@ -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<long> Create([Body] CreateTextMatchRequest request, CancellationToken cancellationToken = default);
|
||||
|
||||
[Get(WebRoutes.TextMatches.ById)]
|
||||
Task<TextMatchData?> GetById([Path] long id, CancellationToken cancellationToken = default);
|
||||
|
||||
[Get(WebRoutes.TextMatches.ByUserId)]
|
||||
Task<TextMatchData[]> 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);
|
||||
}
|
||||
@ -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<long> Create([Body] CreateTextSubscriptionRequest request, CancellationToken cancellationToken = default);
|
||||
|
||||
[Get(WebRoutes.TextSubscriptions.ById)]
|
||||
Task<TextSubscriptionData?> GetById([Path] long id, CancellationToken cancellationToken = default);
|
||||
|
||||
[Get(WebRoutes.TextSubscriptions.ByUserId)]
|
||||
Task<TextSubscriptionData[]> 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);
|
||||
}
|
||||
@ -1,6 +1,8 @@
|
||||
using Nocr.TextMatcher.Contracts;
|
||||
|
||||
namespace Nocr.TextMatcher.Api.Contracts.TextMatches.Requests;
|
||||
|
||||
public class CreateTextMatchRequest
|
||||
public class CreateTextSubscriptionRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Идентификатор пользователя в системе
|
||||
@ -20,5 +22,5 @@ public class CreateTextMatchRequest
|
||||
/// <summary>
|
||||
/// Тип совпадения
|
||||
/// </summary>
|
||||
public TextMatchRule Rule { get; set; }
|
||||
public TextSubscriptionRule Rule { get; set; }
|
||||
}
|
||||
@ -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";
|
||||
|
||||
|
||||
11
src/Nocr.TextMatcher.AppServices/Constants.cs
Normal file
11
src/Nocr.TextMatcher.AppServices/Constants.cs
Normal file
@ -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";
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,8 @@
|
||||
<ProjectReference Include="..\Nocr.TextMatcher.Api.Contracts\Nocr.TextMatcher.Api.Contracts.csproj" />
|
||||
<ProjectReference Include="..\Nocr.TextMatcher.Async.Api.Contracts\Nocr.TextMatcher.Async.Api.Contracts.csproj" />
|
||||
<ProjectReference Include="..\Nocr.TextMatcher.Core\Nocr.TextMatcher.Core.csproj" />
|
||||
<ProjectReference Include="..\Nocr.TextMatcher.Persistence\Nocr.TextMatcher.Persistence.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
|
||||
@ -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<MessageReceivedHandler>();
|
||||
services.AddScoped<ITextMatchService, TextMatchService>();
|
||||
services.AddSingleton<ITextMatchRepository, InMemoryTextMatchRepository>();
|
||||
services.AddScoped<ITextSubscriptionsService, TextSubscriptionsService>();
|
||||
services.AddScoped<ITextSubscriptionRepository, TextSubscriptionRepository>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
namespace Nocr.TextMatcher.AppServices.TextMatches.Repositories;
|
||||
|
||||
public interface ITextMatchRepository
|
||||
{
|
||||
Task<long> Create(TextMatch textMatch, CancellationToken cancellationToken = default);
|
||||
|
||||
Task Delete(long id, CancellationToken cancellationToken = default);
|
||||
|
||||
Task<IReadOnlyCollection<TextMatch>> Get(CancellationToken cancellationToken = default);
|
||||
|
||||
Task<IReadOnlyCollection<TextMatch>> GetByUserId(long userId, CancellationToken cancellationToken = default);
|
||||
|
||||
Task<TextMatch?> GetById(long id, CancellationToken cancellationToken = default);
|
||||
|
||||
Task Update(TextMatch textMatch, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
namespace Nocr.TextMatcher.AppServices.TextMatches.Repositories;
|
||||
|
||||
public interface ITextSubscriptionRepository
|
||||
{
|
||||
Task<long> Create(TextSubscription textSubscription, CancellationToken cancellationToken = default);
|
||||
|
||||
Task Delete(long id, CancellationToken cancellationToken = default);
|
||||
|
||||
Task<IReadOnlyCollection<TextSubscription>> Get(CancellationToken cancellationToken = default);
|
||||
|
||||
Task<IReadOnlyCollection<TextSubscription>> GetByUserId(long userId, CancellationToken cancellationToken = default);
|
||||
|
||||
Task<TextSubscription?> GetById(long id, CancellationToken cancellationToken = default);
|
||||
|
||||
Task Update(TextSubscription subscription, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@ -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<TextMatch> _textMatches = new List<TextMatch>();
|
||||
|
||||
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<long> 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<IReadOnlyCollection<TextMatch>> Get(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult<IReadOnlyCollection<TextMatch>>(_textMatches);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyCollection<TextMatch>> GetByUserId(long userId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult<IReadOnlyCollection<TextMatch>>(_textMatches.Where(x => x.UserId == userId).ToArray());
|
||||
}
|
||||
|
||||
public Task<TextMatch?> 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;
|
||||
}
|
||||
}
|
||||
@ -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<long> 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<IReadOnlyCollection<TextSubscription>> Get(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _db.TextSubscriptions.AsNoTracking().ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<TextSubscription>> GetByUserId(long userId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _db.TextSubscriptions.Where(x => x.UserId == userId).AsNoTracking().ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<TextSubscription?> 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);
|
||||
}
|
||||
}
|
||||
@ -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<long> Create(long userId, string chatUsername, string template, TextMatchRule rule,
|
||||
Task<long> Create(long userId, string chatUsername, string template, TextSubscriptionRule rule,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<TextMatchData?> GetById(long id, CancellationToken cancellationToken = default);
|
||||
Task<TextSubscriptionData?> 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<IReadOnlyCollection<TextMatchData>> Get(CancellationToken cancellationToken = default);
|
||||
Task<IReadOnlyCollection<TextSubscriptionData>> Get(CancellationToken cancellationToken = default);
|
||||
|
||||
Task<IReadOnlyCollection<TextMatchData>> GetByUserId(long userId, CancellationToken cancellationToken = default);
|
||||
Task<IReadOnlyCollection<TextSubscriptionData>> GetByUserId(long userId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@ -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<long> Create(long userId, string chatUsername, string template, TextMatchRule rule,
|
||||
public async Task<long> 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<TextMatchData?> GetById(long id, CancellationToken cancellationToken)
|
||||
public async Task<TextSubscriptionData?> 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<IReadOnlyCollection<TextMatchData>> Get(CancellationToken cancellationToken = default)
|
||||
public async Task<IReadOnlyCollection<TextSubscriptionData>> Get(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var matches = await _repository.Get(cancellationToken);
|
||||
|
||||
return matches.Select(MapToTextMatchData).ToArray();
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<TextMatchData>> GetByUserId(long userId,
|
||||
public async Task<IReadOnlyCollection<TextSubscriptionData>> 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
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -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<MessageReceived>
|
||||
{
|
||||
private readonly ILogger<MessageReceivedHandler> _logger;
|
||||
private readonly IBus _bus;
|
||||
private readonly ITextMatchRepository _textMatchService;
|
||||
private readonly ITextSubscriptionRepository _textSubscriptionService;
|
||||
private readonly ICurrentDateProvider _dateProvider;
|
||||
|
||||
public MessageReceivedHandler(ILogger<MessageReceivedHandler> 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<MessageReceived>
|
||||
{
|
||||
_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<MessageReceived>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
namespace Nocr.TextMatcher.Async.Api.Contracts;
|
||||
|
||||
public class TextMatchCreated : IEvent
|
||||
public class TextSubscriptionCreated : IEvent
|
||||
{
|
||||
public Guid Id => Guid.NewGuid();
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
namespace Nocr.TextMatcher.Async.Api.Contracts;
|
||||
|
||||
public class TextMatchMatched : IEvent
|
||||
public class TextSubscriptionMatched : IEvent
|
||||
{
|
||||
public Guid Id => Guid.NewGuid();
|
||||
|
||||
/// <summary>
|
||||
/// Идентификатор матча
|
||||
/// </summary>
|
||||
public long MatchId { get; set; }
|
||||
public long SubscriptionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Идентификатор владельца матча
|
||||
/// </summary>
|
||||
public long MatchUserId { get; set; }
|
||||
public long SubscriptionUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Username чата
|
||||
@ -0,0 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<IsPackable>true</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@ -1,6 +1,6 @@
|
||||
namespace Nocr.TextMatcher.Api.Contracts.TextMatches;
|
||||
namespace Nocr.TextMatcher.Contracts;
|
||||
|
||||
public enum TextMatchRule
|
||||
public enum TextSubscriptionRule
|
||||
{
|
||||
// Substring
|
||||
Full = 1,
|
||||
@ -2,11 +2,28 @@ namespace Nocr.TextMatcher.Core.Options;
|
||||
|
||||
public sealed class RebusRabbitMqOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Строка подключение
|
||||
/// </summary>
|
||||
public string ConnectionString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Имя входящей очереди
|
||||
/// </summary>
|
||||
public string InputQueueName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Имя direct exchange
|
||||
/// </summary>
|
||||
public string DirectExchangeName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Имя topics exchange
|
||||
/// </summary>
|
||||
public string TopicsExchangeName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Список подписок
|
||||
/// </summary>
|
||||
public string[] Subscriptions { get; set; } = Array.Empty<string>();
|
||||
}
|
||||
12
src/Nocr.TextMatcher.Domain/Nocr.TextMatcher.Domain.csproj
Normal file
12
src/Nocr.TextMatcher.Domain/Nocr.TextMatcher.Domain.csproj
Normal file
@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
|
||||
<PropertyGroup>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nocr.TextMatcher.Contracts\Nocr.TextMatcher.Contracts.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@ -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:
|
||||
@ -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<long> 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<TextMatchData?> 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<IReadOnlyCollection<TextMatchData>> 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);
|
||||
}
|
||||
}
|
||||
@ -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<long> 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<TextSubscriptionData?> 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<IReadOnlyCollection<TextSubscriptionData>> 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);
|
||||
}
|
||||
}
|
||||
@ -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<TextMatcherContext>(
|
||||
(ctx, context) =>
|
||||
{
|
||||
context.UseMySql(Configuration.GetConnectionString(nameof(TextMatcherContext)),
|
||||
new MariaDbServerVersion(MariaDbServerVersion.LatestSupportedServerVersion),
|
||||
builder => builder.MigrationsAssembly(typeof(TextMatcherContext).Assembly.FullName))
|
||||
.UseLoggerFactory(ctx.GetRequiredService<ILoggerFactory>());
|
||||
}
|
||||
);
|
||||
|
||||
services.Configure<RebusRabbitMqOptions>(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<IOptions<RebusRabbitMqOptions>>();
|
||||
var bus = app.ApplicationServices.GetRequiredService<IBus>();
|
||||
bus.Advanced.Topics.Subscribe("nocr.telegram.listener").GetAwaiter().GetResult();
|
||||
foreach (var subscription in rabbitMqOptions.Value.Subscriptions)
|
||||
{
|
||||
bus.Advanced.Topics.Subscribe(subscription).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -19,6 +19,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nocr.TextMatcher.Api.Contracts\Nocr.TextMatcher.Api.Contracts.csproj" />
|
||||
<ProjectReference Include="..\Nocr.TextMatcher.AppServices\Nocr.TextMatcher.AppServices.csproj" />
|
||||
<ProjectReference Include="..\Nocr.TextMatcher.Persistence\Nocr.TextMatcher.Persistence.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,9 @@
|
||||
"RebusRabbitMqOptions": {
|
||||
"InputQueueName": "nocr.text.matcher.queue",
|
||||
"DirectExchangeName": "nocr.direct",
|
||||
"TopicsExchangeName": "nocr.topics"
|
||||
"TopicsExchangeName": "nocr.topics",
|
||||
"Subscriptions": [
|
||||
"nocr.telegram.listener"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Nocr.TextMatcher.Persistence;
|
||||
|
||||
public class DesignTimeTextMatcherContextFactory : IDesignTimeDbContextFactory<TextMatcherContext>
|
||||
{
|
||||
public TextMatcherContext CreateDbContext(string[] args)
|
||||
{
|
||||
var optionsBuilder = new DbContextOptionsBuilder<TextMatcherContext>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
65
src/Nocr.TextMatcher.Persistence/Migrations/20240328201810_InitialMigration.Designer.cs
generated
Normal file
65
src/Nocr.TextMatcher.Persistence/Migrations/20240328201810_InitialMigration.Designer.cs
generated
Normal file
@ -0,0 +1,65 @@
|
||||
// <auto-generated />
|
||||
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
|
||||
{
|
||||
/// <inheritdoc />
|
||||
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<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("ChatUsername")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("varchar(1024)");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedDateTime")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("Rule")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Template")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("varchar(1024)");
|
||||
|
||||
b.Property<long>("UserId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("TextSubscriptions");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Nocr.TextMatcher.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitialMigration : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterDatabase()
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "TextSubscriptions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
UserId = table.Column<long>(type: "bigint", nullable: false),
|
||||
ChatUsername = table.Column<string>(type: "varchar(1024)", maxLength: 1024, nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Template = table.Column<string>(type: "varchar(1024)", maxLength: 1024, nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Rule = table.Column<int>(type: "int", nullable: false),
|
||||
Active = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
CreatedDateTime = table.Column<DateTimeOffset>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_TextSubscriptions", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "TextSubscriptions");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
// <auto-generated />
|
||||
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<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("ChatUsername")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("varchar(1024)");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedDateTime")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("Rule")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Template")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("varchar(1024)");
|
||||
|
||||
b.Property<long>("UserId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("TextSubscriptions");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nocr.TextMatcher.Domain\Nocr.TextMatcher.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="appsettings.json" />
|
||||
<Content Include="appsettings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
25
src/Nocr.TextMatcher.Persistence/TextMatcherContext.cs
Normal file
25
src/Nocr.TextMatcher.Persistence/TextMatcherContext.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Nocr.TextMatcher.AppServices.TextMatches;
|
||||
|
||||
namespace Nocr.TextMatcher.Persistence;
|
||||
|
||||
public class TextMatcherContext : DbContext
|
||||
{
|
||||
public DbSet<TextSubscription> 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);
|
||||
}
|
||||
}
|
||||
@ -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<TextSubscription>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<TextSubscription> builder)
|
||||
{
|
||||
builder.HasKey(x => x.Id);
|
||||
builder.Property(x=> x.ChatUsername).IsRequired().HasMaxLength(1024);
|
||||
builder.Property(x => x.Template).IsRequired().HasMaxLength(1024);
|
||||
}
|
||||
}
|
||||
5
src/Nocr.TextMatcher.Persistence/appsettings.json
Normal file
5
src/Nocr.TextMatcher.Persistence/appsettings.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"MariaLocal": "server=localhost;port=3306;database=nocr_text_matcher;uid=root;pwd=toor"
|
||||
}
|
||||
}
|
||||
@ -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");
|
||||
Loading…
Reference in New Issue
Block a user