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
2 changes: 2 additions & 0 deletions backend/Infrastructure/Core/DependencyInjection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Domain.Core.Abstractions;
using Infrastructure.Core.Time;
using Infrastructure.Database;
using Infrastructure.Database.Seeding;
using Infrastructure.Documents;
using Infrastructure.Reports;
using Microsoft.EntityFrameworkCore;
Expand Down Expand Up @@ -31,6 +32,7 @@ IConfiguration configuration
{
services.AddSingleton<IDateTimeProvider, DateTimeProvider>();
services.AddScoped<ExcelService>();
services.AddScoped<SeedingService>();
services.AddScoped<IReportsService, ReportsService>();

services.AddDatabase(configuration);
Expand Down
64 changes: 64 additions & 0 deletions backend/Infrastructure/Database/Seeding/SeedingService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using Domain.Contacts;
using Domain.Core.Primitives;
using Infrastructure.Documents;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace Infrastructure.Database.Seeding;

/// <summary>
/// Represents the seeding service.
/// </summary>
/// <param name="excelService">The excel service.</param>
/// <param name="context">The EF Core database context.</param>
/// <param name="logger">The logger used for diagnostic and error output.</param>
public class SeedingService(
ExcelService excelService,
PhoneForgeDbContext context,
ILogger<SeedingService> logger
)
{
/// <summary>
/// Reads contacts from the Excel document and seeds them into the database.
/// </summary>
/// <remarks>
/// This method attempts to load contacts using <see cref="ExcelService.GetContacts"/>.
/// If loading fails for any reason (e.g., invalid data),
/// the method exits without modifying the database.
/// Seeding is skipped when the contacts table already contains data.
/// </remarks>
/// <returns>
/// A task representing the asynchronous operation.
/// The task completes when the contacts have been saved,
/// or exits early if excel parsing failed or the database already contains contacts.
/// </returns>
public async Task SeedContacts()
{
logger.LogInformation("Seeding contacts to the database.");

Result<List<Contact>> getContactsResult = await excelService.GetContacts();

if (getContactsResult.IsFailure)
{
return;
}

List<Contact> contacts = getContactsResult.Value;

if (await context.Contacts.AnyAsync())
{
logger.LogWarning(
"Contacts already exists in the database. Aborting process."
);
return;
}

context.AddRange(contacts);

await context.SaveChangesAsync();
logger.LogInformation(
"Successfully seeded {Count} contacts to the database.",
contacts.Count
);
}
}
49 changes: 29 additions & 20 deletions backend/Infrastructure/Documents/ExcelService.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
using ClosedXML.Excel;
using Domain.Contacts;
using Domain.Core.Primitives;
using Infrastructure.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace Infrastructure.Documents;

/// <summary>
/// Provides functionality for interacting with excel documents.
/// Represents the excel service.
/// </summary>
/// <param name="context">The database context.</param>
/// <param name="logger">The logger used for diagnostic and error output</param>
public class ExcelService(PhoneForgeDbContext context, ILogger<ExcelService> logger)
/// <param name="logger">The logger used for diagnostic and error output.</param>
public class ExcelService(ILogger<ExcelService> logger)
{
/// <summary>
/// Seeds data from excel spreadsheet to the database.
/// Reads contacts from the Excel document and loads them into memory.
/// </summary>
/// <returns>The result of seeding process or an error.</returns>
public async Task SeedExcelDataAsync()
/// <remarks>
/// This method processes each row of the Excel worksheet and attempts to
/// create a <see cref="Contact"/> instance using the domain value objects.
/// If any validation error occurs while reading a row, the operation is aborted
/// and the error is logged.
/// </remarks>
/// <returns>A <see cref="Result"/> containing a list of successfully parsed <see cref="Contact"/> entities,
/// or an error describing why loading failed.
/// </returns>
public async Task<Result<List<Contact>>> GetContacts()
{
logger.LogInformation("Started seeding the database from excel document.");

if (await context.Contacts.AnyAsync())
{
logger.LogWarning("Data already exists in the database. Aborting process.");
return;
}
logger.LogInformation("Loading contacts from excel document.");

List<Contact> contacts = [];

Expand Down Expand Up @@ -55,7 +54,7 @@ public async Task SeedExcelDataAsync()
row.RowNumber(),
firstFailOrSuccess.Error
);
return;
return firstFailOrSuccess.Error;
}

Contact contact = Contact.Create(
Expand All @@ -68,10 +67,20 @@ public async Task SeedExcelDataAsync()
contacts.Add(contact);
}

context.AddRange(contacts);
if (contacts.Count == 0)
{
logger.LogInformation("No contacts found in excel document.");
return Error.Failure(
"ExcelDocument.Empty",
"No contacts found in excel document."
);
}

await context.SaveChangesAsync();
logger.LogInformation(
"Successfully retrieved {Count} contacts from excel document.",
contacts.Count
);

logger.LogInformation("Successfully seeded the database from excel document.");
return contacts;
}
}
1 change: 1 addition & 0 deletions backend/Infrastructure/Infrastructure.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<PackageReference Include="PuppeteerSharp" />
</ItemGroup>
<ItemGroup>
<Folder Include="Database\Seeding\" />
<Folder Include="Reports\" />
</ItemGroup>
</Project>
8 changes: 4 additions & 4 deletions backend/WebApi/Core/Extensions/MiddlewareExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Infrastructure.Database;
using Infrastructure.Documents;
using Infrastructure.Database.Seeding;
using Microsoft.EntityFrameworkCore;
using Serilog;
using WebApi.Core.Middleware;
Expand Down Expand Up @@ -82,9 +82,9 @@ private static async Task SeedDatabase(this IApplicationBuilder app)
{
using IServiceScope scope = app.ApplicationServices.CreateScope();

ExcelService excelService =
scope.ServiceProvider.GetRequiredService<ExcelService>();
SeedingService excelService =
scope.ServiceProvider.GetRequiredService<SeedingService>();

await excelService.SeedExcelDataAsync();
await excelService.SeedContacts();
}
}