клиент для tansu
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
namespace Tnsu.ApprovalRoute.Infrastructure.TnsuPortal;
|
||||
|
||||
// Клиент api.tnsu.kz (api-tnsu-portal). GetEmployeeByEmailAsync и GetProjectExecutorsAsync — заглушки:
|
||||
// на момент написания у api-tnsu-portal нет эндпоинта GET /Employee/ByEmail (есть только ByIIN/Manager),
|
||||
// а форма ответа GET /Project/{oid} в его openapi-спеке не типизирована. Заменить на реальные вызовы,
|
||||
// когда эндпоинты будут готовы — токен уже прокидывается через TnsuPortalTokenProvider.
|
||||
public sealed class HttpTnsuPortalClient(HttpClient httpClient, TnsuPortalTokenProvider tokenProvider)
|
||||
{
|
||||
public Task<EmployeeDirectoryInfo> GetEmployeeByEmailAsync(string email, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(new EmployeeDirectoryInfo(
|
||||
DepartmentId: Guid.Parse("00000000-0000-0000-0000-000000000001"),
|
||||
CompanyId: Guid.Parse("00000000-0000-0000-0000-000000000002")));
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<ProjectExecutorInfo>> GetProjectExecutorsAsync(Guid projectId, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult<IReadOnlyList<ProjectExecutorInfo>>([]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using Tnsu.ApprovalRoute.Domain.Enums;
|
||||
|
||||
namespace Tnsu.ApprovalRoute.Infrastructure.TnsuPortal;
|
||||
|
||||
public sealed record EmployeeDirectoryInfo(Guid DepartmentId, Guid CompanyId);
|
||||
|
||||
public sealed record ProjectExecutorInfo(string Email, string Position, ProjectRole ProjectRole);
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Tnsu.ApprovalRoute.Infrastructure.TnsuPortal;
|
||||
|
||||
public sealed class TnsuPortalOptions
|
||||
{
|
||||
public string BaseUrl { get; set; } = "https://api.tnsu.kz";
|
||||
public string TenantId { get; set; } = string.Empty;
|
||||
public string ClientId { get; set; } = string.Empty;
|
||||
public string ClientSecret { get; set; } = string.Empty;
|
||||
public string Scope { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Tnsu.ApprovalRoute.Infrastructure.TnsuPortal;
|
||||
|
||||
// Сервисный (client-credentials) токен для вызова api.tnsu.kz — не связан с токеном конечного пользователя.
|
||||
public sealed class TnsuPortalTokenProvider(HttpClient httpClient, IOptions<TnsuPortalOptions> options)
|
||||
{
|
||||
private string? _cachedToken;
|
||||
private DateTimeOffset _expiresAt = DateTimeOffset.MinValue;
|
||||
|
||||
public async Task<string> GetAccessTokenAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (_cachedToken is not null && DateTimeOffset.UtcNow < _expiresAt)
|
||||
return _cachedToken;
|
||||
|
||||
var opts = options.Value;
|
||||
var response = await httpClient.PostAsync(
|
||||
$"https://login.microsoftonline.com/{opts.TenantId}/oauth2/v2.0/token",
|
||||
new FormUrlEncodedContent(new Dictionary<string, string>
|
||||
{
|
||||
["grant_type"] = "client_credentials",
|
||||
["client_id"] = opts.ClientId,
|
||||
["client_secret"] = opts.ClientSecret,
|
||||
["scope"] = opts.Scope
|
||||
}),
|
||||
cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var payload = await response.Content.ReadFromJsonAsync<TokenResponse>(cancellationToken)
|
||||
?? throw new InvalidOperationException("Не удалось получить токен для api.tnsu.kz.");
|
||||
|
||||
_cachedToken = payload.AccessToken;
|
||||
_expiresAt = DateTimeOffset.UtcNow.AddSeconds(payload.ExpiresIn - 60);
|
||||
|
||||
return _cachedToken;
|
||||
}
|
||||
|
||||
private sealed record TokenResponse(
|
||||
[property: JsonPropertyName("access_token")] string AccessToken,
|
||||
[property: JsonPropertyName("expires_in")] int ExpiresIn);
|
||||
}
|
||||
Reference in New Issue
Block a user