This adds five new free API integrations for enhanced CV verification: - FCA Register API: Verify financial services professionals - SRA Register API: Verify solicitors and legal professionals - GitHub API: Verify developer profiles and technical skills - OpenCorporates API: Verify international companies across jurisdictions - ORCID API: Verify academic researchers and publications Includes: - API clients for all five services with retry policies - Service implementations with name matching and validation - Models for verification results with detailed flags - Configuration options in appsettings.json - DI registration for all services 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
170 lines
6.8 KiB
C#
170 lines
6.8 KiB
C#
using Hangfire;
|
|
using Hangfire.SqlServer;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Polly;
|
|
using Polly.Extensions.Http;
|
|
using RealCV.Application.Interfaces;
|
|
using RealCV.Infrastructure.Clients;
|
|
using RealCV.Infrastructure.Configuration;
|
|
using RealCV.Infrastructure.Data;
|
|
using RealCV.Infrastructure.ExternalApis;
|
|
using RealCV.Infrastructure.Jobs;
|
|
using RealCV.Infrastructure.Services;
|
|
|
|
namespace RealCV.Infrastructure;
|
|
|
|
public static class DependencyInjection
|
|
{
|
|
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
|
|
{
|
|
// Configure DbContext with SQL Server
|
|
// AddDbContextFactory enables thread-safe parallel operations
|
|
services.AddDbContextFactory<ApplicationDbContext>(options =>
|
|
options.UseSqlServer(
|
|
configuration.GetConnectionString("DefaultConnection"),
|
|
sqlOptions =>
|
|
{
|
|
sqlOptions.EnableRetryOnFailure(
|
|
maxRetryCount: 3,
|
|
maxRetryDelay: TimeSpan.FromSeconds(30),
|
|
errorNumbersToAdd: null);
|
|
}));
|
|
|
|
// Also register DbContext for scoped injection (non-parallel scenarios)
|
|
services.AddDbContext<ApplicationDbContext>(options =>
|
|
options.UseSqlServer(
|
|
configuration.GetConnectionString("DefaultConnection"),
|
|
sqlOptions =>
|
|
{
|
|
sqlOptions.EnableRetryOnFailure(
|
|
maxRetryCount: 3,
|
|
maxRetryDelay: TimeSpan.FromSeconds(30),
|
|
errorNumbersToAdd: null);
|
|
}));
|
|
|
|
// Configure Hangfire with SQL Server storage
|
|
services.AddHangfire(config => config
|
|
.SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
|
|
.UseSimpleAssemblyNameTypeSerializer()
|
|
.UseRecommendedSerializerSettings()
|
|
.UseSqlServerStorage(
|
|
configuration.GetConnectionString("HangfireConnection"),
|
|
new SqlServerStorageOptions
|
|
{
|
|
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
|
|
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
|
|
QueuePollInterval = TimeSpan.Zero,
|
|
UseRecommendedIsolationLevel = true,
|
|
DisableGlobalLocks = true
|
|
}));
|
|
|
|
services.AddHangfireServer();
|
|
|
|
// Configure options
|
|
services.Configure<CompaniesHouseSettings>(
|
|
configuration.GetSection(CompaniesHouseSettings.SectionName));
|
|
|
|
services.Configure<AnthropicSettings>(
|
|
configuration.GetSection(AnthropicSettings.SectionName));
|
|
|
|
services.Configure<AzureBlobSettings>(
|
|
configuration.GetSection(AzureBlobSettings.SectionName));
|
|
|
|
services.Configure<LocalStorageSettings>(
|
|
configuration.GetSection(LocalStorageSettings.SectionName));
|
|
|
|
// Configure options for additional verification APIs
|
|
services.Configure<FcaOptions>(
|
|
configuration.GetSection("FcaRegister"));
|
|
|
|
services.Configure<GitHubOptions>(
|
|
configuration.GetSection("GitHub"));
|
|
|
|
services.Configure<OpenCorporatesOptions>(
|
|
configuration.GetSection("OpenCorporates"));
|
|
|
|
// Configure HttpClient for CompaniesHouseClient with retry policy
|
|
services.AddHttpClient<CompaniesHouseClient>((serviceProvider, client) =>
|
|
{
|
|
var settings = configuration
|
|
.GetSection(CompaniesHouseSettings.SectionName)
|
|
.Get<CompaniesHouseSettings>();
|
|
|
|
if (settings is not null)
|
|
{
|
|
client.BaseAddress = new Uri(settings.BaseUrl);
|
|
}
|
|
})
|
|
.AddPolicyHandler(GetRetryPolicy());
|
|
|
|
// Configure HttpClient for FCA Register API
|
|
services.AddHttpClient<FcaRegisterClient>()
|
|
.AddPolicyHandler(GetRetryPolicy());
|
|
|
|
// Configure HttpClient for SRA Register API
|
|
services.AddHttpClient<SraRegisterClient>()
|
|
.AddPolicyHandler(GetRetryPolicy());
|
|
|
|
// Configure HttpClient for GitHub API
|
|
services.AddHttpClient<GitHubApiClient>()
|
|
.AddPolicyHandler(GetRetryPolicy());
|
|
|
|
// Configure HttpClient for OpenCorporates API
|
|
services.AddHttpClient<OpenCorporatesClient>()
|
|
.AddPolicyHandler(GetRetryPolicy());
|
|
|
|
// Configure HttpClient for ORCID API
|
|
services.AddHttpClient<OrcidClient>()
|
|
.AddPolicyHandler(GetRetryPolicy());
|
|
|
|
// Register services
|
|
services.AddScoped<ICVParserService, CVParserService>();
|
|
services.AddScoped<ICompanyNameMatcherService, AICompanyNameMatcherService>();
|
|
services.AddScoped<ICompanyVerifierService, CompanyVerifierService>();
|
|
services.AddScoped<IEducationVerifierService, EducationVerifierService>();
|
|
services.AddScoped<ITimelineAnalyserService, TimelineAnalyserService>();
|
|
services.AddScoped<ICVCheckService, CVCheckService>();
|
|
services.AddScoped<IUserContextService, UserContextService>();
|
|
services.AddScoped<IAuditService, AuditService>();
|
|
|
|
// Register additional verification services
|
|
services.AddScoped<IProfessionalVerifierService, ProfessionalVerifierService>();
|
|
services.AddScoped<IGitHubVerifierService, GitHubVerifierService>();
|
|
services.AddScoped<IInternationalCompanyVerifierService, InternationalCompanyVerifierService>();
|
|
services.AddScoped<IAcademicVerifierService, AcademicVerifierService>();
|
|
|
|
// Register file storage - use local storage if configured, otherwise Azure
|
|
var useLocalStorage = configuration.GetValue<bool>("UseLocalStorage");
|
|
if (useLocalStorage)
|
|
{
|
|
services.AddScoped<IFileStorageService, LocalFileStorageService>();
|
|
}
|
|
else
|
|
{
|
|
services.AddScoped<IFileStorageService, FileStorageService>();
|
|
}
|
|
|
|
// Register Hangfire jobs
|
|
services.AddTransient<ProcessCVCheckJob>();
|
|
|
|
return services;
|
|
}
|
|
|
|
private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
|
|
{
|
|
return HttpPolicyExtensions
|
|
.HandleTransientHttpError()
|
|
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
|
|
.WaitAndRetryAsync(
|
|
retryCount: 3,
|
|
sleepDurationProvider: retryAttempt =>
|
|
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
|
|
onRetry: (outcome, timespan, retryAttempt, context) =>
|
|
{
|
|
// Logging could be added here via ILogger if injected
|
|
});
|
|
}
|
|
}
|