using MediatR; using Microsoft.Extensions.Configuration; using ReC.Application.Common; using ReC.Application.Common.Constants; using ReC.Application.Common.Dto; using ReC.Application.Common.Exceptions; using ReC.Application.Common.Procedures.InsertProcedure; using ReC.Application.Results.Commands; using ReC.Domain.Constants; using System.Net; using System.Net.Http.Headers; using System.Text; using System.Text.Json; namespace ReC.Application.RecActions.Commands; public record InvokeRecActionViewCommand : IRequest { public RecActionViewDto Action { get; set; } = null!; } public static class InvokeRecActionViewCommandExtensions { public static InvokeRecActionViewCommand ToInvokeCommand(this RecActionViewDto dto) => new() { Action = dto }; } public class InvokeRecActionViewCommandHandler( ISender sender, IHttpClientFactory clientFactory, IConfiguration? config = null ) : IRequestHandler { public async Task Handle(InvokeRecActionViewCommand request, CancellationToken cancel) { var action = request.Action; using var http = clientFactory.CreateClient(Http.ClientName); if (action.RestType is not RestType restType) throw new DataIntegrityException( $"Rec action could not be invoked because the RestType value is null. " + $"ProfileId: {action.ProfileId}, " + $"Id: {action.Id}" ); using var httpReq = restType .ToHttpMethod() .ToHttpRequestMessage(action.EndpointUri); if (action.Body is not null) { using var reqBody = new StringContent(action.Body); httpReq.Content = reqBody; } if (action.Headers is not null) foreach (var header in action.Headers) httpReq.Headers.Add(header.Key, header.Value); switch (action.EndpointAuthType) { case EndpointAuthType.NoAuth: break; case EndpointAuthType.ApiKey: if (action.EndpointAuthApiKey is string apiKey && action.EndpointAuthApiValue is string apiValue) { switch (action.EndpointAuthApiKeyAddTo) { case ApiKeyLocation.Header: httpReq.Headers.Add(apiKey, apiValue); break; case ApiKeyLocation.Query: var uriBuilder = new UriBuilder(httpReq.RequestUri!); var query = System.Web.HttpUtility.ParseQueryString(uriBuilder.Query); query[apiKey] = apiValue; uriBuilder.Query = query.ToString(); httpReq.RequestUri = uriBuilder.Uri; break; default: throw new DataIntegrityException( $"The API key location '{action.EndpointAuthApiKeyAddTo}' is not supported. " + $"ProfileId: {action.ProfileId}, " + $"Id: {action.Id}" ); } } break; case EndpointAuthType.BearerToken: case EndpointAuthType.JwtBearer: case EndpointAuthType.OAuth2: if (action.EndpointAuthToken is string authToken) httpReq.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken); break; case EndpointAuthType.BasicAuth: if (action.EndpointAuthUsername is string authUsername && action.EndpointAuthPassword is string authPassword) { var basicAuth = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{authUsername}:{authPassword}")); httpReq.Headers.Authorization = new AuthenticationHeaderValue("Basic", basicAuth); } break; case EndpointAuthType.NtlmAuth: if (!string.IsNullOrWhiteSpace(action.EndpointAuthUsername)) { var credentials = new NetworkCredential( action.EndpointAuthUsername, action.EndpointAuthPassword, action.EndpointAuthDomain); var credentialCache = new CredentialCache { { httpReq.RequestUri!, "NTLM", credentials } }; httpReq.Options.Set(new HttpRequestOptionsKey("Credentials"), credentialCache); } break; case EndpointAuthType.DigestAuth: case EndpointAuthType.OAuth1: case EndpointAuthType.AwsSignature: // These authentication methods require more complex implementations, // often involving multi-step handshakes or specialized libraries. // They are left as placeholders for future implementation. default: throw new NotImplementedException( $"The authentication type '{action.EndpointAuthType}' is not supported yet. " + $"ProfileId: {action.ProfileId}, " + $"Id: {action.Id}" ); } using var response = await http.SendAsync(httpReq, cancel); var resBody = await response.Content.ReadAsStringAsync(cancel); var resHeaders = response.Headers.ToDictionary(); var statusCode = (short)response.StatusCode; await sender.ExecuteInsertProcedure(new InsertResultProcedure() { StatusId = statusCode, ActionId = action.Id, Header = JsonSerializer.Serialize(resHeaders, options: new() { WriteIndented = false }), Body = resBody }, config?["AddedWho"], cancel); return response.IsSuccessStatusCode; } }