Skip to content

Commit

Permalink
dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
wojteksuwala committed Jan 29, 2020
1 parent b2354c2 commit 7bfb555
Show file tree
Hide file tree
Showing 12 changed files with 313 additions and 9 deletions.
22 changes: 22 additions & 0 deletions DashboardService.Test/DashboardService.Test.csproj
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="DotNet.Testcontainers" Version="1.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.0.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\DashboardService.Api\DashboardService.Api.csproj" />
<ProjectReference Include="..\DashboardService\DashboardService.csproj" />
</ItemGroup>

</Project>
73 changes: 73 additions & 0 deletions DashboardService.Test/UnitTest1.cs
@@ -0,0 +1,73 @@
using System;
using System.Threading.Tasks;
using DashboardService.DataAccess.Elastic;
using DashboardService.Domain;
using DotNet.Testcontainers.Containers.Builders;
using DotNet.Testcontainers.Containers.Modules;
using DotNet.Testcontainers.Containers.WaitStrategies;
using Nest;
using Xunit;

namespace DashboardService.Test
{
public class UnitTest1
{
[Fact]
public async Task Test1()
{
//docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 elasticsearch:7.5.1

var testContainersBuilder = new TestcontainersBuilder<TestcontainersContainer>()
.WithImage("elasticsearch:6.4.0")
.WithName("elasticsearch-33333")
.WithEnvironment("discovery.type","single-node")
.WithPortBinding(9200, 9200)
.WithPortBinding(9300, 9300)
//.WithCleanUp(true)
//.WithWaitStrategy(Wait.UntilContainerIsRunning());
.WithWaitStrategy(Wait.UntilPortsAreAvailable(9200));

using var testContainer = testContainersBuilder.Build();
await testContainer.StartAsync();

var policyRepo = new ElasticPolicyRepository(CreateElasticClient());

var pol = new PolicyDocument
(
"POL1201",
new DateTime(2019,1,1),
new DateTime(2019,12,31),
"Jan Ziomalski",
"BDA",
4500M,
"jim beam"
);

policyRepo.Save(pol);

var saved = policyRepo.FindByNumber("POL1201");


Assert.NotNull(saved);

/*
* Elasticsearch.Net.UnexpectedElasticsearchClientException: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Int64' because the type requires a JSON primitive value (e.g. string, number, boolean, null) to deserialize correctly.
To fix this error either change the JSON to a JSON primitive value (e.g. string, number, boolean, null) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path 'hits.total.value', line 1, position 114.
---> Nest.Json.JsonSerializationException: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Int64' because the type requires a JSON primitive value (e.g. string, number, boolean, null) to deserialize correctly.
To fix this error either change the JSON to a JSON primitive value (e.g. string, number, boolean, null) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path 'hits.total.value', line 1, position 114.
at Nest.Json.Serialization
*/

}

private ElasticClient CreateElasticClient()
{
var connectionSettings = new ConnectionSettings()
.DefaultMappingFor<PolicyDocument>(m=>
m.IndexName("policy_lab_stats").IdProperty(d=>d.Number));
return new ElasticClient(connectionSettings);
}
}
}
3 changes: 3 additions & 0 deletions DashboardService/DashboardService.csproj
Expand Up @@ -10,10 +10,13 @@
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.0" />
<PackageReference Include="NEST" Version="6.4.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="RawRabbit.DependencyInjection.ServiceCollection" Version="2.0.0-rc5" />
<PackageReference Include="RawRabbit.Operations.Subscribe" Version="2.0.0-rc5" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\DashboardService.Api\DashboardService.Api.csproj" />
<ProjectReference Include="..\PolicyService.Api\PolicyService.Api.csproj" />
</ItemGroup>


Expand Down
15 changes: 7 additions & 8 deletions DashboardService/DataAccess/Elastic/ElasticPolicyRepository.cs
@@ -1,3 +1,4 @@
using System;
using System.Linq;
using DashboardService.Domain;
using Nest;
Expand All @@ -8,13 +9,6 @@ public class ElasticPolicyRepository : IPolicyRepository
{
private readonly ElasticClient elasticClient;

public ElasticPolicyRepository()
{
var connectionSettings = new ConnectionSettings()
.DefaultMappingFor<PolicyDocument>(m=>m.IndexName("policy_lab_stats").IdProperty(d=>d.Number));
elasticClient = new ElasticClient(connectionSettings);
}

public ElasticPolicyRepository(ElasticClient elasticClient)
{
this.elasticClient = elasticClient;
Expand All @@ -28,13 +22,18 @@ public ElasticPolicyRepository(ElasticClient elasticClient)

public void Save(PolicyDocument policy)
{
elasticClient.Index
var response = elasticClient.Index
(
policy,
i => i
.Index("policy_lab_stats")
.Id(policy.Number)
);

if (!response.IsValid)
{
throw new ApplicationException("Failed to index a policy document");
}
}

public PolicyDocument FindByNumber(string policyNumber)
Expand Down
25 changes: 25 additions & 0 deletions DashboardService/DataAccess/Elastic/NestInstaller.cs
@@ -0,0 +1,25 @@
using System;
using DashboardService.Domain;
using Microsoft.Extensions.DependencyInjection;
using Nest;

namespace DashboardService.DataAccess.Elastic
{
public static class NestInstaller
{
public static IServiceCollection AddElasticSearch(this IServiceCollection services, string cnString)
{
services.AddSingleton(typeof(ElasticClient), svc => CreateElasticClient(cnString));
services.AddScoped(typeof(IPolicyRepository), typeof(ElasticPolicyRepository));
return services;
}

private static ElasticClient CreateElasticClient(string cnString)
{
var connectionSettings = new ConnectionSettings(new Uri(cnString))
.DefaultMappingFor<PolicyDocument>(m=>
m.IndexName("policy_lab_stats").IdProperty(d=>d.Number));
return new ElasticClient(connectionSettings);
}
}
}
36 changes: 36 additions & 0 deletions DashboardService/Listeners/PolicyCreatedHandler.cs
@@ -0,0 +1,36 @@
using System.Threading;
using System.Threading.Tasks;
using DashboardService.Domain;
using MediatR;
using PolicyService.Api.Events;

namespace DashboardService.Listeners
{
public class PolicyCreatedHandler : INotificationHandler<PolicyCreated>
{
private readonly IPolicyRepository policyRepository;

public PolicyCreatedHandler(IPolicyRepository policyRepository)
{
this.policyRepository = policyRepository;
}

public Task Handle(PolicyCreated notification, CancellationToken cancellationToken)
{
var policy = new PolicyDocument
(
notification.PolicyNumber,
notification.PolicyFrom,
notification.PolicyTo,
$"{notification.PolicyHolder.FirstName} {notification.PolicyHolder.LastName}",
notification.ProductCode,
notification.TotalPremium,
notification.AgentLogin
);

policyRepository.Save(policy);

return Task.CompletedTask;
}
}
}
57 changes: 57 additions & 0 deletions DashboardService/Messaging/RabbitMq/RabbitEventListener.cs
@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using RawRabbit;

namespace DashboardService.Messaging.RabbitMq
{
public class RabbitEventListener
{
private readonly IBusClient busClient;
private readonly IServiceProvider serviceProvider;

public RabbitEventListener(
IBusClient busClient,
IServiceProvider serviceProvider)
{
this.busClient = busClient;
this.serviceProvider = serviceProvider;
}

public void ListenTo(List<Type> eventsToSubscribe)
{
foreach (var evtType in eventsToSubscribe)
{
//add check if is INotification
this.GetType()
.GetMethod("Subscribe", System.Reflection.BindingFlags.NonPublic| System.Reflection.BindingFlags.Instance)
.MakeGenericMethod(evtType)
.Invoke(this, new object[] { });
}
}

private void Subscribe<T>() where T : INotification
{
//TODO: move exchange name and queue prefix to cfg
this.busClient.SubscribeAsync<T>(
async (msg) =>
{
//add logging
using (var scope = serviceProvider.CreateScope())
{
var internalBus = scope.ServiceProvider.GetRequiredService<IMediator>();
await internalBus.Publish(msg);
}
},
cfg => cfg.UseSubscribeConfiguration(
c => c
.OnDeclaredExchange(e => e
.WithName("lab-dotnet-micro")
.WithType(RawRabbit.Configuration.Exchange.ExchangeType.Topic)
.WithArgument("key", typeof(T).Name.ToLower()))
.FromDeclaredQueue(q => q.WithName("lab-dashboard-service-" + typeof(T).Name)))
);
}
}
}
9 changes: 9 additions & 0 deletions DashboardService/Messaging/RabbitMq/RabbitMqOptions.cs
@@ -0,0 +1,9 @@
namespace DashboardService.Messaging.RabbitMq
{
public class RabbitMqOptions
{
public string Host { get; set; }

public int Port { get; set; }
}
}
59 changes: 59 additions & 0 deletions DashboardService/Messaging/RabbitMq/RawRabbitInstaller.cs
@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using RawRabbit;
using RawRabbit.DependencyInjection.ServiceCollection;
using RawRabbit.Instantiation;

namespace DashboardService.Messaging.RabbitMq
{
public static class RawRabbitInstaller
{
public static IServiceCollection AddRabbitListeners(this IServiceCollection services, RabbitMqOptions options)
{
services.AddRawRabbit(new RawRabbitOptions
{
ClientConfiguration = new RawRabbit.Configuration.RawRabbitConfiguration
{
Username = "guest",
Password = "guest",
VirtualHost = "/",
Port = options.Port,
Hostnames = new List<string> { options.Host },
RequestTimeout = TimeSpan.FromSeconds(10),
PublishConfirmTimeout = TimeSpan.FromSeconds(1),
RecoveryInterval = TimeSpan.FromSeconds(1),
PersistentDeliveryMode = true,
AutoCloseConnection = true,
AutomaticRecovery = true,
TopologyRecovery = true,
Exchange = new RawRabbit.Configuration.GeneralExchangeConfiguration
{
Durable = true,
AutoDelete = false,
Type = RawRabbit.Configuration.Exchange.ExchangeType.Topic
},
Queue = new RawRabbit.Configuration.GeneralQueueConfiguration
{
Durable = true,
AutoDelete = false,
Exclusive = false
}
}
});

services.AddSingleton(svc => new RabbitEventListener(svc.GetRequiredService<IBusClient>(), svc));

return services;
}
}

public static class RabbitListenersInstaller
{
public static void UseRabbitListeners(this IApplicationBuilder app, List<Type> eventTypes)
{
app.ApplicationServices.GetRequiredService<RabbitEventListener>().ListenTo(eventTypes);
}
}
}
8 changes: 8 additions & 0 deletions DashboardService/Startup.cs
@@ -1,12 +1,16 @@
using System;
using System.Collections.Generic;
using DashboardService.DataAccess.Elastic;
using DashboardService.Domain;
using DashboardService.Messaging.RabbitMq;
using MediatR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using PolicyService.Api.Events;

namespace DashboardService
{
Expand All @@ -26,7 +30,9 @@ public void ConfigureServices(IServiceCollection services)
.AddNewtonsoftJson()
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddMediatR();
services.AddElasticSearch(Configuration.GetConnectionString("ElasticSearchConnection"));
services.AddSingleton<IPolicyRepository, ElasticPolicyRepository>();
services.AddRabbitListeners(Configuration.GetSection("RabbitMqOptions").Get<RabbitMqOptions>());
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Expand All @@ -44,6 +50,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseAuthorization();

app.UseEndpoints(endpoints => { endpoints.MapControllers(); });

app.UseRabbitListeners(new List<Type> { typeof(PolicyCreated) });
}
}
}
9 changes: 8 additions & 1 deletion DashboardService/appsettings.json
Expand Up @@ -6,5 +6,12 @@
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
"AllowedHosts": "*",
"RabbitMqOptions" : {
"Host" : "localhost",
"Port" : 5672
},
"ConnectionStrings": {
"ElasticSearchConnection": "http://localhost:9200"
}
}

0 comments on commit 7bfb555

Please sign in to comment.