feat: Add additional verification APIs (FCA, SRA, GitHub, OpenCorporates, ORCID)

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>
This commit is contained in:
2026-01-24 13:05:52 +00:00
parent 8a4e46d872
commit 5d2965beae
19 changed files with 3249 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
namespace RealCV.Application.Models;
/// <summary>
/// Result of verifying an academic researcher via ORCID
/// </summary>
public sealed record AcademicVerificationResult
{
public required string ClaimedName { get; init; }
public required bool IsVerified { get; init; }
// ORCID profile
public string? OrcidId { get; init; }
public string? MatchedName { get; init; }
public string? OrcidUrl { get; init; }
// Academic affiliations
public List<AcademicAffiliation> Affiliations { get; init; } = [];
// Publications
public int TotalPublications { get; init; }
public List<Publication> RecentPublications { get; init; } = [];
// Education from ORCID
public List<AcademicEducation> Education { get; init; } = [];
public string? VerificationNotes { get; init; }
public List<AcademicVerificationFlag> Flags { get; init; } = [];
}
public sealed record AcademicAffiliation
{
public required string Organization { get; init; }
public string? Department { get; init; }
public string? Role { get; init; }
public DateOnly? StartDate { get; init; }
public DateOnly? EndDate { get; init; }
}
public sealed record Publication
{
public required string Title { get; init; }
public string? Journal { get; init; }
public int? Year { get; init; }
public string? Doi { get; init; }
public string? Type { get; init; }
}
public sealed record AcademicEducation
{
public required string Institution { get; init; }
public string? Degree { get; init; }
public string? Subject { get; init; }
public int? Year { get; init; }
}
public sealed record AcademicVerificationFlag
{
public required string Type { get; init; }
public required string Severity { get; init; }
public required string Message { get; init; }
public int ScoreImpact { get; init; }
}

View File

@@ -0,0 +1,52 @@
namespace RealCV.Application.Models;
/// <summary>
/// Result of verifying a developer's GitHub profile
/// </summary>
public sealed record GitHubVerificationResult
{
public required string ClaimedUsername { get; init; }
public required bool IsVerified { get; init; }
// Profile details
public string? ProfileName { get; init; }
public string? ProfileUrl { get; init; }
public string? Bio { get; init; }
public string? Company { get; init; }
public string? Location { get; init; }
public DateOnly? AccountCreated { get; init; }
// Activity metrics
public int PublicRepos { get; init; }
public int Followers { get; init; }
public int Following { get; init; }
public int TotalContributions { get; init; }
// Language breakdown
public Dictionary<string, int> LanguageStats { get; init; } = new();
// Claimed skills verification
public List<SkillVerification> SkillVerifications { get; init; } = [];
public string? VerificationNotes { get; init; }
public List<GitHubVerificationFlag> Flags { get; init; } = [];
}
public sealed record SkillVerification
{
public required string ClaimedSkill { get; init; }
public required bool IsVerified { get; init; }
public int RepoCount { get; init; }
public int TotalLinesOfCode { get; init; }
public DateOnly? FirstUsed { get; init; }
public DateOnly? LastUsed { get; init; }
public string? Notes { get; init; }
}
public sealed record GitHubVerificationFlag
{
public required string Type { get; init; }
public required string Severity { get; init; }
public required string Message { get; init; }
public int ScoreImpact { get; init; }
}

View File

@@ -0,0 +1,30 @@
namespace RealCV.Application.Models;
/// <summary>
/// Result of verifying an international company via OpenCorporates
/// </summary>
public sealed record InternationalCompanyResult
{
public required string ClaimedCompany { get; init; }
public required string ClaimedJurisdiction { get; init; }
public required bool IsVerified { get; init; }
// Matched company details
public string? MatchedCompanyName { get; init; }
public string? CompanyNumber { get; init; }
public string? Jurisdiction { get; init; }
public string? JurisdictionCode { get; init; }
public string? CompanyType { get; init; }
public string? Status { get; init; }
public DateOnly? IncorporationDate { get; init; }
public DateOnly? DissolutionDate { get; init; }
public string? RegisteredAddress { get; init; }
// OpenCorporates specific
public string? OpenCorporatesUrl { get; init; }
public DateTime? DataLastUpdated { get; init; }
public int MatchScore { get; init; }
public string? VerificationNotes { get; init; }
public List<CompanyVerificationFlag> Flags { get; init; } = [];
}

View File

@@ -0,0 +1,38 @@
namespace RealCV.Application.Models;
/// <summary>
/// Result of verifying a professional qualification (FCA, SRA, etc.)
/// </summary>
public sealed record ProfessionalVerificationResult
{
public required string ClaimedName { get; init; }
public required string ProfessionalBody { get; init; }
public required bool IsVerified { get; init; }
// Matched professional details
public string? MatchedName { get; init; }
public string? RegistrationNumber { get; init; }
public string? Status { get; init; }
public string? CurrentEmployer { get; init; }
public DateOnly? RegistrationDate { get; init; }
// For FCA
public List<string>? ApprovedFunctions { get; init; }
public List<string>? ControlledFunctions { get; init; }
// For SRA
public string? SolicitorType { get; init; }
public string? AdmissionDate { get; init; }
public string? PractisingCertificateStatus { get; init; }
public string? VerificationNotes { get; init; }
public List<ProfessionalVerificationFlag> Flags { get; init; } = [];
}
public sealed record ProfessionalVerificationFlag
{
public required string Type { get; init; }
public required string Severity { get; init; }
public required string Message { get; init; }
public int ScoreImpact { get; init; }
}