Skip to content

Commit

Permalink
Add message configuration (#48)
Browse files Browse the repository at this point in the history
Close #30

Реализовал возможность задать шаблон сообщения в конфигурационном файле,
поддержал замену в этом шаблоне подстрок {value.Title} и {value.Url} на
информацию из issue / pull request. Добавил тесты, добавил базово
документацию.
  • Loading branch information
FrediKats authored Jul 20, 2023
1 parent f40145b commit d5d59cd
Show file tree
Hide file tree
Showing 12 changed files with 201 additions and 13 deletions.
36 changes: 36 additions & 0 deletions Issueneter.Host/Modules/ScanModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Issueneter.Domain.Models;
using Issueneter.Host.Composition;
using Issueneter.Runner;
using Issueneter.Telegram;
using Issueneter.Telegram.Formatters;

namespace Issueneter.Host.Modules;

public static class ScanModule
{
public static IServiceCollection AddScanModule(this IServiceCollection services, IHostEnvironment env, IConfigurationRoot configuration) =>
services
.AddSingleton(GetIssueFormatter)
.AddSingleton(GetPullRequestFormatter)
.AddSingleton<ScanRunner>();

private static IMessageFormatter<Issue> GetIssueFormatter(IServiceProvider serviceProvider)
{
var options = serviceProvider.GetRequiredOptions<TelegramOptions>();

if (options.IssueMessageTemplate is not null)
return new ConfigurableMessageFormatter<Issue>(options.IssueMessageTemplate);

return new IssueMessageFormatter();
}

private static IMessageFormatter<PullRequest> GetPullRequestFormatter(IServiceProvider serviceProvider)
{
var options = serviceProvider.GetRequiredOptions<TelegramOptions>();

if (options.PullRequestMessageTemplate is not null)
return new ConfigurableMessageFormatter<PullRequest>(options.PullRequestMessageTemplate);

return new PullRequestMessageFormatter();
}
}
2 changes: 1 addition & 1 deletion Issueneter.Host/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
.AddHangfireModule(env, config)
.AddSwaggerModule(env, config)
.AddTelegramModule(env, config)
.AddSingleton<ScanRunner>();
.AddScanModule(env, config);

var app = builder.Build();

Expand Down
6 changes: 4 additions & 2 deletions Issueneter.Host/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
"Token" : null
},

"TelegramOptions" : {
"Token": null
"TelegramOptions": {
"Token": null,
"IssueMessageTemplate": "изи [ишуя]({value.Url})",
"PullRequestMessageTemplate": "[пр]({entity.Url})"
}
}
19 changes: 9 additions & 10 deletions Issueneter.Runner/ScanRunner.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
using Issueneter.Domain;
using Issueneter.Domain.Models;
using Issueneter.Filters;
using Issueneter.Domain.Models;
using Issueneter.Github;
using Issueneter.Json;
using Issueneter.Mappings;
using Issueneter.Persistence;
using Issueneter.Telegram;
using Issueneter.Telegram.Formatters;
using Newtonsoft.Json;

namespace Issueneter.Runner;
Expand All @@ -16,12 +13,16 @@ public class ScanRunner
private readonly TelegramSender _sender;
private readonly GithubApiService _github;
private readonly ScanStorage _storage;

public ScanRunner(TelegramSender sender, GithubApiService github, ScanStorage storage)
private readonly IMessageFormatter<Issue> _issueFormatter;
private readonly IMessageFormatter<PullRequest> _pullRequestFormatter;

public ScanRunner(TelegramSender sender, GithubApiService github, ScanStorage storage, IMessageFormatter<Issue> issueMessageIssueFormatter, IMessageFormatter<PullRequest> pullRequestMessageFormatter)
{
_sender = sender;
_github = github;
_storage = storage;
_issueFormatter = issueMessageIssueFormatter;
_pullRequestFormatter = pullRequestMessageFormatter;
}

public async Task Run(long scanId)
Expand All @@ -34,21 +35,19 @@ public async Task Run(long scanId)
var source = new ActivitySource(scan.Owner, scan.Repo);
if (scan.ScanType == ScanType.Issue)
{
var formatter = new IssueMessageFormatter();
var issues = await _github.GetIssues(DateTimeOffset.Now - TimeSpan.FromMinutes(30), source);

var filters = JsonConvert.DeserializeObject<String>(scan.Filters);
var rootFilter = IssueneterJsonSerializer.Deserialize<Issue>(filters);
var results = issues.Where(k => rootFilter.Apply(k)).ToArray();
await _sender.SendResults(scan.ChatId, formatter, results);
await _sender.SendResults(scan.ChatId, _issueFormatter, results);
}
else
{
var issues = await _github.GetPullRequests(DateTimeOffset.Now - TimeSpan.FromMinutes(30), source);
var rootFilter = IssueneterJsonSerializer.Deserialize<PullRequest>(scan.Filters);
var results = issues.Where(k => rootFilter.Apply(k)).ToArray();
var formatter = new PullRequestMessageFormatter();
await _sender.SendResults(scan.ChatId, formatter, results);
await _sender.SendResults(scan.ChatId, _pullRequestFormatter, results);
}
}
}
24 changes: 24 additions & 0 deletions Issueneter.Telegram/Formatters/ConfigurableMessageFormatter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Issueneter.Annotation;
using Issueneter.Mappings;

namespace Issueneter.Telegram.Formatters;

public class ConfigurableMessageFormatter<T> : IMessageFormatter<T> where T : IFilterable
{
private readonly string _template;

public ConfigurableMessageFormatter(string template)
{
_template = template;
}

public string ToMessage(T entity)
{
var result = _template;

foreach (string property in ModelsInfo.AvailableScanSources[T.ScanType])
result = result.Replace($"{{value.{property}}}", entity.GetProperty(property));

return result;
}
}
2 changes: 2 additions & 0 deletions Issueneter.Telegram/TelegramOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
public class TelegramOptions
{
public string? Token { get; init; }
public string? IssueMessageTemplate { get; init; }
public string? PullRequestMessageTemplate { get; init; }
}
1 change: 1 addition & 0 deletions Issueneter.Tests/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using NUnit.Framework;
37 changes: 37 additions & 0 deletions Issueneter.Tests/IssueConfigurableMessageFormatterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.Collections;
using Issueneter.Domain.Models;
using Issueneter.Domain.Utility;
using Issueneter.Telegram.Formatters;

namespace Issueneter.Tests
{
public class IssueConfigurableMessageFormatterTests
{
private static IEnumerable GetTestCases()
{
var dummyRefValue = new Ref<List<TimelineEvent>>(() => Task.FromResult(new List<TimelineEvent>()));

var issue = new Issue(
"Issue title",
"Issue author",
"Url",
IssueState.Opened,
Array.Empty<string>(),
dummyRefValue);

yield return new TestCaseData("Message template", issue, "Message template");
yield return new TestCaseData("Issue {value.Title}", issue, $"Issue {issue.Title}");
yield return new TestCaseData("Issue {value.Url}", issue, $"Issue {issue.Url}");
}

[TestCaseSource(nameof(GetTestCases))]
public void Formatter_ReturnCorrectString(string template, Issue issue, string expected)
{
var formatter = new ConfigurableMessageFormatter<Issue>(template);

var actual = formatter.ToMessage(issue);

Assert.That(actual, Is.EqualTo(expected));
}
}
}
24 changes: 24 additions & 0 deletions Issueneter.Tests/Issueneter.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.4.2" />
<PackageReference Include="NUnit.Analyzers" Version="3.6.1" />
<PackageReference Include="coverlet.collector" Version="3.2.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Issueneter.Telegram\Issueneter.Telegram.csproj" />
</ItemGroup>

</Project>
36 changes: 36 additions & 0 deletions Issueneter.Tests/PullRequestConfigurableMessageFormatterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Collections;
using Issueneter.Domain.Models;
using Issueneter.Domain.Utility;
using Issueneter.Telegram.Formatters;

namespace Issueneter.Tests;

public class PullRequestConfigurableMessageFormatterTests
{
private static IEnumerable GetTestCases()
{
var dummyRefValue = new Ref<List<TimelineEvent>>(() => Task.FromResult(new List<TimelineEvent>()));

var issue = new PullRequest(
"PR title",
"PR author",
"Url",
PullRequestState.Opened,
Array.Empty<string>(),
dummyRefValue);

yield return new TestCaseData("Message template", issue, "Message template");
yield return new TestCaseData("Pull request {value.Title}", issue, $"Pull request {issue.Title}");
yield return new TestCaseData("Pull request {value.Url}", issue, $"Pull request {issue.Url}");
}

[TestCaseSource(nameof(GetTestCases))]
public void Formatter_ReturnCorrectString(string template, PullRequest pullRequest, string expected)
{
var formatter = new ConfigurableMessageFormatter<PullRequest>(template);

var actual = formatter.ToMessage(pullRequest);

Assert.That(actual, Is.EqualTo(expected));
}
}
6 changes: 6 additions & 0 deletions Issueneter.sln
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Issueneter.Annotation", "Ge
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Issueneter.ScanSourcesGenerator", "Generations\Issueneter.ScanSourcesGenerator\Issueneter.ScanSourcesGenerator.csproj", "{8E8BA128-DD99-4367-8502-88EFC1205613}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Issueneter.Tests", "Issueneter.Tests\Issueneter.Tests.csproj", "{B696EBD5-4F35-42BC-8158-618DE4D9CF2F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -68,6 +70,10 @@ Global
{8E8BA128-DD99-4367-8502-88EFC1205613}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8E8BA128-DD99-4367-8502-88EFC1205613}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8E8BA128-DD99-4367-8502-88EFC1205613}.Release|Any CPU.Build.0 = Release|Any CPU
{B696EBD5-4F35-42BC-8158-618DE4D9CF2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B696EBD5-4F35-42BC-8158-618DE4D9CF2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B696EBD5-4F35-42BC-8158-618DE4D9CF2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B696EBD5-4F35-42BC-8158-618DE4D9CF2F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{95386239-56C7-4BB3-A098-258B30404A33} = {4652AD14-6886-4361-B050-8F40F0214E3A}
Expand Down
21 changes: 21 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Issueneter

## Configuration

### Message templates

Для изменения текста сообщений нужно выставить шаблоны в конфигурационном файле:

```
{
"TelegramOptions": {
"IssueMessageTemplate": "изи [ишуя]({value.Url})",
"PullRequestMessageTemplate": "[пр]({entity.Url})"
}
}
```

Шаблоны поддерживают динамически вычисляемые поля. Для того, чтобы в шаблон вставить информацию из issue / pull request нужно указать `{value.*}`, где `*` - это название поля из issue / pull request. Список поддерживаемых динамических полей:

- Title
- Url

0 comments on commit d5d59cd

Please sign in to comment.