using interfaces; using Microsoft.Extensions.Options; using options; namespace services; internal class SmappeeTokenService : IBearerTokenService { private readonly ChargerOptions options; private readonly IHttpClientFactory httpClientFactory; private TokenData? tokenData = null; public SmappeeTokenService( IOptions options, IHttpClientFactory httpClientFactory ) { this.options = options?.Value ?? throw new ArgumentNullException(nameof(options)); this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory)); } public async ValueTask GetToken(bool forceNewToken) { if ( tokenData == null || tokenData.Expiration < DateTime.UtcNow.AddSeconds(-10) || forceNewToken ) { tokenData = await RequestNewToken(); } return tokenData.Token; } private async Task RequestNewToken() { var httpClient = httpClientFactory.CreateClient("smappeeTokenService"); httpClient.BaseAddress = options.Url; var result = await httpClient.PostAsJsonAsync( "/dev/v3/oauth2/token", new TokenRequestBody( "password", options.ClientId, options.ClientSecret, options.UserName, options.Password ) ); result.EnsureSuccessStatusCode(); var tokenResponse = await result.Content.ReadFromJsonAsync() ?? throw new InvalidOperationException("Token could not be fetched"); return new TokenData( tokenResponse.access_token, DateTime.UtcNow.AddSeconds(tokenResponse.expires_in) ); } record TokenRequestBody( string grant_type, string client_id, string client_secret, string username, string password ); record TokenResponse(string access_token, int expires_in); record TokenData(string Token, DateTime Expiration); }