diff --git a/Covid19Api.sln b/Covid19Api.sln
index e4afdc6..834360f 100644
--- a/Covid19Api.sln
+++ b/Covid19Api.sln
@@ -53,6 +53,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{EBCCA0C8-4
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Covid19Api.Tests", "test\Covid19Api.Tests\Covid19Api.Tests.csproj", "{663655F5-2BFE-4154-AF3F-BB8BF36C2165}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Covid19Api.Mongo.Migrator", "src\Covid19Api.Mongo.Migrator\Covid19Api.Mongo.Migrator.csproj", "{9C4F9DEA-77E9-42E7-8991-C5227AAC0119}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Covid19Api.AutoMapper", "src\Covid19Api.AutoMapper\Covid19Api.AutoMapper.csproj", "{2DEC9058-1FD3-457F-A0B2-DF053371DF34}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -258,6 +262,30 @@ Global
{663655F5-2BFE-4154-AF3F-BB8BF36C2165}.Release|x64.Build.0 = Release|Any CPU
{663655F5-2BFE-4154-AF3F-BB8BF36C2165}.Release|x86.ActiveCfg = Release|Any CPU
{663655F5-2BFE-4154-AF3F-BB8BF36C2165}.Release|x86.Build.0 = Release|Any CPU
+ {9C4F9DEA-77E9-42E7-8991-C5227AAC0119}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9C4F9DEA-77E9-42E7-8991-C5227AAC0119}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9C4F9DEA-77E9-42E7-8991-C5227AAC0119}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {9C4F9DEA-77E9-42E7-8991-C5227AAC0119}.Debug|x64.Build.0 = Debug|Any CPU
+ {9C4F9DEA-77E9-42E7-8991-C5227AAC0119}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {9C4F9DEA-77E9-42E7-8991-C5227AAC0119}.Debug|x86.Build.0 = Debug|Any CPU
+ {9C4F9DEA-77E9-42E7-8991-C5227AAC0119}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9C4F9DEA-77E9-42E7-8991-C5227AAC0119}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9C4F9DEA-77E9-42E7-8991-C5227AAC0119}.Release|x64.ActiveCfg = Release|Any CPU
+ {9C4F9DEA-77E9-42E7-8991-C5227AAC0119}.Release|x64.Build.0 = Release|Any CPU
+ {9C4F9DEA-77E9-42E7-8991-C5227AAC0119}.Release|x86.ActiveCfg = Release|Any CPU
+ {9C4F9DEA-77E9-42E7-8991-C5227AAC0119}.Release|x86.Build.0 = Release|Any CPU
+ {2DEC9058-1FD3-457F-A0B2-DF053371DF34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2DEC9058-1FD3-457F-A0B2-DF053371DF34}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2DEC9058-1FD3-457F-A0B2-DF053371DF34}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2DEC9058-1FD3-457F-A0B2-DF053371DF34}.Debug|x64.Build.0 = Debug|Any CPU
+ {2DEC9058-1FD3-457F-A0B2-DF053371DF34}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2DEC9058-1FD3-457F-A0B2-DF053371DF34}.Debug|x86.Build.0 = Debug|Any CPU
+ {2DEC9058-1FD3-457F-A0B2-DF053371DF34}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2DEC9058-1FD3-457F-A0B2-DF053371DF34}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2DEC9058-1FD3-457F-A0B2-DF053371DF34}.Release|x64.ActiveCfg = Release|Any CPU
+ {2DEC9058-1FD3-457F-A0B2-DF053371DF34}.Release|x64.Build.0 = Release|Any CPU
+ {2DEC9058-1FD3-457F-A0B2-DF053371DF34}.Release|x86.ActiveCfg = Release|Any CPU
+ {2DEC9058-1FD3-457F-A0B2-DF053371DF34}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{1A670B81-08F0-4F15-81AF-4DDD12E765D6} = {2A35F3E7-12D1-4F74-8867-F5882FB47009}
@@ -276,5 +304,7 @@ Global
{3FAF620F-B417-43EF-8540-B0DDBA932DC8} = {2A35F3E7-12D1-4F74-8867-F5882FB47009}
{CC6C9309-C452-4C12-9783-35B5298781AF} = {2A35F3E7-12D1-4F74-8867-F5882FB47009}
{663655F5-2BFE-4154-AF3F-BB8BF36C2165} = {EBCCA0C8-4218-490F-A131-E8DF265DC415}
+ {9C4F9DEA-77E9-42E7-8991-C5227AAC0119} = {2A35F3E7-12D1-4F74-8867-F5882FB47009}
+ {2DEC9058-1FD3-457F-A0B2-DF053371DF34} = {2A35F3E7-12D1-4F74-8867-F5882FB47009}
EndGlobalSection
EndGlobal
diff --git a/src/Covid19Api/AutoMapper/CountryStatsProfile.cs b/src/Covid19Api.AutoMapper/CountryStatsProfile.cs
similarity index 100%
rename from src/Covid19Api/AutoMapper/CountryStatsProfile.cs
rename to src/Covid19Api.AutoMapper/CountryStatsProfile.cs
diff --git a/src/Covid19Api.AutoMapper/Covid19Api.AutoMapper.csproj b/src/Covid19Api.AutoMapper/Covid19Api.AutoMapper.csproj
new file mode 100644
index 0000000..ae57a89
--- /dev/null
+++ b/src/Covid19Api.AutoMapper/Covid19Api.AutoMapper.csproj
@@ -0,0 +1,16 @@
+
+
+
+ net5.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Covid19Api.AutoMapper/GlobalStatisticsAggregateProfile.cs b/src/Covid19Api.AutoMapper/GlobalStatisticsAggregateProfile.cs
new file mode 100644
index 0000000..1ae4f597
--- /dev/null
+++ b/src/Covid19Api.AutoMapper/GlobalStatisticsAggregateProfile.cs
@@ -0,0 +1,16 @@
+using AutoMapper;
+using Covid19Api.Domain;
+using Covid19Api.Presentation.Response;
+
+namespace Covid19Api.AutoMapper
+{
+ public class GlobalStatisticsAggregateProfile : Profile
+ {
+ public GlobalStatisticsAggregateProfile()
+ {
+ this.CreateMap()
+ .ConstructUsing(source => new GlobalStatisticsAggregateDto(source.Id, source.Total, source.Recovered,
+ source.Deaths, source.Month, source.Year));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Covid19Api/AutoMapper/GlobalStatsProfile.cs b/src/Covid19Api.AutoMapper/GlobalStatsProfile.cs
similarity index 100%
rename from src/Covid19Api/AutoMapper/GlobalStatsProfile.cs
rename to src/Covid19Api.AutoMapper/GlobalStatsProfile.cs
diff --git a/src/Covid19Api.Domain/CountryStatistics.cs b/src/Covid19Api.Domain/CountryStatistics.cs
index 9f107e1..dfcd679 100644
--- a/src/Covid19Api.Domain/CountryStatistics.cs
+++ b/src/Covid19Api.Domain/CountryStatistics.cs
@@ -32,7 +32,6 @@ public class CountryStatistics
public DateTime FetchedAt { get; private set; }
-
public CountryStatistics(string country, string? countryCode, int totalCases, int newCases, int totalDeaths,
int newDeaths,
int recoveredCases, int activeCases, int seriousCases, DateTime fetchedAt)
diff --git a/src/Covid19Api.Domain/Enums/AggregateType.cs b/src/Covid19Api.Domain/Enums/AggregateType.cs
new file mode 100644
index 0000000..6dc378f
--- /dev/null
+++ b/src/Covid19Api.Domain/Enums/AggregateType.cs
@@ -0,0 +1,8 @@
+namespace Covid19Api.Domain.Enums
+{
+ public enum AggregateType
+ {
+ Month = 0,
+ Year = 1,
+ }
+}
\ No newline at end of file
diff --git a/src/Covid19Api.Domain/GlobalStatisticsAggregate.cs b/src/Covid19Api.Domain/GlobalStatisticsAggregate.cs
new file mode 100644
index 0000000..01f1073
--- /dev/null
+++ b/src/Covid19Api.Domain/GlobalStatisticsAggregate.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace Covid19Api.Domain
+{
+ // ReSharper disable AutoPropertyCanBeMadeGetOnly.Local
+ // ReSharper disable UnusedAutoPropertyAccessor.Global
+ public class GlobalStatisticsAggregate
+ {
+ public GlobalStatisticsAggregate(int total, int recovered, int deaths, int month, int year)
+ {
+ this.Total = total;
+ this.Recovered = recovered;
+ this.Deaths = deaths;
+ this.Month = month;
+ this.Year = year;
+ this.Id = this.Generate();
+ }
+
+ public Guid Id { get; private set; }
+ public int Total { get; private set; }
+ public int Recovered { get; private set; }
+ public int Deaths { get; private set; }
+ public int Month { get; private set; }
+ public int Year { get; private set; }
+
+ private Guid Generate()
+ {
+ using var hasher = MD5.Create();
+
+ var unhashed = $"{nameof(GlobalStatisticsAggregate)}_{this.Month}.{this.Year}";
+
+ var hashed = hasher.ComputeHash(Encoding.UTF8.GetBytes(unhashed));
+
+ return new Guid(hashed);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Covid19Api.IoC/Extensions/ContainerBuilderExtensions.cs b/src/Covid19Api.IoC/Extensions/ContainerBuilderExtensions.cs
index 174cb82..3c3e0f6 100644
--- a/src/Covid19Api.IoC/Extensions/ContainerBuilderExtensions.cs
+++ b/src/Covid19Api.IoC/Extensions/ContainerBuilderExtensions.cs
@@ -25,6 +25,10 @@ public static ContainerBuilder RegisterRepositories(this ContainerBuilder builde
.As()
.InstancePerLifetimeScope();
+ builder.RegisterType()
+ .As()
+ .InstancePerLifetimeScope();
+
builder.RegisterType()
.As()
.InstancePerLifetimeScope();
@@ -39,7 +43,7 @@ public static ContainerBuilder RegisterServices(this ContainerBuilder builder)
builder.RegisterType()
.As()
.SingleInstance();
-
+
builder.RegisterType()
.As()
.SingleInstance();
@@ -74,6 +78,10 @@ public static ContainerBuilder RegisterWorker(this ContainerBuilder builder)
.As()
.InstancePerDependency();
+ builder.RegisterType()
+ .As()
+ .InstancePerDependency();
+
return builder;
}
}
diff --git a/src/Covid19Api.Mongo.Migrator/Abstractions/DatabaseMigration.cs b/src/Covid19Api.Mongo.Migrator/Abstractions/DatabaseMigration.cs
new file mode 100644
index 0000000..b20f528
--- /dev/null
+++ b/src/Covid19Api.Mongo.Migrator/Abstractions/DatabaseMigration.cs
@@ -0,0 +1,27 @@
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+
+namespace Covid19Api.Mongo.Migrator.Abstractions
+{
+ public abstract class DatabaseMigration
+ {
+ private readonly ILogger logger;
+
+ protected DatabaseMigration(ILogger logger)
+ {
+ this.logger = logger;
+ }
+ public abstract int Number { get; }
+
+ protected abstract string Name { get; }
+
+ public async Task ExecuteUpdateAsync()
+ {
+ this.logger.LogInformation("Executing migration {number}-{migration}", this.Number, this.Name);
+ await ExecuteAsync();
+ this.logger.LogInformation("Executed migration {number}-{migration}", this.Number, this.Name);
+ }
+
+ protected abstract Task ExecuteAsync();
+ }
+}
\ No newline at end of file
diff --git a/src/Covid19Api.Mongo.Migrator/Configuration/GlobalAggregatesStartConfiguration.cs b/src/Covid19Api.Mongo.Migrator/Configuration/GlobalAggregatesStartConfiguration.cs
new file mode 100644
index 0000000..793e9f9
--- /dev/null
+++ b/src/Covid19Api.Mongo.Migrator/Configuration/GlobalAggregatesStartConfiguration.cs
@@ -0,0 +1,9 @@
+namespace Covid19Api.Mongo.Migrator.Configuration
+{
+ public class GlobalAggregatesStartConfiguration
+ {
+ public int Month { get; set; } = 9;
+
+ public int Year { get; set; } = 2020;
+ }
+}
\ No newline at end of file
diff --git a/src/Covid19Api.Mongo.Migrator/Covid19Api.Mongo.Migrator.csproj b/src/Covid19Api.Mongo.Migrator/Covid19Api.Mongo.Migrator.csproj
new file mode 100644
index 0000000..1807f0e
--- /dev/null
+++ b/src/Covid19Api.Mongo.Migrator/Covid19Api.Mongo.Migrator.csproj
@@ -0,0 +1,36 @@
+
+
+
+ Exe
+ net5.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ Always
+ PreserveNewest
+
+
+ true
+ PreserveNewest
+ Never
+
+
+
+
diff --git a/src/Covid19Api.Mongo.Migrator/Migrations/000000_GlobalAggregatesMigration.cs b/src/Covid19Api.Mongo.Migrator/Migrations/000000_GlobalAggregatesMigration.cs
new file mode 100644
index 0000000..889907a
--- /dev/null
+++ b/src/Covid19Api.Mongo.Migrator/Migrations/000000_GlobalAggregatesMigration.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Threading.Tasks;
+using Covid19Api.Mongo.Migrator.Abstractions;
+using Covid19Api.Mongo.Migrator.Configuration;
+using Covid19Api.UseCases.Abstractions.Commands;
+using Covid19Api.UseCases.Abstractions.Queries;
+using MediatR;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+namespace Covid19Api.Mongo.Migrator.Migrations
+{
+ public class GlobalAggregatesMigration : DatabaseMigration
+ {
+ private readonly GlobalAggregatesStartConfiguration options;
+ private readonly IMediator mediator;
+
+ // ReSharper disable once SuggestBaseTypeForParameter
+ public GlobalAggregatesMigration(ILogger logger,
+ IOptions options, IMediator mediator) : base(logger)
+ {
+ this.mediator = mediator;
+ this.options = options?.Value ?? throw new ArgumentNullException(nameof(options));
+ }
+
+ public override int Number => 0;
+ protected override string Name => nameof(GlobalAggregatesMigration);
+
+ protected override async Task ExecuteAsync()
+ {
+ var next = new DateTime(this.options.Year, this.options.Month, 1, 0, 0, 0, DateTimeKind.Utc);
+ var end = DateTime.UtcNow.Date;
+
+ while (true)
+ {
+ if (next.Month > end.Month && next.Year >= end.Year)
+ break;
+
+ var query = new LoadGlobalStatisticsAggregate(next.Month, next.Year);
+ var aggregate = await this.mediator.Send(query);
+
+ if (aggregate is {})
+ {
+ next = next.AddMonths(1);
+ continue;
+ }
+
+ var command = new AggregateGlobalStatisticsCommand(next.Month, next.Year);
+ await this.mediator.Send(command);
+
+ next = next.AddMonths(1);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Covid19Api.Mongo.Migrator/Program.cs b/src/Covid19Api.Mongo.Migrator/Program.cs
new file mode 100644
index 0000000..a6d9cc7
--- /dev/null
+++ b/src/Covid19Api.Mongo.Migrator/Program.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Autofac;
+using Autofac.Extensions.DependencyInjection;
+using AutoMapper.Contrib.Autofac.DependencyInjection;
+using Covid19Api.AutoMapper;
+using Covid19Api.IoC.Extensions;
+using Covid19Api.Mongo.Migrator.Abstractions;
+using Covid19Api.Mongo.Migrator.Configuration;
+using Covid19Api.UseCases.Commands;
+using MediatR.Extensions.Autofac.DependencyInjection;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Serilog;
+
+namespace Covid19Api.Mongo.Migrator
+{
+ public static class Program
+ {
+ public static async Task Main(string[] args)
+ {
+ using var host = CreateHost(args);
+
+ await host.StartAsync();
+
+ var migrations = host.Services.GetServices();
+
+ foreach (var databaseMigration in migrations.OrderBy(migration => migration.Number))
+ await databaseMigration.ExecuteUpdateAsync();
+
+ await host.StopAsync();
+ }
+
+ private static IHost CreateHost(string[] args)
+ => Host.CreateDefaultBuilder(args)
+ .UseContentRoot(AppContext.BaseDirectory)
+ .UseServiceProviderFactory(new AutofacServiceProviderFactory())
+ .UseSerilog(ConfigureLogger)
+ .ConfigureServices(ConfigureServices)
+ .ConfigureContainer(ConfigureContainer)
+ .Build();
+
+ private static void ConfigureServices(HostBuilderContext hostBuilderContext, IServiceCollection services)
+ {
+ services.AddOptions();
+ services.Configure(options =>
+ hostBuilderContext.Configuration.GetSection(nameof(GlobalAggregatesStartConfiguration)).Bind(options));
+ }
+
+ private static void ConfigureLogger(HostBuilderContext context, LoggerConfiguration loggerConfiguration)
+ {
+ loggerConfiguration.ReadFrom.Configuration(context.Configuration);
+ }
+
+ private static void ConfigureContainer(HostBuilderContext context, ContainerBuilder builder)
+ {
+ builder.RegisterAssemblyTypes(typeof(Program).Assembly)
+ .AssignableTo(typeof(DatabaseMigration))
+ .As()
+ .InstancePerLifetimeScope();
+
+ builder.RegisterRepositories(context.HostingEnvironment, context.Configuration)
+ .RegisterServices()
+ .RegisterMediatR(typeof(RefreshGlobalStatisticsCommandHandler).Assembly)
+ .RegisterAutoMapper(typeof(CountryStatsProfile).Assembly);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Covid19Api.Mongo.Migrator/Properties/launchSettings.json b/src/Covid19Api.Mongo.Migrator/Properties/launchSettings.json
new file mode 100644
index 0000000..6888314
--- /dev/null
+++ b/src/Covid19Api.Mongo.Migrator/Properties/launchSettings.json
@@ -0,0 +1,11 @@
+{
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "Covid19Api.Mongo.Migrator": {
+ "commandName": "Project",
+ "environmentVariables": {
+ "DOTNET_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/src/Covid19Api.Mongo.Migrator/appsettings.json b/src/Covid19Api.Mongo.Migrator/appsettings.json
new file mode 100644
index 0000000..2dd93e3
--- /dev/null
+++ b/src/Covid19Api.Mongo.Migrator/appsettings.json
@@ -0,0 +1,21 @@
+{
+ "Serilog": {
+ "MinimumLevel": "Information",
+ "WriteTo": [
+ {
+ "Name": "Console",
+ "Args": {
+ "minimumLogEventLevel": "Information",
+ "outputTemplate": "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}"
+ }
+ }
+ ]
+ },
+ "ConnectionStrings": {
+ "MongoDb": "mongodb://root:root@localhost:27017/Covid19Api?authSource=admin"
+ },
+ "GlobalAggregatesStartConfiguration": {
+ "Month": 9,
+ "Year": 2020
+ }
+}
diff --git a/src/Covid19Api.Mongo.Scaffolder/Covid19Api.Mongo.Scaffolder.csproj b/src/Covid19Api.Mongo.Scaffolder/Covid19Api.Mongo.Scaffolder.csproj
index 7e0ad41..2fd7c50 100644
--- a/src/Covid19Api.Mongo.Scaffolder/Covid19Api.Mongo.Scaffolder.csproj
+++ b/src/Covid19Api.Mongo.Scaffolder/Covid19Api.Mongo.Scaffolder.csproj
@@ -24,7 +24,6 @@
-
diff --git a/src/Covid19Api.Mongo.Scaffolder/Program.cs b/src/Covid19Api.Mongo.Scaffolder/Program.cs
index 58d7ea3..9ee58dd 100644
--- a/src/Covid19Api.Mongo.Scaffolder/Program.cs
+++ b/src/Covid19Api.Mongo.Scaffolder/Program.cs
@@ -45,7 +45,7 @@ private static void ConfigureContainer(HostBuilderContext context, ContainerBuil
builder.RegisterAssemblyTypes(typeof(Program).Assembly)
.AssignableTo(typeof(DatabaseUpdateDefinition))
.As()
- .InstancePerDependency();
+ .InstancePerLifetimeScope();
builder.RegisterRepositories(context.HostingEnvironment, context.Configuration);
}
diff --git a/src/Covid19Api.Mongo.Scaffolder/Properties/launchSettings.json b/src/Covid19Api.Mongo.Scaffolder/Properties/launchSettings.json
index 6fafebb..e48a114 100644
--- a/src/Covid19Api.Mongo.Scaffolder/Properties/launchSettings.json
+++ b/src/Covid19Api.Mongo.Scaffolder/Properties/launchSettings.json
@@ -4,7 +4,7 @@
"Covid19Api.Mongo.Scaffolder": {
"commandName": "Project",
"environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development"
+ "DOTNET_ENVIRONMENT": "Development"
}
}
}
diff --git a/src/Covid19Api.Mongo.Scaffolder/Updates/000002_GlobalStatisticAggregateUpdateDefinition.cs b/src/Covid19Api.Mongo.Scaffolder/Updates/000002_GlobalStatisticAggregateUpdateDefinition.cs
new file mode 100644
index 0000000..b6a07b9
--- /dev/null
+++ b/src/Covid19Api.Mongo.Scaffolder/Updates/000002_GlobalStatisticAggregateUpdateDefinition.cs
@@ -0,0 +1,70 @@
+using System.Threading.Tasks;
+using Covid19Api.Domain;
+using Covid19Api.Mongo.Scaffolder.Abstractions;
+using Covid19Api.Mongo.Scaffolder.Extensions;
+using Microsoft.Extensions.Logging;
+using MongoDB.Driver;
+
+namespace Covid19Api.Mongo.Scaffolder.Updates
+{
+ // ReSharper disable once UnusedType.Global
+ public class GlobalStatisticAggregateUpdateDefinition : DatabaseUpdateDefinition
+ {
+ private readonly Covid19ApiDbContext databaseContext;
+
+ // ReSharper disable once SuggestBaseTypeForParameter
+ public GlobalStatisticAggregateUpdateDefinition(ILogger logger,
+ Covid19ApiDbContext databaseContext) : base(logger)
+ {
+ this.databaseContext = databaseContext;
+ }
+
+ public override int Version => 2;
+
+ protected override async Task ExecuteAsync()
+ {
+ await this.databaseContext.Database.CreateCollectionIfNotExistsAsync(CollectionNames
+ .GlobalStatisticsAggregate);
+
+ var monthIndex = Builders
+ .IndexKeys
+ .Descending(statistics => statistics.Month);
+
+ var monthIndexModel = new CreateIndexModel(monthIndex, new CreateIndexOptions
+ {
+ Name = $"{CollectionNames.GlobalStatisticsAggregate}_month_descending"
+ });
+
+ var yearIndex = Builders
+ .IndexKeys
+ .Descending(statistics => statistics.Year);
+
+ var yearIndexModel = new CreateIndexModel(yearIndex, new CreateIndexOptions
+ {
+ Name = $"{CollectionNames.GlobalStatisticsAggregate}_year_descending"
+ });
+
+ var yearMonthIndex = Builders
+ .IndexKeys
+ .Combine(yearIndex, monthIndex);
+
+ var yearMonthIndexModel = new CreateIndexModel(yearMonthIndex,
+ new CreateIndexOptions
+ {
+ Name = $"{CollectionNames.GlobalStatisticsAggregate}_year_month",
+ Unique = true
+ });
+
+ var collection =
+ this.databaseContext.Database.GetCollection(CollectionNames
+ .GlobalStatisticsAggregate);
+
+ await collection.Indexes.CreateManyAsync(new[]
+ {
+ monthIndexModel,
+ yearIndexModel,
+ yearMonthIndexModel
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Covid19Api.Mongo.Scaffolder/appsettings.json b/src/Covid19Api.Mongo.Scaffolder/appsettings.json
index 8379731..a56fe89 100644
--- a/src/Covid19Api.Mongo.Scaffolder/appsettings.json
+++ b/src/Covid19Api.Mongo.Scaffolder/appsettings.json
@@ -1,11 +1,4 @@
{
- "Logging": {
- "LogLevel": {
- "Default": "Information",
- "Microsoft": "Warning",
- "Microsoft.Hosting.Lifetime": "Information"
- }
- },
"Serilog": {
"MinimumLevel": "Information",
"WriteTo": [
diff --git a/src/Covid19Api.Mongo/CollectionNames.cs b/src/Covid19Api.Mongo/CollectionNames.cs
index abec83f..a129575 100644
--- a/src/Covid19Api.Mongo/CollectionNames.cs
+++ b/src/Covid19Api.Mongo/CollectionNames.cs
@@ -3,6 +3,8 @@ namespace Covid19Api.Mongo
public static class CollectionNames
{
public const string GlobalStatistics = "globalStatistics";
+ public const string GlobalStatisticsAggregate = "globalStatisticsAggregate";
+
public const string CountryStatistics = "countryStatistics";
}
}
\ No newline at end of file
diff --git a/src/Covid19Api.Presentation/Response/GlobalStatisticsAggregateDto.cs b/src/Covid19Api.Presentation/Response/GlobalStatisticsAggregateDto.cs
new file mode 100644
index 0000000..1702686
--- /dev/null
+++ b/src/Covid19Api.Presentation/Response/GlobalStatisticsAggregateDto.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace Covid19Api.Presentation.Response
+{
+ public class GlobalStatisticsAggregateDto
+ {
+ public GlobalStatisticsAggregateDto(Guid id, int total, int recovered, int deaths, int month, int year)
+ {
+ this.Id = id;
+ this.Total = total;
+ this.Recovered = recovered;
+ this.Deaths = deaths;
+ this.Month = month;
+ this.Year = year;
+ }
+
+ public Guid Id { get; set; }
+ public int Total { get; set; }
+ public int Recovered { get; set; }
+ public int Deaths { get; set; }
+ public int Month { get; set; }
+ public int Year { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Covid19Api.Repositories.Abstractions/IGlobalStatisticsAggregatesRepository.cs b/src/Covid19Api.Repositories.Abstractions/IGlobalStatisticsAggregatesRepository.cs
new file mode 100644
index 0000000..0b15d9a
--- /dev/null
+++ b/src/Covid19Api.Repositories.Abstractions/IGlobalStatisticsAggregatesRepository.cs
@@ -0,0 +1,12 @@
+using System.Threading.Tasks;
+using Covid19Api.Domain;
+
+namespace Covid19Api.Repositories.Abstractions
+{
+ public interface IGlobalStatisticsAggregatesRepository
+ {
+ Task StoreAsync(GlobalStatisticsAggregate globalStatisticsAggregate);
+
+ Task FindAsync(int month, int year);
+ }
+}
\ No newline at end of file
diff --git a/src/Covid19Api.Repositories.Abstractions/IGlobalStatisticsRepository.cs b/src/Covid19Api.Repositories.Abstractions/IGlobalStatisticsRepository.cs
index f6dc0bd..088b421 100644
--- a/src/Covid19Api.Repositories.Abstractions/IGlobalStatisticsRepository.cs
+++ b/src/Covid19Api.Repositories.Abstractions/IGlobalStatisticsRepository.cs
@@ -10,5 +10,6 @@ public interface IGlobalStatisticsRepository
Task StoreAsync(GlobalStatistics globalStatistics);
Task> HistoricalAsync(DateTime minFetchedAt);
Task> HistoricalForDayAsync(DateTime minFetchedAt);
+ Task> HistoricalInRange(DateTime inclusiveStart, DateTime inclusiveEnd);
}
}
\ No newline at end of file
diff --git a/src/Covid19Api.Repositories/GlobalStatisticsAggregatesRepository.cs b/src/Covid19Api.Repositories/GlobalStatisticsAggregatesRepository.cs
new file mode 100644
index 0000000..e9b63d8
--- /dev/null
+++ b/src/Covid19Api.Repositories/GlobalStatisticsAggregatesRepository.cs
@@ -0,0 +1,45 @@
+using System.Threading.Tasks;
+using Covid19Api.Domain;
+using Covid19Api.Mongo;
+using Covid19Api.Repositories.Abstractions;
+using MongoDB.Driver;
+
+namespace Covid19Api.Repositories
+{
+ public class GlobalStatisticsAggregatesRepository : IGlobalStatisticsAggregatesRepository
+ {
+ private readonly Covid19ApiDbContext context;
+
+ public GlobalStatisticsAggregatesRepository(Covid19ApiDbContext context)
+ {
+ this.context = context;
+ }
+
+ public Task StoreAsync(GlobalStatisticsAggregate globalStatisticsAggregate)
+ {
+ var collection = this.GetCollection();
+
+ var filter = Builders
+ .Filter.Where(aggregate => aggregate.Id == globalStatisticsAggregate.Id);
+
+ return collection.ReplaceOneAsync(filter,
+ globalStatisticsAggregate, new ReplaceOptions
+ {
+ IsUpsert = true
+ });
+ }
+
+ public async Task FindAsync(int month, int year)
+ {
+ var collection = this.GetCollection();
+
+ var cursor = await collection.FindAsync(aggregate => aggregate.Month == month && aggregate.Year == year);
+
+ return await cursor.SingleOrDefaultAsync();
+ }
+
+ private IMongoCollection GetCollection()
+ => this.context.Database.GetCollection(CollectionNames
+ .GlobalStatisticsAggregate);
+ }
+}
\ No newline at end of file
diff --git a/src/Covid19Api.Repositories/GlobalStatisticsRepository.cs b/src/Covid19Api.Repositories/GlobalStatisticsRepository.cs
index 65ee6b2..cb062f7 100644
--- a/src/Covid19Api.Repositories/GlobalStatisticsRepository.cs
+++ b/src/Covid19Api.Repositories/GlobalStatisticsRepository.cs
@@ -59,6 +59,18 @@ public async Task> HistoricalForDayAsync(DateTime
return all.OrderBy(entry => entry.FetchedAt);
}
+ public async Task> HistoricalInRange(DateTime inclusiveStart, DateTime inclusiveEnd)
+ {
+ var collection = this.GetCollection();
+
+ var leftFilter = Builders.Filter.Where(global => global.FetchedAt >= inclusiveStart);
+ var rightFilter = Builders.Filter.Where(global => global.FetchedAt <= inclusiveEnd);
+ var combinedFilter = leftFilter & rightFilter;
+
+ var cursor = await collection.FindAsync(combinedFilter);
+ return await cursor.ToListAsync();
+ }
+
private IMongoCollection GetCollection()
=> this.context.Database.GetCollection(CollectionNames.GlobalStatistics);
}
diff --git a/src/Covid19Api.Services.Abstractions/Models/CountryMetaData.cs b/src/Covid19Api.Services.Abstractions/Models/CountryMetaData.cs
index c3b7fb2..0ad99c0 100644
--- a/src/Covid19Api.Services.Abstractions/Models/CountryMetaData.cs
+++ b/src/Covid19Api.Services.Abstractions/Models/CountryMetaData.cs
@@ -2,6 +2,7 @@
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
// ReSharper disable UnusedMember.Local
// ReSharper disable ClassNeverInstantiated.Global
+
namespace Covid19Api.Services.Abstractions.Models
{
public class CountryMetaData
@@ -12,8 +13,10 @@ public CountryMetaData(string name, string alpha2Code, string[] altSpellings)
this.Alpha2Code = alpha2Code;
this.AltSpellings = altSpellings;
}
-
- private CountryMetaData () {}
+
+ private CountryMetaData()
+ {
+ }
public string Name { get; set; } = null!;
diff --git a/src/Covid19Api.Services/Loader/CountryStatisticsLoader.cs b/src/Covid19Api.Services/Loader/CountryStatisticsLoader.cs
index a5fbf4a..a331b9d 100644
--- a/src/Covid19Api.Services/Loader/CountryStatisticsLoader.cs
+++ b/src/Covid19Api.Services/Loader/CountryStatisticsLoader.cs
@@ -53,7 +53,7 @@ public CountryStatisticsLoader(ICountryMetaDataLoader countryMetaDataLoader,
var serious = ParseIntegerValue(tableDataNodes[9]);
if (string.IsNullOrWhiteSpace(country)) return null;
-
+
var countryCode = GetCountryCode(countryMetaData, country);
return new CountryStatistics(country, countryCode, totalCases, newCases, totalDeaths, newDeaths, recovered,
@@ -75,7 +75,7 @@ public CountryStatisticsLoader(ICountryMetaDataLoader countryMetaDataLoader,
,
StringComparison.InvariantCultureIgnoreCase) ||
metaData.AltSpellings.Contains(country, StringComparer.InvariantCultureIgnoreCase))?.Alpha2Code;
-
+
return countryCode;
}
diff --git a/src/Covid19Api.UseCases.Abstractions/Commands/AggregateGlobalStatisticsCommand.cs b/src/Covid19Api.UseCases.Abstractions/Commands/AggregateGlobalStatisticsCommand.cs
new file mode 100644
index 0000000..da18ebb
--- /dev/null
+++ b/src/Covid19Api.UseCases.Abstractions/Commands/AggregateGlobalStatisticsCommand.cs
@@ -0,0 +1,17 @@
+using MediatR;
+
+namespace Covid19Api.UseCases.Abstractions.Commands
+{
+ public class AggregateGlobalStatisticsCommand : IRequest
+ {
+ public AggregateGlobalStatisticsCommand(int month, int year)
+ {
+ this.Month = month;
+ this.Year = year;
+ }
+
+ public int Month { get; }
+
+ public int Year { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Covid19Api.UseCases.Abstractions/Models/CacheConfiguration.cs b/src/Covid19Api.UseCases.Abstractions/Models/CacheConfiguration.cs
index b13b114..53b08d2 100644
--- a/src/Covid19Api.UseCases.Abstractions/Models/CacheConfiguration.cs
+++ b/src/Covid19Api.UseCases.Abstractions/Models/CacheConfiguration.cs
@@ -9,7 +9,7 @@ public CacheConfiguration(string key, TimeSpan duration)
this.Key = !string.IsNullOrWhiteSpace(key) ? key : throw new ArgumentNullException(nameof(key));
this.Duration = duration;
}
-
+
public string Key { get; }
public TimeSpan Duration { get; }
diff --git a/src/Covid19Api.UseCases.Abstractions/Queries/LoadGlobalStatisticsAggregate.cs b/src/Covid19Api.UseCases.Abstractions/Queries/LoadGlobalStatisticsAggregate.cs
new file mode 100644
index 0000000..c9fad59
--- /dev/null
+++ b/src/Covid19Api.UseCases.Abstractions/Queries/LoadGlobalStatisticsAggregate.cs
@@ -0,0 +1,18 @@
+using Covid19Api.Presentation.Response;
+using MediatR;
+
+namespace Covid19Api.UseCases.Abstractions.Queries
+{
+ public class LoadGlobalStatisticsAggregate : IRequest
+ {
+ public LoadGlobalStatisticsAggregate(int month, int year)
+ {
+ this.Month = month;
+ this.Year = year;
+ }
+
+ public int Month { get; }
+
+ public int Year { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Covid19Api.UseCases.Abstractions/Queries/LoadHistoricalGlobalStatisticsQuery.cs b/src/Covid19Api.UseCases.Abstractions/Queries/LoadHistoricalGlobalStatisticsQuery.cs
index 61ba69a..9cd70f0 100644
--- a/src/Covid19Api.UseCases.Abstractions/Queries/LoadHistoricalGlobalStatisticsQuery.cs
+++ b/src/Covid19Api.UseCases.Abstractions/Queries/LoadHistoricalGlobalStatisticsQuery.cs
@@ -15,6 +15,8 @@ public LoadHistoricalGlobalStatisticsQuery(DateTime minFetchedAt)
}
public DateTime MinFetchedAt { get; }
- public CacheConfiguration GetCacheConfiguration() => new CacheConfiguration(nameof(LoadHistoricalGlobalStatisticsQuery), TimeSpan.FromMinutes(30));
+
+ public CacheConfiguration GetCacheConfiguration() =>
+ new CacheConfiguration(nameof(LoadHistoricalGlobalStatisticsQuery), TimeSpan.FromMinutes(30));
}
}
\ No newline at end of file
diff --git a/src/Covid19Api.UseCases/Behaviors/CachingBehavior.cs b/src/Covid19Api.UseCases/Behaviors/CachingBehavior.cs
index 985aa82..dfaa548 100644
--- a/src/Covid19Api.UseCases/Behaviors/CachingBehavior.cs
+++ b/src/Covid19Api.UseCases/Behaviors/CachingBehavior.cs
@@ -13,7 +13,8 @@
namespace Covid19Api.UseCases.Behaviors
{
- public class CachingBehavior : IPipelineBehavior where TRequest : notnull where TResponse : class
+ public class CachingBehavior : IPipelineBehavior
+ where TRequest : notnull where TResponse : class
{
private readonly JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions
{
@@ -25,7 +26,7 @@ public class CachingBehavior : IPipelineBehavior Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next)
+ public async Task Handle(TRequest request, CancellationToken cancellationToken,
+ RequestHandlerDelegate next)
{
if (!(request is ICacheableRequest cacheableRequest))
{
diff --git a/src/Covid19Api.UseCases/Commands/AggregateGlobalStatisticsCommandHandler.cs b/src/Covid19Api.UseCases/Commands/AggregateGlobalStatisticsCommandHandler.cs
new file mode 100644
index 0000000..44ce2c5
--- /dev/null
+++ b/src/Covid19Api.UseCases/Commands/AggregateGlobalStatisticsCommandHandler.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Globalization;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Covid19Api.Domain;
+using Covid19Api.Repositories.Abstractions;
+using Covid19Api.UseCases.Abstractions.Commands;
+using Covid19Api.UseCases.Extensions;
+using MediatR;
+using Microsoft.Extensions.Logging;
+
+namespace Covid19Api.UseCases.Commands
+{
+ public class AggregateGlobalStatisticsCommandHandler : IRequestHandler
+ {
+ private readonly ILogger logger;
+ private readonly IGlobalStatisticsRepository globalStatisticsRepository;
+ private readonly IGlobalStatisticsAggregatesRepository globalStatisticsAggregatesRepository;
+
+
+ public AggregateGlobalStatisticsCommandHandler(ILogger logger,
+ IGlobalStatisticsRepository globalStatisticsRepository,
+ IGlobalStatisticsAggregatesRepository globalStatisticsAggregatesRepository)
+ {
+ this.logger = logger;
+ this.globalStatisticsRepository = globalStatisticsRepository;
+ this.globalStatisticsAggregatesRepository = globalStatisticsAggregatesRepository;
+ }
+
+ public async Task Handle(AggregateGlobalStatisticsCommand request, CancellationToken cancellationToken)
+ {
+ var start = new DateTime(request.Year, request.Month, 1, 0, 0, 0, DateTimeKind.Utc);
+ var end = start.MonthsEnd();
+
+ this.logger.LogInformation("Aggregating {entity} from {from} to {to}",
+ nameof(GlobalStatistics),
+ start.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture),
+ end.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture));
+
+ var globalStatisticsInRange = await this.globalStatisticsRepository.HistoricalInRange(start, end);
+
+ if (!globalStatisticsInRange.Any()) return Unit.Value;
+
+ var aggregate = new GlobalStatisticsAggregate(
+ globalStatisticsInRange.Sum(statistics => statistics.Total),
+ globalStatisticsInRange.Sum(statistics => statistics.Recovered),
+ globalStatisticsInRange.Sum(statistics => statistics.Deaths),
+ request.Month, request.Year);
+
+ await this.globalStatisticsAggregatesRepository.StoreAsync(aggregate);
+
+ this.logger.LogInformation("Done aggregating {entity} from {from} to {to}",
+ nameof(GlobalStatistics),
+ start.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture),
+ end.ToString("dd.MM.yyyy", CultureInfo.InvariantCulture));
+
+ return Unit.Value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Covid19Api.UseCases/Extensions/DateTimeExtensions.cs b/src/Covid19Api.UseCases/Extensions/DateTimeExtensions.cs
new file mode 100644
index 0000000..a111431
--- /dev/null
+++ b/src/Covid19Api.UseCases/Extensions/DateTimeExtensions.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Covid19Api.UseCases.Extensions
+{
+ internal static class DateTimeExtensions
+ {
+ ///
+ /// Adds one month and subtracts 1 second from the given .
+ ///
+ /// The for the calculation.
+ ///
+ public static DateTime MonthsEnd(this DateTime dateTime)
+ => dateTime.AddMonths(1).AddSeconds(-1);
+ }
+}
\ No newline at end of file
diff --git a/src/Covid19Api.UseCases/Queries/LoadGlobalStatisticsAggregateQueryHandler.cs b/src/Covid19Api.UseCases/Queries/LoadGlobalStatisticsAggregateQueryHandler.cs
new file mode 100644
index 0000000..17ed4f9
--- /dev/null
+++ b/src/Covid19Api.UseCases/Queries/LoadGlobalStatisticsAggregateQueryHandler.cs
@@ -0,0 +1,35 @@
+using System.Threading;
+using System.Threading.Tasks;
+using AutoMapper;
+using Covid19Api.Presentation.Response;
+using Covid19Api.Repositories.Abstractions;
+using Covid19Api.UseCases.Abstractions.Queries;
+using MediatR;
+
+namespace Covid19Api.UseCases.Queries
+{
+ public class
+ LoadGlobalStatisticsAggregateQueryHandler : IRequestHandler
+ {
+ private readonly IMapper mapper;
+ private readonly IGlobalStatisticsAggregatesRepository globalStatisticsAggregatesRepository;
+
+ public LoadGlobalStatisticsAggregateQueryHandler(IMapper mapper,
+ IGlobalStatisticsAggregatesRepository globalStatisticsAggregatesRepository)
+ {
+ this.mapper = mapper;
+ this.globalStatisticsAggregatesRepository = globalStatisticsAggregatesRepository;
+ }
+
+ public async Task Handle(LoadGlobalStatisticsAggregate request,
+ CancellationToken cancellationToken)
+ {
+ var aggregate = await this.globalStatisticsAggregatesRepository.FindAsync(request.Month, request.Year);
+
+ return aggregate is null
+ ? null
+ : this.mapper.Map(aggregate);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Covid19Api.Worker/GlobalStatisticsAggregationWorker.cs b/src/Covid19Api.Worker/GlobalStatisticsAggregationWorker.cs
new file mode 100644
index 0000000..a90ee90
--- /dev/null
+++ b/src/Covid19Api.Worker/GlobalStatisticsAggregationWorker.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Covid19Api.UseCases.Abstractions.Commands;
+using MediatR;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+
+namespace Covid19Api.Worker
+{
+ public class GlobalStatisticsAggregationWorker : BackgroundService
+ {
+ private readonly ILogger logger;
+ private readonly IServiceProvider serviceProvider;
+
+ public GlobalStatisticsAggregationWorker(ILogger logger,
+ IServiceProvider serviceProvider)
+ {
+ this.logger = logger;
+ this.serviceProvider = serviceProvider;
+ }
+
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ var nextRun = DateTime.UtcNow;
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ if (nextRun <= DateTime.UtcNow)
+ {
+ await ExecuteAggregationAsync(nextRun, stoppingToken);
+ nextRun = nextRun.AddHours(12);
+ }
+
+ await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
+ }
+ }
+
+ private async Task ExecuteAggregationAsync(DateTime nextRun, CancellationToken stoppingToken)
+ {
+ try
+ {
+ using var scope = this.serviceProvider.CreateScope();
+ var command = new AggregateGlobalStatisticsCommand(nextRun.Month, nextRun.Year);
+ var mediator = scope.ServiceProvider.GetRequiredService();
+ await mediator.Send(command, stoppingToken);
+ }
+ catch (Exception e)
+ {
+ this.logger.LogCritical(e, "Error while aggregating global-statistics");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Covid19Api/Covid19Api.csproj b/src/Covid19Api/Covid19Api.csproj
index aaf6790..e946207 100644
--- a/src/Covid19Api/Covid19Api.csproj
+++ b/src/Covid19Api/Covid19Api.csproj
@@ -20,6 +20,7 @@
+
diff --git a/src/Covid19Api/Startup.cs b/src/Covid19Api/Startup.cs
index 78499f2..fbf2cc0 100644
--- a/src/Covid19Api/Startup.cs
+++ b/src/Covid19Api/Startup.cs
@@ -1,6 +1,9 @@
using System.IO.Compression;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using Autofac;
using AutoMapper.Contrib.Autofac.DependencyInjection;
+using Covid19Api.AutoMapper;
using Covid19Api.ExceptionFilter;
using Covid19Api.IoC.Extensions;
using Covid19Api.Middleware;
@@ -42,6 +45,11 @@ public void ConfigureServices(IServiceCollection services)
{
options.Filters.Add();
options.Filters.Add();
+ }).AddJsonOptions(options =>
+ {
+ options.JsonSerializerOptions.IgnoreNullValues = true;
+ options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
+ options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
});
services.AddSwaggerGen(options => options.SwaggerDoc(ApiVersion, new OpenApiInfo
@@ -70,7 +78,7 @@ public void ConfigureServices(IServiceCollection services)
public void ConfigureContainer(ContainerBuilder containerBuilder)
{
containerBuilder
- .RegisterAutoMapper(typeof(Startup).Assembly)
+ .RegisterAutoMapper(typeof(CountryStatsProfile).Assembly)
.RegisterMediatR(typeof(LoadLatestGlobalStatisticsQueryHandler).Assembly, typeof(CachingBehavior<,>))
.RegisterWorker()
.RegisterServices()