Skip to content

Commit aafa106

Browse files
Merge pull request #61 from TransactionProcessing/task/#26_supportreportjob
Added Support Report Job
2 parents 877a5d0 + ed1064f commit aafa106

11 files changed

Lines changed: 319 additions & 51 deletions
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net7.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<ProjectReference Include="..\TransactionProcessing.SchedulerService.Jobs\TransactionProcessing.SchedulerService.Jobs.csproj" />
12+
</ItemGroup>
13+
14+
</Project>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
namespace JobTestDriver
2+
{
3+
using MessagingService.Client;
4+
using TransactionProcessing.SchedulerService.Jobs;
5+
6+
internal class Program{
7+
static async Task Main(string[] args){
8+
//List<(String groupName, String streamName, Int64 parkedMessageCount)>? info = await Jobs.GetParkedQueueInformation("esdb://admin:changeit@192.168.0.133:2113?tls=false&tlsVerifyCert=false", CancellationToken.None);
9+
//foreach ((String groupName, String streamName, Int64 parkedMessageCount) infoItem in info){
10+
// Console.WriteLine($"Group: {infoItem.groupName} Stream: {infoItem.streamName} Parked Count: {infoItem.parkedMessageCount}");
11+
//}
12+
13+
//List<(Guid, String, DateTime, String)>? incompleteFiles = await Jobs.GetIncompleteFileList("server=192.168.0.133;user id=sa;password=Sc0tland;database=EstateReportingReadModel<estateid>;Encrypt=True;TrustServerCertificate=True", CancellationToken.None);
14+
//foreach ((Guid, String, DateTime, String) incompleteFile in incompleteFiles){
15+
// Console.WriteLine($"FileId: {incompleteFile.Item1} Location: {incompleteFile.Item2} Rcvd: {incompleteFile.Item3} Merchant: {incompleteFile.Item4}");
16+
//}
17+
HttpClient client = new HttpClient();
18+
IMessagingServiceClient messagingServiceClient = new MessagingServiceClient(delegate(String s){ return "http://127.0.0.1:5006";}, client);
19+
String accessToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjQzOUMxRDk5MDUwQTYyMDhEM0U5M0JFMjlBQUJBNzI5IiwidHlwIjoiYXQrand0In0.eyJpc3MiOiJodHRwczovLzEyNy4wLjAuMTo1MDAxIiwibmJmIjoxNjgzMTA2NDE2LCJpYXQiOjE2ODMxMDY0MTYsImV4cCI6MTY4MzExMDAxNiwiYXVkIjpbImVzdGF0ZU1hbmFnZW1lbnQiLCJlc3RhdGVSZXBvcnRpbmciLCJmaWxlUHJvY2Vzc29yIiwibWVzc2FnaW5nU2VydmljZSIsInRyYW5zYWN0aW9uUHJvY2Vzc29yIiwidHJhbnNhY3Rpb25Qcm9jZXNzb3JBQ0wiLCJ2b3VjaGVyTWFuYWdlbWVudCIsImh0dHBzOi8vMTI3LjAuMC4xOjUwMDEvcmVzb3VyY2VzIl0sInNjb3BlIjpbImVzdGF0ZU1hbmFnZW1lbnQiLCJlc3RhdGVSZXBvcnRpbmciLCJmaWxlUHJvY2Vzc29yIiwibWVzc2FnaW5nU2VydmljZSIsInRyYW5zYWN0aW9uUHJvY2Vzc29yIiwidHJhbnNhY3Rpb25Qcm9jZXNzb3JBQ0wiLCJ2b3VjaGVyTWFuYWdlbWVudCJdLCJjbGllbnRfaWQiOiJzZXJ2aWNlQ2xpZW50IiwianRpIjoiMDZBMUI4NzYyRjFGNDJGNkIwMzM5RTYwRTk2MkVDQkUifQ.G5pFWRJF430ZZxnGO_yIxEC6Zj81LRr3HNq6d8V9EV4Pswp5YO7hZ867Ln4mjnrYag4lGI4cpT5S6646r9KNdZMiLOsdQs2LEJPuUjEdVADIwm8rcdqT8OX-sC6uGA6VL0bMmYWQXw1E8d4kax444I6jeNquLjpoWVD1BDp9L1zzC6e_k9W7Fc9MQOogOqO82TXrBl9nkpBbmJQ0HDiub2yVUTUKLwkCeRfBDlyeU8tyNE7kH6IGdHIL_WYUtiiRYjBJ2PNLzTtrXQk4rqw6GB-25K2qcgP5FO0MI675tAkuPKI0DaySXHnAjYssW8wZYy0tkaJL0OIlmOUe-9jM6g";
20+
await Jobs.SendSupportEmail(DateTime.Now,
21+
accessToken,
22+
"esdb://admin:changeit@192.168.0.133:2113?tls=false&tlsVerifyCert=false",
23+
"server=192.168.0.133;user id=sa;password=Sc0tland;database=EstateReportingReadModel<estateid>;Encrypt=True;TrustServerCertificate=True",
24+
new List<String>{
25+
"435613ac-a468-47a3-ac4f-649d89764c22"
26+
},
27+
messagingServiceClient,
28+
CancellationToken.None);
29+
30+
}
31+
}
32+
}

‎TransactionProcessing.SchedulerService/TransactionProcessing.SchedulerService.Jobs/Bootstrapper.cs‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using DataGeneration;
88
using EstateManagement.Client;
99
using EstateManagement.DataTransferObjects.Responses;
10+
using MessagingService.Client;
1011
using Microsoft.Extensions.DependencyInjection;
1112
using Quartz;
1213
using SecurityService.Client;
@@ -37,6 +38,7 @@ public static void ConfigureServices(IJobExecutionContext jobExecutionContext){
3738

3839
Bootstrapper.Services.AddSingleton(httpClient);
3940
Bootstrapper.Services.AddSingleton<ISecurityServiceClient, SecurityServiceClient>();
41+
Bootstrapper.Services.AddSingleton<IMessagingServiceClient, MessagingServiceClient>();
4042
Bootstrapper.Services.AddSingleton<IEstateClient, EstateClient>();
4143
Bootstrapper.Services.AddSingleton<ITransactionProcessorClient, TransactionProcessorClient>();
4244
Bootstrapper.Services.AddSingleton<Func<String, String>>(container => serviceName => { return jobExecutionContext.MergedJobDataMap.GetString(serviceName); });
@@ -76,9 +78,4 @@ protected async Task<String> GetToken(String clientId, String clientSecret, Canc
7678

7779
return token.AccessToken;
7880
}
79-
80-
protected async Task<MerchantResponse> GetMerchant(String accessToken, Guid estateId, Guid merchantId, CancellationToken cancellationToken){
81-
IEstateClient estateClient = Bootstrapper.GetService<IEstateClient>();
82-
return await estateClient.GetMerchant(accessToken, estateId, merchantId, cancellationToken);
83-
}
8481
}

‎TransactionProcessing.SchedulerService/TransactionProcessing.SchedulerService.Jobs/GenerateFileUploadsJob.cs‎

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using DataGeneration;
77
using EstateManagement.DataTransferObjects.Responses;
88
using Quartz;
9+
using Quartz.Logging;
910

1011
[DisallowConcurrentExecution]
1112
public class GenerateFileUploadsJob : BaseJob, IJob
@@ -19,21 +20,10 @@ public async Task Execute(IJobExecutionContext context)
1920
String clientSecret = context.MergedJobDataMap.GetString("ClientSecret");
2021
Guid estateId = context.MergedJobDataMap.GetGuidValueFromString("EstateId");
2122
Guid merchantId = context.MergedJobDataMap.GetGuidValueFromString("MerchantId");
22-
23-
String accessToken = await this.GetToken(clientId,
24-
clientSecret,
25-
context.CancellationToken);
2623

2724
ITransactionDataGenerator t = CreateTransactionDataGenerator(clientId, clientSecret, RunningMode.Live);
2825

29-
MerchantResponse merchant = await this.GetMerchant(accessToken, estateId, merchantId, context.CancellationToken);
30-
31-
List<ContractResponse> contracts = await t.GetMerchantContracts(merchant, context.CancellationToken);
32-
DateTime fileDate = DateTime.Now;
33-
foreach (ContractResponse contract in contracts){
34-
// Generate a file and upload
35-
await t.SendUploadFile(fileDate, contract, merchant, context.CancellationToken);
36-
}
26+
await Jobs.GenerateFileUploads(t, estateId, merchantId, context.CancellationToken);
3727
}
3828

3929

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
namespace TransactionProcessing.SchedulerService.Jobs;
22

33
using System;
4-
using System.Collections.Generic;
54
using System.Threading.Tasks;
65
using DataGeneration;
7-
using EstateManagement.DataTransferObjects.Responses;
86
using Quartz;
97

108
public class GenerateMerchantStatementJob : BaseJob, IJob
@@ -18,9 +16,21 @@ public async Task Execute(IJobExecutionContext context)
1816

1917
ITransactionDataGenerator t = this.CreateTransactionDataGenerator(clientId, clientSecret, RunningMode.Live);
2018

21-
List<MerchantResponse> merchants = await t.GetMerchants(estateId, context.CancellationToken);
22-
foreach (MerchantResponse merchantResponse in merchants){
23-
await t.GenerateMerchantStatement(merchantResponse.EstateId, merchantResponse.MerchantId, DateTime.Now, context.CancellationToken);
24-
}
19+
await Jobs.GenerateMerchantStatements(t, estateId, context.CancellationToken);
2520
}
21+
}
22+
23+
public class SupportReportJob : BaseJob, IJob
24+
{
25+
public async Task Execute(IJobExecutionContext context)
26+
{
27+
Bootstrapper.ConfigureServices(context);
28+
29+
String eventStoreAddress = context.MergedJobDataMap.GetString("EventStoreAddress");
30+
String databaseConnectionString = context.MergedJobDataMap.GetString("DatabaseConnectionString");
31+
32+
// Events in Parked Queues
33+
// Incomplete Files
34+
}
35+
2636
}

‎TransactionProcessing.SchedulerService/TransactionProcessing.SchedulerService.Jobs/GenerateTransactionsJob.cs‎

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -38,33 +38,7 @@ public async Task Execute(IJobExecutionContext context){
3838

3939
ITransactionDataGenerator t = CreateTransactionDataGenerator(clientId, clientSecret, RunningMode.Live);
4040

41-
// get a token
42-
String accessToken = await this.GetToken(clientId,
43-
clientSecret,
44-
context.CancellationToken);
45-
46-
// get the merchant
47-
MerchantResponse merchant = await this.GetMerchant(accessToken, estateId, merchantId, context.CancellationToken);
48-
49-
DateTime transactionDate = DateTime.Now;
50-
51-
if (requireLogon){
52-
// Do a logon transaction for the merchant
53-
await t.PerformMerchantLogon(transactionDate, merchant, context.CancellationToken);
54-
}
55-
56-
// Get the merchants contracts
57-
List<ContractResponse> contracts = await t.GetMerchantContracts(merchant, context.CancellationToken);
58-
59-
foreach (ContractResponse contract in contracts){
60-
// Generate and send some sales
61-
await t.SendSales(transactionDate, merchant, contract, context.CancellationToken);
62-
63-
// Generate a file and upload
64-
await t.SendUploadFile(transactionDate, contract, merchant, context.CancellationToken);
65-
}
66-
67-
Console.WriteLine($"Logon sent for Merchant [{merchant.MerchantName}]");
41+
await Jobs.GenerateTransactions(t, estateId, merchantId, requireLogon, context.CancellationToken);
6842
}
6943
catch(Exception e){
7044
// TODO: Log the error
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
namespace TransactionProcessing.SchedulerService.Jobs;
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Data;
6+
using System.Linq;
7+
using System.Net.Http;
8+
using System.Text;
9+
using System.Threading;
10+
using System.Threading.Tasks;
11+
using DataGeneration;
12+
using EstateManagement.DataTransferObjects.Responses;
13+
using EventStore.Client;
14+
using MessagingService.Client;
15+
using MessagingService.DataTransferObjects;
16+
using Microsoft.Data.SqlClient;
17+
using Newtonsoft.Json;
18+
19+
public static class Jobs{
20+
public static async Task GenerateMerchantStatements(ITransactionDataGenerator t, Guid estateId, CancellationToken cancellationToken){
21+
List<MerchantResponse> merchants = await t.GetMerchants(estateId, cancellationToken);
22+
foreach (MerchantResponse merchantResponse in merchants)
23+
{
24+
await t.GenerateMerchantStatement(merchantResponse.EstateId, merchantResponse.MerchantId, DateTime.Now, cancellationToken);
25+
}
26+
}
27+
28+
public static async Task GenerateFileUploads(ITransactionDataGenerator t, Guid estateId, Guid merchantId, CancellationToken cancellationToken)
29+
{
30+
MerchantResponse merchant = await t.GetMerchant( estateId, merchantId, cancellationToken);
31+
32+
List<ContractResponse> contracts = await t.GetMerchantContracts(merchant, cancellationToken);
33+
DateTime fileDate = DateTime.Now;
34+
foreach (ContractResponse contract in contracts)
35+
{
36+
// Generate a file and upload
37+
await t.SendUploadFile(fileDate, contract, merchant, cancellationToken);
38+
}
39+
}
40+
41+
public static async Task GenerateTransactions(ITransactionDataGenerator t, Guid estateId, Guid merchantId, Boolean requireLogon, CancellationToken cancellationToken)
42+
{
43+
// get the merchant
44+
MerchantResponse merchant = await t.GetMerchant(estateId, merchantId, cancellationToken);
45+
46+
DateTime transactionDate = DateTime.Now;
47+
48+
if (requireLogon)
49+
{
50+
// Do a logon transaction for the merchant
51+
await t.PerformMerchantLogon(transactionDate, merchant, cancellationToken);
52+
}
53+
54+
// Get the merchants contracts
55+
List<ContractResponse> contracts = await t.GetMerchantContracts(merchant, cancellationToken);
56+
57+
foreach (ContractResponse contract in contracts)
58+
{
59+
// Generate and send some sales
60+
await t.SendSales(transactionDate, merchant, contract, cancellationToken);
61+
62+
// Generate a file and upload
63+
await t.SendUploadFile(transactionDate, contract, merchant, cancellationToken);
64+
}
65+
}
66+
67+
public static async Task PerformSettlement(ITransactionDataGenerator t, DateTime dateTime, Guid estateId, CancellationToken cancellationToken)
68+
{
69+
await t.PerformSettlement(dateTime, estateId, cancellationToken);
70+
}
71+
72+
public static async Task<List<(String groupName, String streamName, Int64 parkedMessageCount)>> GetParkedQueueInformation(String eventStoreConnectionString, CancellationToken cancellationToken){
73+
EventStoreClientSettings clientSettings = EventStoreClientSettings.Create(eventStoreConnectionString);
74+
EventStore.Client.EventStorePersistentSubscriptionsClient client = new EventStorePersistentSubscriptionsClient(clientSettings);
75+
List<(String groupName, String streamName, Int64 parkedMessageCount)> result = new List<(String groupName, String streamName, Int64 parkedMessageCount)>();
76+
IEnumerable<PersistentSubscriptionInfo> x = await client.ListAllAsync(cancellationToken:cancellationToken);
77+
foreach (PersistentSubscriptionInfo persistentSubscriptionInfo in x){
78+
if (persistentSubscriptionInfo.Stats.ParkedMessageCount > 0){
79+
// Add to replay list
80+
result.Add((persistentSubscriptionInfo.GroupName, persistentSubscriptionInfo.EventSource, persistentSubscriptionInfo.Stats.ParkedMessageCount));
81+
}
82+
}
83+
84+
return result;
85+
}
86+
87+
public static async Task<List<(Guid,String,DateTime,String)>> GetIncompleteFileList(String databaseConnectionString,CancellationToken cancellationToken){
88+
List<(Guid, String, DateTime, String)> result = new List<(Guid, String, DateTime, String)>();
89+
90+
using (SqlConnection connection = new SqlConnection(databaseConnectionString)){
91+
await connection.OpenAsync(cancellationToken);
92+
93+
SqlCommand command = connection.CreateCommand();
94+
command.CommandText = "select FileId, FileLocation, FileReceivedDateTime, merchant.Name from [file] f inner join merchant on merchant.MerchantId = f.MerchantId where IsCompleted= 0";
95+
command.CommandType = CommandType.Text;
96+
97+
var reader = await command.ExecuteReaderAsync(cancellationToken);
98+
99+
while (await reader.ReadAsync(cancellationToken)){
100+
101+
var fileId = reader.GetGuid(0);
102+
var fileLocation = reader.GetString(1);
103+
var fileReceivedDateTime = reader.GetDateTime(2);
104+
var merchantName = reader.GetString(3);
105+
106+
result.Add((fileId, fileLocation, fileReceivedDateTime, merchantName));
107+
}
108+
}
109+
110+
return result;
111+
}
112+
113+
public static SendEmailRequest BuildSupportEmail(DateTime dateTime,
114+
List<(Guid fileId, String fileLocation, DateTime fileReceivedDateTime, String merchantName)> incompleteFiles,
115+
List<(String groupName, String streamName, Int64 parkedMessageCount)> parkedQueueInformation)
116+
{
117+
// Build uo the HTML String
118+
StringBuilder htmlBuilder = new StringBuilder();
119+
120+
htmlBuilder.AppendLine("<html>");
121+
htmlBuilder.AppendLine("<head>");
122+
htmlBuilder.AppendLine("<style>");
123+
htmlBuilder.AppendLine("table, th, td { border: 1px solid black; }");
124+
htmlBuilder.AppendLine("</style>");
125+
htmlBuilder.AppendLine("</head>");
126+
127+
htmlBuilder.AppendLine("<body>");
128+
129+
htmlBuilder.AppendLine($"<h1>Daily Support Report for {dateTime.ToString("dd-MM-yyyy")}</h1>");
130+
131+
htmlBuilder.AppendLine("<h2>Parked Messages Stats</h2>");
132+
133+
if (parkedQueueInformation.Any() == false){
134+
htmlBuilder.AppendLine("<p>No Data</p>");
135+
}
136+
else{
137+
htmlBuilder.AppendLine("<table>");
138+
htmlBuilder.Append("<tr><th>Group</th><th>Stream</th><th>Parked Messages</th></tr>");
139+
140+
foreach ((String groupName, String streamName, Int64 parkedMessageCount) info in parkedQueueInformation){
141+
htmlBuilder.Append($"<tr><td>{info.groupName}</td><td>{info.streamName}</td><td>{info.parkedMessageCount}</td></tr>");
142+
}
143+
144+
htmlBuilder.AppendLine("</table>");
145+
}
146+
147+
htmlBuilder.AppendLine("<h2>Incomplete Bulk Files</h2>");
148+
149+
if (incompleteFiles.Any() == false)
150+
{
151+
htmlBuilder.AppendLine("<p>No Data</p>");
152+
}
153+
else
154+
{
155+
htmlBuilder.AppendLine("<table>");
156+
htmlBuilder.Append("<tr><th>Id</th><th>Location</th><th>File Received</th><th>Merchant Name</th></tr>");
157+
158+
foreach ((Guid fileId, String fileLocation, DateTime fileReceivedDateTime, String merchantName) incompleteFile in incompleteFiles){
159+
160+
htmlBuilder.Append($"<tr><td>{incompleteFile.fileId}</td><td>{incompleteFile.fileLocation}</td><td>{incompleteFile.fileReceivedDateTime}</td><td>{incompleteFile.merchantName}</td></tr>");
161+
}
162+
163+
htmlBuilder.AppendLine("</table>");
164+
}
165+
166+
htmlBuilder.AppendLine("</body>");
167+
htmlBuilder.AppendLine("</html>");
168+
169+
SendEmailRequest request = new SendEmailRequest{
170+
Body = htmlBuilder.ToString(),
171+
ConnectionIdentifier = Guid.NewGuid(),
172+
FromAddress = "support@transactionprocessing.com",
173+
IsHtml = true,
174+
MessageId = Guid.NewGuid(),
175+
Subject = $"Daily Support Report for {dateTime:dd-MM-yyyy}",
176+
ToAddresses = new List<String>{
177+
"stuart_ferguson1development@outlook.com"
178+
}
179+
};
180+
return request;
181+
}
182+
183+
public static async Task SendSupportEmail(DateTime dateTime,
184+
String accessToken,
185+
String eventStoreConnectionString, String databaseConnectionString, List<String> estateIds,
186+
IMessagingServiceClient messagingServiceClient,
187+
CancellationToken cancellationToken)
188+
{
189+
List<(String groupName, String streamName, Int64 parkedMessageCount)> parkedQueueInfo = await Jobs.GetParkedQueueInformation(eventStoreConnectionString, cancellationToken);
190+
List<(Guid, String, DateTime, String)> incompleteFiles = new List<(Guid, String, DateTime, String)>();
191+
foreach (String estateId in estateIds){
192+
String connectionString = databaseConnectionString.Replace("<estateid>", estateId.ToString());
193+
incompleteFiles.AddRange(await GetIncompleteFileList(connectionString, cancellationToken));
194+
}
195+
196+
SendEmailRequest emailRequest = BuildSupportEmail(dateTime, incompleteFiles, parkedQueueInfo);
197+
emailRequest.ConnectionIdentifier = Guid.Parse(estateIds.First());
198+
await messagingServiceClient.SendEmail(accessToken, emailRequest, CancellationToken.None);
199+
}
200+
}

0 commit comments

Comments
 (0)