@@ -1,14 +1,23 @@
using System.Net ;
using System.Net ;
using System.Net.Http.Headers ;
using System.Net.Http.Headers ;
using System.Net.Http.Json ;
using System.Net.Http.Json ;
using System.Text.Json ;
using System.Text.Json.Serialization ;
using System.Text.Json.Serialization ;
using Microsoft.AspNetCore.Http ;
using Tnsu.ApprovalRoute.Domain.Enums ;
using Tnsu.ApprovalRoute.Domain.Enums ;
namespace Tnsu.ApprovalRoute.Infrastructure.TnsuPortal ;
namespace Tnsu.ApprovalRoute.Infrastructure.TnsuPortal ;
// Клиент api.tnsu.kz (api-tnsu-portal). Сервисный токен прокидывается через TnsuPortalTokenProvider.
// Клиент api.tnsu.kz (api-tnsu-portal).
public sealed class HttpTnsuPortalClient ( HttpClient httpClient , TnsuPortalTokenProvider tokenProvider ) : ITnsuPortalClient
public sealed class HttpTnsuPortalClient (
HttpClient httpClient ,
TnsuPortalTokenProvider tokenProvider ,
IHttpContextAccessor httpContextAccessor ) : ITnsuPortalClient
{
{
// Разные эндпоинты api-tnsu-portal отдают JSON в разном регистре (Dapper-результаты — PascalCase,
// строго типизированные модели — camelCase), поэтому сопоставляем поля без учёта регистра.
private static readonly JsonSerializerOptions JsonOptions = new ( ) { PropertyNameCaseInsensitive = true } ;
public async Task < EmployeeDirectoryInfo ? > GetEmployeeByEmailAsync ( string email , CancellationToken cancellationToken )
public async Task < EmployeeDirectoryInfo ? > GetEmployeeByEmailAsync ( string email , CancellationToken cancellationToken )
{
{
using var request = new HttpRequestMessage ( HttpMethod . Get , $"/Employee/ByEmail?email={Uri.EscapeDataString(email)}" ) ;
using var request = new HttpRequestMessage ( HttpMethod . Get , $"/Employee/ByEmail?email={Uri.EscapeDataString(email)}" ) ;
@@ -19,7 +28,7 @@ public sealed class HttpTnsuPortalClient(HttpClient httpClient, TnsuPortalTokenP
return null ;
return null ;
response . EnsureSuccessStatusCode ( ) ;
response . EnsureSuccessStatusCode ( ) ;
var employee = await response . Content . ReadFromJsonAsync < EmployeeResponse > ( cancellationToken ) ;
var employee = await response . Content . ReadFromJsonAsync < EmployeeResponse > ( JsonOptions , cancellationToken ) ;
if ( employee is null )
if ( employee is null )
return null ;
return null ;
@@ -36,7 +45,7 @@ public sealed class HttpTnsuPortalClient(HttpClient httpClient, TnsuPortalTokenP
return [ ] ;
return [ ] ;
response . EnsureSuccessStatusCode ( ) ;
response . EnsureSuccessStatusCode ( ) ;
var project = await response . Content . ReadFromJsonAsync < ProjectResponse > ( cancellationToken ) ;
var project = await response . Content . ReadFromJsonAsync < ProjectResponse > ( JsonOptions , cancellationToken ) ;
if ( project is null )
if ( project is null )
return [ ] ;
return [ ] ;
@@ -56,8 +65,18 @@ public sealed class HttpTnsuPortalClient(HttpClient httpClient, TnsuPortalTokenP
executors . Add ( new ProjectExecutorInfo ( role , email ) ) ;
executors . Add ( new ProjectExecutorInfo ( role , email ) ) ;
}
}
// ВРЕМЕННО: для TnsuPortal ещё нет своего client-credentials приложения в Entra, поэтому прокидываем
// токен текущего пользователя дальше — его aud совпадает с ClientId api-tnsu-portal, так что подходит.
// Когда появится своё приложение — вернуть на tokenProvider.GetAccessTokenAsync(...).
private async Task AuthorizeAsync ( HttpRequestMessage request , CancellationToken cancellationToken )
private async Task AuthorizeAsync ( HttpRequestMessage request , CancellationToken cancellationToken )
{
{
var incoming = httpContextAccessor . HttpContext ? . Request . Headers . Authorization . ToString ( ) ;
if ( ! string . IsNullOrWhiteSpace ( incoming ) )
{
request . Headers . TryAddWithoutValidation ( "Authorization" , incoming ) ;
return ;
}
var token = await tokenProvider . GetAccessTokenAsync ( cancellationToken ) ;
var token = await tokenProvider . GetAccessTokenAsync ( cancellationToken ) ;
request . Headers . Authorization = new AuthenticationHeaderValue ( "Bearer" , token ) ;
request . Headers . Authorization = new AuthenticationHeaderValue ( "Bearer" , token ) ;
}
}