| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.IO.Abstractions;
- using System.Net.Http;
- using System.Net.Http.Headers;
- using System.Threading.Tasks;
- using BO.AppServer.Metadata.Dto;
- using Quadarax.Foundation.Core.Data.Interface;
- using Quadarax.Foundation.Core.Data.Interface.Entity;
- using Quadarax.Foundation.Core.Data.Interface.Entity.Dto;
- using Quadarax.Foundation.Core.Json;
- using Quadarax.Foundation.Core.Logging;
- using Quadarax.Foundation.Core.Object;
- using Quadarax.Foundation.Core.Value.Extensions;
- namespace BO.Connector
- {
- public abstract class AbstractConnection : DisposableObject
- {
- #region *** Private fields ***
- private HttpClient _client;
- private Uri _uriApiBase;
- private ILogHandler _log;
- private TimeSpan _timeout;
- private string _ticket;
- private Binder _binder;
- private bool _isDumpContentEnabled;
- protected abstract string ApiName { get; }
- protected bool IsOpen => !string.IsNullOrEmpty(_ticket);
- protected string Ticket => _ticket;
- #endregion
- #region *** Constructors ***
- protected AbstractConnection(Uri uriApiBase, TimeSpan timeout, ILogHandler log = null, bool isDumpContentEnabled = false)
- {
- _uriApiBase = uriApiBase ?? throw new ArgumentNullException(nameof(uriApiBase));
- _uriApiBase = new Uri(_uriApiBase.AbsoluteUri + ApiName + "/");
- _log = log;
- _timeout = timeout;
- _binder = new Binder(new FileSystem());
- _binder.SerializerOptions.IgnoreReadOnlyProperties = true;
- _binder.SerializerOptions.IgnoreNullValues = true;
- _isDumpContentEnabled = isDumpContentEnabled;
- }
- #endregion
- #region *** Public Operations ***
- public void Open(string userName, string userPassword, string magicKey)
- {
- Log(LogSeverityEnum.Trace, $"Opening Connector '{ApiName}' open with magicKey='{magicKey}'");
- _client = CreateClient();
- InitTicket(magicKey);
- Login(userName, userPassword);
- OnOpening();
- Log(LogSeverityEnum.Debug, $"Connector '{ApiName}' open for user '{userName}' with timeout setting {_timeout}.");
- }
- public void Close()
- {
- _client?.CancelPendingRequests();
- _client?.Dispose();
- _client = null;
- _ticket = string.Empty;
- Log(LogSeverityEnum.Debug, $"Connector '{ApiName}' closed.");
- }
- public bool Ping(out TimeSpan duration, out string status)
- {
- CheckIsOpen();
- var start = DateTime.Now;
- var ping = Task.Run(() =>CallRequestAsync<ResultValueDto<PingDto>>($"{_ticket}/ping", RestCallTypeEnum.Get, null, null, true));
- ping.Wait();
- duration = DateTime.Now - start;
- if (ping.Result == null)
- {
- status = string.Empty;
- return false;
- }
- status = ping.Result.Value.Status;
- return ping.Result.IsSuccess;
- }
- #endregion
- #region *** Virtuals & Overrides ***
- protected override void OnDisposing()
- {
- _client?.Dispose();
- }
- #endregion
- #region *** Private Operations ***
- protected void CheckIsOpen()
- {
- if (!IsOpen)
- throw new InvalidOperationException(
- "Connector is not open to call specific operation. Call Open() first.");
- }
- protected void Log(LogSeverityEnum type, string message, Exception e = null)
- {
- _log?.Log(type, message, e);
- }
- protected void InitTicket(string magicKey)
- {
- var result = Task.Run(() => CallRequestAsync<ResultValueDto<AuthDto>>($"{magicKey}/auth", RestCallTypeEnum.Get));;
- result.Wait();
- if (!result.Result.IsSuccess)
- throw result.Result.ToAggregateException();
- _ticket = result.Result.Value.Ticket;
- }
- protected void Login(string userName, string userPassword)
- {
- var headerAtts = new Dictionary<string, string>();
- headerAtts.Add("userName",userName);
- headerAtts.Add("userPassword",userPassword);
- var result = Task.Run(() => CallRequestAsync<ResultValueDto<UserRDto>>($"{_ticket}/login", RestCallTypeEnum.Post, new ResultPlain(), headerAtts));
- result.Wait();
- if (!result.Result.IsSuccess)
- throw result.Result.ToAggregateException();
- Log(LogSeverityEnum.Info,$"User '{result.Result.Value.Name}' is logged in.");
- }
- protected IDictionary<string, string> GetPagingHeaderAttributes(PagingDto paging)
- {
- if (paging == null || paging.IsDisabled)
- return null;//paging = new PagingDto();
- var result = new Dictionary<string, string>
- {
- { "page", paging.Page.ToString() },
- { "pageSize", paging.PageSize.ToString() }
- };
- return result;
- }
- protected async Task<Stream> CallGetStreamAsync(string relativeUrlApiCall,
- IDictionary<string, string> headerAttributes = null, bool silent = false)
- {
- Log(LogSeverityEnum.Trace, $"Calling REST API GET STREAM {_uriApiBase.AbsoluteUri + relativeUrlApiCall}");
- if (headerAttributes != null)
- {
- foreach (var key in headerAttributes.Keys)
- {
- _client.DefaultRequestHeaders.Remove(key);
- _client.DefaultRequestHeaders.Add(key,headerAttributes[key]);
- }
- }
- return await _client.GetStreamAsync(relativeUrlApiCall);
- }
- protected async Task<TResult> CallRequestAsync<TResult>(string relativeUrlApiCall,RestCallTypeEnum method, IDto data = null,IDictionary<string,string> headerAttributes = null, bool silent = false)
- where TResult : ResultDto
- {
- if (data == null)
- if(method != RestCallTypeEnum.Get && method != RestCallTypeEnum.GetStream && method != RestCallTypeEnum.Delete)
- throw new ArgumentNullException(nameof(data), $"For REST API method '{method}' data must be specified!");
- Log(LogSeverityEnum.Trace, $"Calling REST API {method} {_uriApiBase.AbsoluteUri + relativeUrlApiCall}");
- var start = DateTime.Now;
- HttpResponseMessage resp = null;
- switch (method)
- {
- case RestCallTypeEnum.Get:
- if (headerAttributes != null)
- {
- foreach (var key in headerAttributes.Keys)
- {
- _client.DefaultRequestHeaders.Remove(key);
- _client.DefaultRequestHeaders.Add(key,headerAttributes[key]);
- }
- }
- resp = await _client.GetAsync(relativeUrlApiCall);
- break;
- case RestCallTypeEnum.Post:
- var req = new StringContent(_binder.SaveToString(data));
- if (headerAttributes!=null)
- foreach (var key in headerAttributes.Keys)
- req.Headers.Add(key,headerAttributes[key]);
- req.Headers.ContentType = new MediaTypeHeaderValue("application/json");
- resp = await _client.PostAsync(relativeUrlApiCall, req);
- break;
- case RestCallTypeEnum.Put:
- req = new StringContent(_binder.SaveToString(data));
- if (headerAttributes!=null)
- foreach (var key in headerAttributes.Keys)
- req.Headers.Add(key,headerAttributes[key]);
- req.Headers.ContentType = new MediaTypeHeaderValue("application/json");
- resp = await _client.PutAsync(relativeUrlApiCall, req);
- break;
- case RestCallTypeEnum.Delete:
- resp = await _client.DeleteAsync(relativeUrlApiCall);
- break;
- default:
- throw new NotSupportedException($"{method} REST API operation is not implemented.");
- }
-
- // may remove paging headers after call
- /*
- if (headerAttributes != null)
- foreach (var key in headerAttributes.Keys)
- _client.DefaultRequestHeaders.Remove(key);
- */
- Log(LogSeverityEnum.Trace, $"REST API {method} {_uriApiBase.AbsoluteUri + relativeUrlApiCall} done @ {(DateTime.Now - start).ToReadableString()}");
- if (!resp.IsSuccessStatusCode)
- {
- var message = $"Calling REST API {method} {_uriApiBase.AbsoluteUri + relativeUrlApiCall} ends with result code '{resp.StatusCode}'";
- Log(LogSeverityEnum.Warn, message);
- throw new Exception(message);
- }
- var content = await resp.Content.ReadAsStringAsync();
- if (_isDumpContentEnabled)
- Log(LogSeverityEnum.Trace, content);
- var result = (TResult)_binder.LoadFromString(content, typeof(TResult));
- if (result.IsSuccess) return result;
- if (silent) return result;
- // handle errors
- var excps = new List<Exception>();
- foreach (var err in result.Errors)
- {
- var exc = new Exception($"[{err.Code}] {err.Message}");
- exc.Data.Add("Code", err.Code);
- excps.Add(exc);
- }
- throw new AggregateException(excps);
- }
- protected virtual void OnOpening()
- {
- }
- private HttpClient CreateClient()
- {
- var client = new HttpClient();
- client.BaseAddress = new Uri(_uriApiBase, ApiName);
- client.Timeout = _timeout;
- client.DefaultRequestHeaders.Accept.Clear();
- client.DefaultRequestHeaders.Add("User-Agent", "BO.Connector." + ApiName);
- // Add an Accept header for JSON format.
- client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
- return client;
- }
- #endregion
- }
- }
|