diff --git a/Lacuna.FocusNFSeIntegration.AppTest/Classes/TestHelpers.cs b/Lacuna.FocusNFSeIntegration.AppTest/Classes/TestHelpers.cs index 65cd47c..5783f09 100644 --- a/Lacuna.FocusNFSeIntegration.AppTest/Classes/TestHelpers.cs +++ b/Lacuna.FocusNFSeIntegration.AppTest/Classes/TestHelpers.cs @@ -1,11 +1,8 @@ -using Lacuna.FocusNFSeIntegration; -using Lacuna.FocusNFSeIntegration.Models; +using Lacuna.FocusNFSeIntegration.Models; using System; using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -namespace Lacuna.FocusNFeIntegration.AppTest.Classes { +namespace Lacuna.FocusNFSeIntegration.AppTest.Classes { public class TestHelpers { public static NFSeRequest GenerateRequest(FocusNFSeIntegrationOptions options, bool hasCnpj = true) { @@ -31,7 +28,7 @@ public static ServiceInfo GenerateServiceInfo(FocusNFSeIntegrationOptions option } public static ClientInfo GenerateClientInfo(FocusNFSeIntegrationOptions options, bool hasCnpj = true) { - var info = new ClientInfo { + var info = new ClientInfo { CompanyNameOrClientName = "LACUNA SOFTWARE LTDA - EPP", Email = "adm@lacunasoftware.com", AddressInfo = new AddressInfo { @@ -50,7 +47,7 @@ public static ClientInfo GenerateClientInfo(FocusNFSeIntegrationOptions options, info.StateSubscription = "144408646118"; } else { info.Cpf = "55500000160"; - } + } return info; } diff --git a/Lacuna.FocusNFSeIntegration.AppTest/Controllers/FocusNFSeController.cs b/Lacuna.FocusNFSeIntegration.AppTest/Controllers/FocusNFSeController.cs index 2341ce3..44172ed 100644 --- a/Lacuna.FocusNFSeIntegration.AppTest/Controllers/FocusNFSeController.cs +++ b/Lacuna.FocusNFSeIntegration.AppTest/Controllers/FocusNFSeController.cs @@ -1,10 +1,7 @@ -using Lacuna.FocusNFeIntegration.AppTest.Classes; -using Microsoft.AspNetCore.Http; +using Lacuna.FocusNFSeIntegration.AppTest.Classes; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using System; -using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; namespace Lacuna.FocusNFSeIntegration.AppTest.Controllers { @@ -21,7 +18,7 @@ public FocusNFSeController(FocusNFSeClient focusClient, IOptions SubmitNFSeAsync(string reference, [FromQuery]bool hasCnpj = true) { + public async Task SubmitNFSeAsync(string reference, [FromQuery] bool hasCnpj = true) { var req = TestHelpers.GenerateRequest(focusOptions.Value, hasCnpj); var retorno = await focusClient.CreateNFSeAsync(reference, req); diff --git a/Lacuna.FocusNFSeIntegration.AppTest/Controllers/SampleDataController.cs b/Lacuna.FocusNFSeIntegration.AppTest/Controllers/SampleDataController.cs index d4feb38..418d13d 100644 --- a/Lacuna.FocusNFSeIntegration.AppTest/Controllers/SampleDataController.cs +++ b/Lacuna.FocusNFSeIntegration.AppTest/Controllers/SampleDataController.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace Lacuna.FocusNFSeIntegration.AppTest.Controllers { [Route("api/[controller]")] diff --git a/Lacuna.FocusNFSeIntegration.AppTest/Lacuna.FocusNFSeIntegration.AppTest.csproj b/Lacuna.FocusNFSeIntegration.AppTest/Lacuna.FocusNFSeIntegration.AppTest.csproj index a048575..b2fad25 100644 --- a/Lacuna.FocusNFSeIntegration.AppTest/Lacuna.FocusNFSeIntegration.AppTest.csproj +++ b/Lacuna.FocusNFSeIntegration.AppTest/Lacuna.FocusNFSeIntegration.AppTest.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + net6.0 true Latest false @@ -10,15 +10,19 @@ false - Lacuna.FocusNFeIntegration.AppTest - Lacuna.FocusNFeIntegration.AppTest + Lacuna.FocusNFSeIntegration.AppTest + Lacuna.FocusNFSeIntegration.AppTest ae90e351-4401-4819-b96e-5cda579fd657 + + + + diff --git a/Lacuna.FocusNFSeIntegration.AppTest/Pages/_ViewImports.cshtml b/Lacuna.FocusNFSeIntegration.AppTest/Pages/_ViewImports.cshtml index 7b9f633..e69a271 100644 --- a/Lacuna.FocusNFSeIntegration.AppTest/Pages/_ViewImports.cshtml +++ b/Lacuna.FocusNFSeIntegration.AppTest/Pages/_ViewImports.cshtml @@ -1,3 +1,3 @@ -@using Lacuna.FocusNFSeIntegration -@namespace Lacuna.FocusNFSeIntegration.AppTest.Pages +@using Lacuna.FocusNFSeIntegration.AppTest +@using Lacuna.FocusNFSeIntegration.AppTest.Pages @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/Lacuna.FocusNFSeIntegration.AppTest/Program.cs b/Lacuna.FocusNFSeIntegration.AppTest/Program.cs index 5ce2b42..0ea560a 100644 --- a/Lacuna.FocusNFSeIntegration.AppTest/Program.cs +++ b/Lacuna.FocusNFSeIntegration.AppTest/Program.cs @@ -1,12 +1,5 @@ using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; namespace Lacuna.FocusNFSeIntegration.AppTest { public class Program { diff --git a/Lacuna.FocusNFSeIntegration.AppTest/Startup.cs b/Lacuna.FocusNFSeIntegration.AppTest/Startup.cs index d57e105..40ca8ee 100644 --- a/Lacuna.FocusNFSeIntegration.AppTest/Startup.cs +++ b/Lacuna.FocusNFSeIntegration.AppTest/Startup.cs @@ -1,8 +1,5 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpsPolicy; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.SpaServices.AngularCli; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -16,8 +13,7 @@ public Startup(IConfiguration configuration) { // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); - + services.AddControllersWithViews(); // Configuring the Focus NFSe Integration Options from the appsettings services.Configure(Configuration.GetSection("Focus")); // Creating the Focus NFSe Integration Client as a Singleton and available for the Services and controllers to use it (Asp.Net Core configuration library option) @@ -27,6 +23,9 @@ public void ConfigureServices(IServiceCollection services) { services.AddSpaStaticFiles(configuration => { configuration.RootPath = "ClientApp/dist"; }); + + services.AddEndpointsApiExplorer(); + services.AddSwaggerGen(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -41,23 +40,16 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseHttpsRedirection(); app.UseStaticFiles(); - app.UseSpaStaticFiles(); - app.UseMvc(routes => { - routes.MapRoute( - name: "default", - template: "{controller}/{action=Index}/{id?}"); - }); + app.UseSwagger(); + app.UseSwaggerUI(); + app.UseRouting(); - app.UseSpa(spa => { - // To learn more about options for serving an Angular SPA from ASP.NET Core, - // see https://go.microsoft.com/fwlink/?linkid=864501 - - spa.Options.SourcePath = "ClientApp"; - - if (env.IsDevelopment()) { - spa.UseAngularCliServer(npmScript: "start"); - } + app.UseEndpoints(endpoints => { + endpoints.MapControllerRoute( + name: "default", + pattern: "{controller}/{action=Index}/{id?}" + ); }); } } diff --git a/Lacuna.FocusNFSeIntegration/Constants.cs b/Lacuna.FocusNFSeIntegration/Constants.cs new file mode 100644 index 0000000..49e310a --- /dev/null +++ b/Lacuna.FocusNFSeIntegration/Constants.cs @@ -0,0 +1,7 @@ +namespace Lacuna.FocusNFSeIntegration { + public static class Constants { + public static readonly string MediaType = "application/json"; + public static readonly string FactoryClientName = "FocusNFSeIntegration:Client"; + public static readonly string CharSet = "UTF-8"; + } +} diff --git a/Lacuna.FocusNFSeIntegration/FocusNFSeClient.cs b/Lacuna.FocusNFSeIntegration/FocusNFSeClient.cs index 19b95fd..7b23731 100644 --- a/Lacuna.FocusNFSeIntegration/FocusNFSeClient.cs +++ b/Lacuna.FocusNFSeIntegration/FocusNFSeClient.cs @@ -1,72 +1,48 @@ using Lacuna.FocusNFSeIntegration.Models; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; using System; using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Net.Http; -using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; namespace Lacuna.FocusNFSeIntegration { public class FocusNFSeClient { - private HttpClient httpClient; - - private readonly FocusNFSeIntegrationOptions options; - - protected HttpClient HttpClient { - get { - if (httpClient == null) { - httpClient = new HttpClient { - BaseAddress = new Uri(options.IsSandbox ? options.SandboxEndpoint : options.Endpoint) - }; - httpClient.DefaultRequestHeaders.Accept.Clear(); - var authHeaderBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(options.IsSandbox ? options.SandboxToken : options.Token)); - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authHeaderBase64); - } - return httpClient; - } - } + private readonly ILogger logger; + private readonly IHttpClientFactory clientFactory; - public FocusNFSeClient(FocusNFSeIntegrationOptions options) { - this.options = options; + public FocusNFSeClient(ILogger logger, IHttpClientFactory clientFactory) { + this.logger = logger; + this.clientFactory = clientFactory; } /// /// Submits a NFSe. Must be verified if the NFSe was accept in further retrieval requests. /// public async Task CreateNFSeAsync(string reference, NFSeRequest request) { - var data = JsonConvert.SerializeObject(request, + var body = JsonConvert.SerializeObject(request, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); + var requestUri = $"/v2/nfse?ref={reference}"; - var postResponse = await performHttpRequestAsync(HttpMethod.Post, requestUri, - () => HttpClient.PostAsync(requestUri, new StringContent(data, Encoding.UTF8)) + var data = new StringContent(body, Encoding.UTF8, Constants.MediaType); + + return await sendHttpRequestAsync( + HttpMethod.Post, + requestUri, + data, + (response, client, obj) => handleErrorResponse( + HttpMethod.Post, + new Uri(client.BaseAddress, requestUri), + "Response error", + "Error on response", + obj.Errors + ) ); - - var stream = await postResponse.Content.ReadAsStreamAsync(); - - using (var reader = new StreamReader(stream)) { - var jsonResp = reader.ReadToEnd(); - - try { - var response = JsonConvert.DeserializeObject(jsonResp); - - if (response.Errors != null) { - throw new FocusNFSeIntegrationApiException(HttpMethod.Post, new Uri(HttpClient.BaseAddress, requestUri), "Response error", "Error on response", response.Errors.Select(e => $"Codigo: {e.Code} - Mensagem: {e.Message}").ToList()); - } - - return response; - - } catch { - var error = JsonConvert.DeserializeObject(jsonResp); - throw new FocusNFSeIntegrationApiException(HttpMethod.Post, new Uri(HttpClient.BaseAddress, requestUri), "Response error", "Error on response", new List { $"Codigo: {error.Code} - Mensagem: {error.Message}" }); - } - } } /// @@ -75,22 +51,17 @@ public async Task CreateNFSeAsync(string reference, NFSeRequest re public async Task RetrieveNFSeAsync(string reference) { var requestUri = $"/v2/nfse/{reference}?completa=0"; - var resp = await performHttpRequestAsync(HttpMethod.Get, requestUri, - () => HttpClient.GetAsync(requestUri) + return await sendHttpRequestAsync( + HttpMethod.Get, + requestUri, + afterDeserialization: (response, client, obj) => handleErrorResponse( + HttpMethod.Get, + new Uri(client.BaseAddress, requestUri), + "Response error", + "Error on response", + obj.Errors + ) ); - - var stream = await HttpClient.GetStreamAsync(requestUri); - - using (var reader = new StreamReader(stream)) { - var jsonretorno = reader.ReadToEnd(); - var response = JsonConvert.DeserializeObject(jsonretorno); - - if (response.Errors != null) { - throw new FocusNFSeIntegrationApiException(HttpMethod.Get, new Uri(HttpClient.BaseAddress, requestUri), "Response error", "Error on response", response.Errors.Select(e => $"Codigo: {e.Code} - Mensagem: {e.Message}").ToList()); - } - - return response; - } } /// @@ -99,22 +70,17 @@ public async Task RetrieveNFSeAsync(string reference) { public async Task CancelNFSeAsync(string reference) { var requestUri = $"/v2/nfse/{reference}"; - var resp = await performHttpRequestAsync(HttpMethod.Delete, requestUri, - () => HttpClient.DeleteAsync(requestUri) + return await sendHttpRequestAsync( + HttpMethod.Delete, + requestUri, + afterDeserialization: (response, client, obj) => handleErrorResponse( + HttpMethod.Delete, + new Uri(client.BaseAddress, requestUri), + "Response error", + "Error on response", + obj.Errors + ) ); - - var stream = await HttpClient.GetStreamAsync(requestUri); - - using (var reader = new StreamReader(stream)) { - var jsonretorno = reader.ReadToEnd(); - var response = JsonConvert.DeserializeObject(jsonretorno); - - if (response.Errors != null) { - throw new FocusNFSeIntegrationApiException(HttpMethod.Delete, new Uri(HttpClient.BaseAddress, requestUri), "Response error", "Error on response", response.Errors.Select(e => $"Codigo: {e.Code} - Mensagem: {e.Message}").ToList()); - } - - return response; - } } /// @@ -142,24 +108,59 @@ public async Task CancelNFSeAsync(string reference) { // using (var reader = new StreamReader(stream)) { // var jsonResp = reader.ReadToEnd(); - + // } //} - private async Task performHttpRequestAsync(HttpMethod verb, string requestUri, Func> asyncFunc) { - var uri = new Uri(HttpClient.BaseAddress, requestUri); - HttpResponseMessage httpResponse; + private async Task sendHttpRequestAsync(HttpMethod method, string endpoint, HttpContent content = null, Action afterDeserialization = null) { + using var client = clientFactory.CreateClient(Constants.FactoryClientName); + HttpResponseMessage httpResponse = null; + try { - httpResponse = await asyncFunc(); + httpResponse = method switch { + var m when m == HttpMethod.Get => await client.GetAsync(endpoint), + var m when m == HttpMethod.Post => await client.PostAsync(endpoint, content), + var m when m == HttpMethod.Delete => await client.DeleteAsync(endpoint), + _ => throw new NotSupportedException($"HTTP method {method} not supported.") + }; } catch (Exception ex) { - throw new FocusNFSeIntegrationUnreachableException(verb, uri, ex); + logger.LogError("Error calling Focus API. Method: {method}, Url: {endpoint}, Message: {Message}", method, endpoint, ex.Message); + throw new FocusNFSeIntegrationUnreachableException(method, new Uri(client.BaseAddress, endpoint), ex); } + + var responseContent = await httpResponse.Content.ReadAsStringAsync(); + if (!httpResponse.IsSuccessStatusCode) { - var stringContent = await httpResponse.Content.ReadAsStringAsync(); - throw new FocusNFSeIntegrationHttpException(verb, uri, httpResponse.StatusCode, httpResponse.ReasonPhrase, content: stringContent); + logger.LogError("Not sucessfull status code {StatusCode}: {stringContent}", httpResponse.StatusCode, responseContent); + throw new FocusNFSeIntegrationHttpException( + method, + new Uri(client.BaseAddress, endpoint), + httpResponse.StatusCode, + httpResponse.ReasonPhrase, + content: responseContent + ); + } + + try { + var result = JsonConvert.DeserializeObject(responseContent); + afterDeserialization?.Invoke(httpResponse, client, result); + return result; + } catch { + var error = JsonConvert.DeserializeObject(responseContent); + throw new FocusNFSeIntegrationApiException( + method, + new Uri(client.BaseAddress, endpoint), + "Response error", + "Error on response", + new List { $"Codigo: {error.Code} - Mensagem: {error.Message}" } + ); } - return httpResponse; } + private static void handleErrorResponse(HttpMethod method, Uri uri, string code, string message, List errors) { + if (errors != null) { + throw new FocusNFSeIntegrationApiException(method, uri, code, message, errors.ConvertAll(e => $"Codigo: {e.Code} - Mensagem: {e.Message}")); + } + } } } diff --git a/Lacuna.FocusNFSeIntegration/FocusNFSeClientAspNetCore.cs b/Lacuna.FocusNFSeIntegration/FocusNFSeClientAspNetCore.cs index ed20379..a0d30f2 100644 --- a/Lacuna.FocusNFSeIntegration/FocusNFSeClientAspNetCore.cs +++ b/Lacuna.FocusNFSeIntegration/FocusNFSeClientAspNetCore.cs @@ -1,10 +1,9 @@ -#if NETSTANDARD2_0 -using Microsoft.Extensions.Options; +using Lacuna.FocusNFSeIntegration; +using Microsoft.Extensions.Logging; +using System.Net.Http; -namespace Lacuna.FocusNFSeIntegration { - internal class FocusNFSeClientAspNetCore: FocusNFSeClient { - public FocusNFSeClientAspNetCore(IOptions options) : base(options.Value) { - } +internal class FocusNFSeAspNetCore : FocusNFSeClient { + + public FocusNFSeAspNetCore(ILogger logger, IHttpClientFactory clientFactory) : base(logger, clientFactory) { } } -#endif diff --git a/Lacuna.FocusNFSeIntegration/Lacuna.FocusNFSeIntegration.csproj b/Lacuna.FocusNFSeIntegration/Lacuna.FocusNFSeIntegration.csproj index bdc46f5..acc3331 100644 --- a/Lacuna.FocusNFSeIntegration/Lacuna.FocusNFSeIntegration.csproj +++ b/Lacuna.FocusNFSeIntegration/Lacuna.FocusNFSeIntegration.csproj @@ -1,8 +1,8 @@  - 1.0.0-beta08 - net45;netstandard2.0 + 1.1.0-beta01 + net6.0 true Lacuna.FocusNFSeIntegration.snk Lacuna Software @@ -17,10 +17,10 @@ - - - - + + + + diff --git a/Lacuna.FocusNFSeIntegration/Models/NFSeError.cs b/Lacuna.FocusNFSeIntegration/Models/NFSeError.cs index 9c7753c..1b39452 100644 --- a/Lacuna.FocusNFSeIntegration/Models/NFSeError.cs +++ b/Lacuna.FocusNFSeIntegration/Models/NFSeError.cs @@ -1,9 +1,5 @@ using Newtonsoft.Json; -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Lacuna.FocusNFSeIntegration.Models { public class NFSeError { diff --git a/Lacuna.FocusNFSeIntegration/ServiceCollectionExtensions.cs b/Lacuna.FocusNFSeIntegration/ServiceCollectionExtensions.cs index 481f201..90b7f94 100644 --- a/Lacuna.FocusNFSeIntegration/ServiceCollectionExtensions.cs +++ b/Lacuna.FocusNFSeIntegration/ServiceCollectionExtensions.cs @@ -1,7 +1,8 @@ -#if NETSTANDARD2_0 - -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using System; +using System.Net.Http.Headers; +using System.Text; namespace Lacuna.FocusNFSeIntegration { @@ -12,11 +13,24 @@ public static IServiceCollection AddFocusNFSeIntegration(this IServiceCollection if (action != null) { services.Configure(action); } - services.AddSingleton(); + + services.AddHttpClient(Constants.FactoryClientName) + .ConfigureHttpClient((serviceProvider, c) => { + var options = serviceProvider.GetRequiredService>().Value; + + c.BaseAddress = new Uri( + options.IsSandbox ? options.SandboxEndpoint : options.Endpoint + ); + + c.DefaultRequestHeaders.Accept.Clear(); + c.DefaultRequestHeaders.Add("Accept-Charset", Constants.CharSet); + var authHeaderBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(options.IsSandbox ? options.SandboxToken : options.Token)); + c.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authHeaderBase64); + }); + + services.AddSingleton(); return services; } } } - -#endif