Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

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

<ItemGroup>
<ProjectReference Include="..\TransactionProcessing.SchedulerService.Jobs\TransactionProcessing.SchedulerService.Jobs.csproj" />
</ItemGroup>

</Project>
32 changes: 32 additions & 0 deletions TransactionProcessing.SchedulerService/JobTestDriver/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace JobTestDriver
{
using MessagingService.Client;
using TransactionProcessing.SchedulerService.Jobs;

internal class Program{
static async Task Main(string[] args){
//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);
//foreach ((String groupName, String streamName, Int64 parkedMessageCount) infoItem in info){
// Console.WriteLine($"Group: {infoItem.groupName} Stream: {infoItem.streamName} Parked Count: {infoItem.parkedMessageCount}");
//}

//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);
//foreach ((Guid, String, DateTime, String) incompleteFile in incompleteFiles){
// Console.WriteLine($"FileId: {incompleteFile.Item1} Location: {incompleteFile.Item2} Rcvd: {incompleteFile.Item3} Merchant: {incompleteFile.Item4}");
//}
HttpClient client = new HttpClient();
IMessagingServiceClient messagingServiceClient = new MessagingServiceClient(delegate(String s){ return "http://127.0.0.1:5006";}, client);
String accessToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjQzOUMxRDk5MDUwQTYyMDhEM0U5M0JFMjlBQUJBNzI5IiwidHlwIjoiYXQrand0In0.eyJpc3MiOiJodHRwczovLzEyNy4wLjAuMTo1MDAxIiwibmJmIjoxNjgzMTA2NDE2LCJpYXQiOjE2ODMxMDY0MTYsImV4cCI6MTY4MzExMDAxNiwiYXVkIjpbImVzdGF0ZU1hbmFnZW1lbnQiLCJlc3RhdGVSZXBvcnRpbmciLCJmaWxlUHJvY2Vzc29yIiwibWVzc2FnaW5nU2VydmljZSIsInRyYW5zYWN0aW9uUHJvY2Vzc29yIiwidHJhbnNhY3Rpb25Qcm9jZXNzb3JBQ0wiLCJ2b3VjaGVyTWFuYWdlbWVudCIsImh0dHBzOi8vMTI3LjAuMC4xOjUwMDEvcmVzb3VyY2VzIl0sInNjb3BlIjpbImVzdGF0ZU1hbmFnZW1lbnQiLCJlc3RhdGVSZXBvcnRpbmciLCJmaWxlUHJvY2Vzc29yIiwibWVzc2FnaW5nU2VydmljZSIsInRyYW5zYWN0aW9uUHJvY2Vzc29yIiwidHJhbnNhY3Rpb25Qcm9jZXNzb3JBQ0wiLCJ2b3VjaGVyTWFuYWdlbWVudCJdLCJjbGllbnRfaWQiOiJzZXJ2aWNlQ2xpZW50IiwianRpIjoiMDZBMUI4NzYyRjFGNDJGNkIwMzM5RTYwRTk2MkVDQkUifQ.G5pFWRJF430ZZxnGO_yIxEC6Zj81LRr3HNq6d8V9EV4Pswp5YO7hZ867Ln4mjnrYag4lGI4cpT5S6646r9KNdZMiLOsdQs2LEJPuUjEdVADIwm8rcdqT8OX-sC6uGA6VL0bMmYWQXw1E8d4kax444I6jeNquLjpoWVD1BDp9L1zzC6e_k9W7Fc9MQOogOqO82TXrBl9nkpBbmJQ0HDiub2yVUTUKLwkCeRfBDlyeU8tyNE7kH6IGdHIL_WYUtiiRYjBJ2PNLzTtrXQk4rqw6GB-25K2qcgP5FO0MI675tAkuPKI0DaySXHnAjYssW8wZYy0tkaJL0OIlmOUe-9jM6g";
await Jobs.SendSupportEmail(DateTime.Now,
accessToken,
"esdb://admin:changeit@192.168.0.133:2113?tls=false&tlsVerifyCert=false",
"server=192.168.0.133;user id=sa;password=Sc0tland;database=EstateReportingReadModel<estateid>;Encrypt=True;TrustServerCertificate=True",
new List<String>{
"435613ac-a468-47a3-ac4f-649d89764c22"
},
messagingServiceClient,
CancellationToken.None);

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using DataGeneration;
using EstateManagement.Client;
using EstateManagement.DataTransferObjects.Responses;
using MessagingService.Client;
using Microsoft.Extensions.DependencyInjection;
using Quartz;
using SecurityService.Client;
Expand Down Expand Up @@ -37,6 +38,7 @@ public static void ConfigureServices(IJobExecutionContext jobExecutionContext){

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

return token.AccessToken;
}

protected async Task<MerchantResponse> GetMerchant(String accessToken, Guid estateId, Guid merchantId, CancellationToken cancellationToken){
IEstateClient estateClient = Bootstrapper.GetService<IEstateClient>();
return await estateClient.GetMerchant(accessToken, estateId, merchantId, cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using DataGeneration;
using EstateManagement.DataTransferObjects.Responses;
using Quartz;
using Quartz.Logging;

[DisallowConcurrentExecution]
public class GenerateFileUploadsJob : BaseJob, IJob
Expand All @@ -19,21 +20,10 @@ public async Task Execute(IJobExecutionContext context)
String clientSecret = context.MergedJobDataMap.GetString("ClientSecret");
Guid estateId = context.MergedJobDataMap.GetGuidValueFromString("EstateId");
Guid merchantId = context.MergedJobDataMap.GetGuidValueFromString("MerchantId");

String accessToken = await this.GetToken(clientId,
clientSecret,
context.CancellationToken);

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

MerchantResponse merchant = await this.GetMerchant(accessToken, estateId, merchantId, context.CancellationToken);

List<ContractResponse> contracts = await t.GetMerchantContracts(merchant, context.CancellationToken);
DateTime fileDate = DateTime.Now;
foreach (ContractResponse contract in contracts){
// Generate a file and upload
await t.SendUploadFile(fileDate, contract, merchant, context.CancellationToken);
}
await Jobs.GenerateFileUploads(t, estateId, merchantId, context.CancellationToken);
}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
namespace TransactionProcessing.SchedulerService.Jobs;

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using DataGeneration;
using EstateManagement.DataTransferObjects.Responses;
using Quartz;

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

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

List<MerchantResponse> merchants = await t.GetMerchants(estateId, context.CancellationToken);
foreach (MerchantResponse merchantResponse in merchants){
await t.GenerateMerchantStatement(merchantResponse.EstateId, merchantResponse.MerchantId, DateTime.Now, context.CancellationToken);
}
await Jobs.GenerateMerchantStatements(t, estateId, context.CancellationToken);
}
}

public class SupportReportJob : BaseJob, IJob
{
public async Task Execute(IJobExecutionContext context)
{
Bootstrapper.ConfigureServices(context);

String eventStoreAddress = context.MergedJobDataMap.GetString("EventStoreAddress");
String databaseConnectionString = context.MergedJobDataMap.GetString("DatabaseConnectionString");

// Events in Parked Queues
// Incomplete Files
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,33 +38,7 @@ public async Task Execute(IJobExecutionContext context){

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

// get a token
String accessToken = await this.GetToken(clientId,
clientSecret,
context.CancellationToken);

// get the merchant
MerchantResponse merchant = await this.GetMerchant(accessToken, estateId, merchantId, context.CancellationToken);

DateTime transactionDate = DateTime.Now;

if (requireLogon){
// Do a logon transaction for the merchant
await t.PerformMerchantLogon(transactionDate, merchant, context.CancellationToken);
}

// Get the merchants contracts
List<ContractResponse> contracts = await t.GetMerchantContracts(merchant, context.CancellationToken);

foreach (ContractResponse contract in contracts){
// Generate and send some sales
await t.SendSales(transactionDate, merchant, contract, context.CancellationToken);

// Generate a file and upload
await t.SendUploadFile(transactionDate, contract, merchant, context.CancellationToken);
}

Console.WriteLine($"Logon sent for Merchant [{merchant.MerchantName}]");
await Jobs.GenerateTransactions(t, estateId, merchantId, requireLogon, context.CancellationToken);
}
catch(Exception e){
// TODO: Log the error
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
namespace TransactionProcessing.SchedulerService.Jobs;

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using DataGeneration;
using EstateManagement.DataTransferObjects.Responses;
using EventStore.Client;
using MessagingService.Client;
using MessagingService.DataTransferObjects;
using Microsoft.Data.SqlClient;
using Newtonsoft.Json;

public static class Jobs{
public static async Task GenerateMerchantStatements(ITransactionDataGenerator t, Guid estateId, CancellationToken cancellationToken){
List<MerchantResponse> merchants = await t.GetMerchants(estateId, cancellationToken);
foreach (MerchantResponse merchantResponse in merchants)
{
await t.GenerateMerchantStatement(merchantResponse.EstateId, merchantResponse.MerchantId, DateTime.Now, cancellationToken);
}
}

public static async Task GenerateFileUploads(ITransactionDataGenerator t, Guid estateId, Guid merchantId, CancellationToken cancellationToken)
{
MerchantResponse merchant = await t.GetMerchant( estateId, merchantId, cancellationToken);

List<ContractResponse> contracts = await t.GetMerchantContracts(merchant, cancellationToken);
DateTime fileDate = DateTime.Now;
foreach (ContractResponse contract in contracts)
{
// Generate a file and upload
await t.SendUploadFile(fileDate, contract, merchant, cancellationToken);
}
}

public static async Task GenerateTransactions(ITransactionDataGenerator t, Guid estateId, Guid merchantId, Boolean requireLogon, CancellationToken cancellationToken)
{
// get the merchant
MerchantResponse merchant = await t.GetMerchant(estateId, merchantId, cancellationToken);

DateTime transactionDate = DateTime.Now;

if (requireLogon)
{
// Do a logon transaction for the merchant
await t.PerformMerchantLogon(transactionDate, merchant, cancellationToken);
}

// Get the merchants contracts
List<ContractResponse> contracts = await t.GetMerchantContracts(merchant, cancellationToken);

foreach (ContractResponse contract in contracts)
{
// Generate and send some sales
await t.SendSales(transactionDate, merchant, contract, cancellationToken);

// Generate a file and upload
await t.SendUploadFile(transactionDate, contract, merchant, cancellationToken);
}
}

public static async Task PerformSettlement(ITransactionDataGenerator t, DateTime dateTime, Guid estateId, CancellationToken cancellationToken)
{
await t.PerformSettlement(dateTime, estateId, cancellationToken);
}

public static async Task<List<(String groupName, String streamName, Int64 parkedMessageCount)>> GetParkedQueueInformation(String eventStoreConnectionString, CancellationToken cancellationToken){
EventStoreClientSettings clientSettings = EventStoreClientSettings.Create(eventStoreConnectionString);
EventStore.Client.EventStorePersistentSubscriptionsClient client = new EventStorePersistentSubscriptionsClient(clientSettings);
List<(String groupName, String streamName, Int64 parkedMessageCount)> result = new List<(String groupName, String streamName, Int64 parkedMessageCount)>();
IEnumerable<PersistentSubscriptionInfo> x = await client.ListAllAsync(cancellationToken:cancellationToken);
foreach (PersistentSubscriptionInfo persistentSubscriptionInfo in x){
if (persistentSubscriptionInfo.Stats.ParkedMessageCount > 0){
// Add to replay list
result.Add((persistentSubscriptionInfo.GroupName, persistentSubscriptionInfo.EventSource, persistentSubscriptionInfo.Stats.ParkedMessageCount));
}
}

return result;
}

public static async Task<List<(Guid,String,DateTime,String)>> GetIncompleteFileList(String databaseConnectionString,CancellationToken cancellationToken){
List<(Guid, String, DateTime, String)> result = new List<(Guid, String, DateTime, String)>();

using (SqlConnection connection = new SqlConnection(databaseConnectionString)){
await connection.OpenAsync(cancellationToken);

SqlCommand command = connection.CreateCommand();
command.CommandText = "select FileId, FileLocation, FileReceivedDateTime, merchant.Name from [file] f inner join merchant on merchant.MerchantId = f.MerchantId where IsCompleted= 0";
command.CommandType = CommandType.Text;

var reader = await command.ExecuteReaderAsync(cancellationToken);

while (await reader.ReadAsync(cancellationToken)){

var fileId = reader.GetGuid(0);
var fileLocation = reader.GetString(1);
var fileReceivedDateTime = reader.GetDateTime(2);
var merchantName = reader.GetString(3);

result.Add((fileId, fileLocation, fileReceivedDateTime, merchantName));
}
}

return result;
}

public static SendEmailRequest BuildSupportEmail(DateTime dateTime,
List<(Guid fileId, String fileLocation, DateTime fileReceivedDateTime, String merchantName)> incompleteFiles,
List<(String groupName, String streamName, Int64 parkedMessageCount)> parkedQueueInformation)
{
// Build uo the HTML String
StringBuilder htmlBuilder = new StringBuilder();

htmlBuilder.AppendLine("<html>");
htmlBuilder.AppendLine("<head>");
htmlBuilder.AppendLine("<style>");
htmlBuilder.AppendLine("table, th, td { border: 1px solid black; }");
htmlBuilder.AppendLine("</style>");
htmlBuilder.AppendLine("</head>");

htmlBuilder.AppendLine("<body>");

htmlBuilder.AppendLine($"<h1>Daily Support Report for {dateTime.ToString("dd-MM-yyyy")}</h1>");

htmlBuilder.AppendLine("<h2>Parked Messages Stats</h2>");

if (parkedQueueInformation.Any() == false){
htmlBuilder.AppendLine("<p>No Data</p>");
}
else{
htmlBuilder.AppendLine("<table>");
htmlBuilder.Append("<tr><th>Group</th><th>Stream</th><th>Parked Messages</th></tr>");

foreach ((String groupName, String streamName, Int64 parkedMessageCount) info in parkedQueueInformation){
htmlBuilder.Append($"<tr><td>{info.groupName}</td><td>{info.streamName}</td><td>{info.parkedMessageCount}</td></tr>");
}

htmlBuilder.AppendLine("</table>");
}

htmlBuilder.AppendLine("<h2>Incomplete Bulk Files</h2>");

if (incompleteFiles.Any() == false)
{
htmlBuilder.AppendLine("<p>No Data</p>");
}
else
{
htmlBuilder.AppendLine("<table>");
htmlBuilder.Append("<tr><th>Id</th><th>Location</th><th>File Received</th><th>Merchant Name</th></tr>");

foreach ((Guid fileId, String fileLocation, DateTime fileReceivedDateTime, String merchantName) incompleteFile in incompleteFiles){

htmlBuilder.Append($"<tr><td>{incompleteFile.fileId}</td><td>{incompleteFile.fileLocation}</td><td>{incompleteFile.fileReceivedDateTime}</td><td>{incompleteFile.merchantName}</td></tr>");
}

htmlBuilder.AppendLine("</table>");
}

htmlBuilder.AppendLine("</body>");
htmlBuilder.AppendLine("</html>");

SendEmailRequest request = new SendEmailRequest{
Body = htmlBuilder.ToString(),
ConnectionIdentifier = Guid.NewGuid(),
FromAddress = "support@transactionprocessing.com",
IsHtml = true,
MessageId = Guid.NewGuid(),
Subject = $"Daily Support Report for {dateTime:dd-MM-yyyy}",
ToAddresses = new List<String>{
"stuart_ferguson1development@outlook.com"
}
};
return request;
}

public static async Task SendSupportEmail(DateTime dateTime,
String accessToken,
String eventStoreConnectionString, String databaseConnectionString, List<String> estateIds,
IMessagingServiceClient messagingServiceClient,
CancellationToken cancellationToken)
{
List<(String groupName, String streamName, Int64 parkedMessageCount)> parkedQueueInfo = await Jobs.GetParkedQueueInformation(eventStoreConnectionString, cancellationToken);
List<(Guid, String, DateTime, String)> incompleteFiles = new List<(Guid, String, DateTime, String)>();
foreach (String estateId in estateIds){
String connectionString = databaseConnectionString.Replace("<estateid>", estateId.ToString());
incompleteFiles.AddRange(await GetIncompleteFileList(connectionString, cancellationToken));
}

SendEmailRequest emailRequest = BuildSupportEmail(dateTime, incompleteFiles, parkedQueueInfo);
emailRequest.ConnectionIdentifier = Guid.Parse(estateIds.First());
await messagingServiceClient.SendEmail(accessToken, emailRequest, CancellationToken.None);
}
}
Loading