using Sarand.Core.App; using Sarand.Core.Base; using Sarand.Core.Data; using Sarand.Core.Net; using Sarand.Core.Tasks; using Sarand.Core.Xml; using Sarand.PatientMgr.Core.Data; using System; using System.Collections.Generic; using System.Diagnostics; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; namespace Sarand.PatientMgr.Core.WebService { //======================================================================= // Patient Web Service Client // Make requests to PatientMgr Web Service hosted by Windows Service application. //----------------------------------------------------------------------- // Threading: // Ref. https://blog.xamarin.com/getting-started-with-async-await/ //======================================================================= public class SpmWebServiceClient { //------------------------------------------------------------------- // Definitions. public class RequestData : SacsTaskData { /// /// Web Service Request data. /// Cancellation token can optionally be supplied if available higher up the chain. /// public RequestData (SpmWebServiceInstanceDefinition instanceDefinition, CancellationToken? cancellationToken = null) : base(cancellationToken) { InstanceDefinition = instanceDefinition; } public SpmWebServiceInstanceDefinition InstanceDefinition { get; } public String Data { get; set; } = ""; } //------------------------------------------------------------------- // Data members. //------------------------------------------------------------------- // Construction. public SpmWebServiceClient () { } //------------------------------------------------------------------- // Properties. //------------------------------------------------------------------- // I s A l i v e //------------------------------------------------------------------- public async Task RequestIsAlive (SpmWebServiceInstanceDefinition instanceDefinition) { String fn = GetType().Name + ".IsAlive"; try { RequestData requestData = new RequestData(instanceDefinition); SpmWebServiceResponse serviceResponse = await DoWebServiceRequest(requestData); if (serviceResponse.ResponseData.EqualsCI(SpmWebServiceDfn.IsAliveResponse)) return true; } catch (Exception exc) { Logger.Warn(fn + " " + exc.GetLogMessage()); } return false; } //------------------------------------------------------------------- // I t e m C o u n t R e q u e s t //------------------------------------------------------------------- public class ItemCountResponseData : SpmWebServiceResponse { public ItemCountResponseData (SpmWebServiceResponse serviceResponse) : base(serviceResponse) { } public long ItemCount { get; set; } } public async Task RequestItemCount (SpmWebServiceRequest.Config configRequest, Dictionary filters, SacsTaskData taskData) { String fn = GetType().Name + ".GetItemCount"; if (SacsSystem.DebugMode && configRequest.RequestType != SpmWebServiceRequest.RequestType.Count) Debugger.Break(); // Service Request. SpmWebServiceRequest serviceRequestData = new SpmWebServiceRequest(configRequest); serviceRequestData.Filters.AddRange(filters); // Configure Web Service and make Request. RequestData requestData = new RequestData(configRequest.Context.InstanceDefinition, taskData.CancellationToken) { Debug = taskData.Debug }; requestData.Data = serviceRequestData.GenerateRequestData(); SpmWebServiceResponse serviceResponse = await DoWebServiceRequest(requestData); ItemCountResponseData responseData = new ItemCountResponseData(serviceResponse); bool itemCountFound = false; // Interpret response. if (serviceResponse.ResponseElement != null && !taskData.IsCancellationRequested) { foreach (XElement responseChildElement in serviceResponse.ResponseElement.Elements()) { if (responseChildElement.Name.LocalName.EqualsCI(SpmWebServiceDfn.ElementName.Data)) { foreach (XElement dataElement in responseChildElement.Elements()) { if (dataElement.Name.LocalName.EqualsCI(SpmWebServiceDfn.ElementName.Count)) { itemCountFound = true; responseData.ItemCount = StringUtil.ToLong(dataElement.Value); break; } if (taskData.IsCancellationRequested) break; } } if (itemCountFound || taskData.IsCancellationRequested) break; } } if (!itemCountFound) responseData.ErrorStateMessage = "Incomplete item count"; if (taskData.IsCancellationRequested) responseData.TaskCancelled = true; return responseData; } //------------------------------------------------------------------- // Q u e r y L i s t R e q u e s t //------------------------------------------------------------------- public class QueryListResponseData : SpmWebServiceResponse { public QueryListResponseData (SpmWebServiceResponse serviceResponse) : base(serviceResponse) { } public List Items { get; } = new List(); public int Count { get; set; } public int Total { get; set; } public uint PageNo { get; set; } public ushort PageLength { get; set; } } public async Task RequestQueryList (SpmWebServiceRequest.Config configRequest, Dictionary filters, List columns, List sortColumns, SacsTaskData taskData) { String fn = GetType().Name + ".GetItemList"; if (SacsSystem.DebugMode && configRequest.RequestType != SpmWebServiceRequest.RequestType.QueryList) Debugger.Break(); // Service Request. SpmWebServiceRequest serviceRequestData = new SpmWebServiceRequest(configRequest); serviceRequestData.Filters.AddRange(filters); serviceRequestData.Columns.AddRange(columns); if (sortColumns != null) serviceRequestData.SortColumns.AddRange(sortColumns); // Configure Web Service and make Request. RequestData requestData = new RequestData(configRequest.Context.InstanceDefinition, taskData.CancellationToken) { Debug = taskData.Debug }; requestData.Data = serviceRequestData.GenerateRequestData(); SpmWebServiceResponse serviceResponse = await DoWebServiceRequest(requestData); QueryListResponseData responseData = new QueryListResponseData(serviceResponse); //if (SacsSystem.DebugMode && responseData.ResponseElement != null) Logger.Debug(fn + " Response:\n\n" + responseData.ResponseElement.FormatXml() + "\n"); // Interpret response. if (serviceResponse.ResponseElement != null && !taskData.IsCancellationRequested) { foreach (XElement responseChildElement in serviceResponse.ResponseElement.Elements()) { if (responseChildElement.Name.LocalName.EqualsCI(SpmWebServiceDfn.ElementName.Data)) { foreach (XElement dataElement in responseChildElement.Elements()) { if (dataElement.Name.LocalName.EqualsCI(SpmWebServiceDfn.ElementName.List)) { // Paging info. // responseData.Count = StringUtil.ToInt(dataElement.GetAttributeValue(SpmWebServiceDfn.AttributeName.Count)); responseData.Total = StringUtil.ToInt(dataElement.GetAttributeValue(SpmWebServiceDfn.AttributeName.Total)); responseData.PageNo = (uint)StringUtil.ToInt(dataElement.GetAttributeValue(SpmWebServiceDfn.AttributeName.PageNo)); responseData.PageLength = (ushort)StringUtil.ToInt(dataElement.GetAttributeValue(SpmWebServiceDfn.AttributeName.PageLength)); // Extract items. foreach (XElement listElement in dataElement.Elements()) { taskData.ThrowIfCancellationRequested(); if (listElement.Name.LocalName.EqualsCI(SpmWebServiceDfn.ElementName.Item)) { SpmWebServiceItemData itemData = new SpmWebServiceItemData(""); List fields = SacsCsvHandler.ParseCsvRecord(listElement.Value); for (int fieldIdx = 0; fieldIdx < fields.Count && fieldIdx < columns.Count; fieldIdx++) { String column = columns[fieldIdx]; String field = fields[fieldIdx]; itemData.AddValue(column, field); } responseData.Items.Add(itemData); } if (taskData.IsCancellationRequested) break; } } if (taskData.IsCancellationRequested) break; } } if (taskData.IsCancellationRequested) break; } } if (taskData.IsCancellationRequested) responseData.TaskCancelled = true; return responseData; } //------------------------------------------------------------------- // I t e m L i s t R e q u e s t //------------------------------------------------------------------- public class ItemListResponseData : SpmWebServiceResponse { public ItemListResponseData (SpmWebServiceResponse serviceResponse) : base(serviceResponse) { } public List Items { get; } = new List(); public int Count { get; set; } public int Total { get; set; } public uint PageNo { get; set; } public ushort PageLength { get; set; } } public async Task RequestItemList (SpmWebServiceRequest.Config configRequest, Dictionary filters, List sortColumns, SacsTaskData taskData) { String fn = GetType().Name + ".GetItemList"; if (SacsSystem.DebugMode && configRequest.RequestType != SpmWebServiceRequest.RequestType.ItemList) Debugger.Break(); // Service Request. SpmWebServiceRequest serviceRequestData = new SpmWebServiceRequest(configRequest); serviceRequestData.Filters.AddRange(filters); if (sortColumns != null) serviceRequestData.SortColumns.AddRange(sortColumns); // Configure Web Service and make Request. RequestData requestData = new RequestData(configRequest.Context.InstanceDefinition, taskData.CancellationToken) { Debug = taskData.Debug }; requestData.Data = serviceRequestData.GenerateRequestData(); SpmWebServiceResponse serviceResponse = await DoWebServiceRequest(requestData); ItemListResponseData responseData = new ItemListResponseData(serviceResponse); //if (SacsSystem.DebugMode && responseData.ResponseElement != null) Logger.Debug(fn + " Response:\n\n" + responseData.ResponseElement.FormatXml() + "\n"); // Interpret response. if (serviceResponse.ResponseElement != null && !taskData.IsCancellationRequested) { foreach (XElement responseChildElement in serviceResponse.ResponseElement.Elements()) { if (responseChildElement.Name.LocalName.EqualsCI(SpmWebServiceDfn.ElementName.Data)) { foreach (XElement dataElement in responseChildElement.Elements()) { if (dataElement.Name.LocalName.EqualsCI(SpmWebServiceDfn.ElementName.List)) { // Paging info. // responseData.Count = StringUtil.ToInt(dataElement.GetAttributeValue(SpmWebServiceDfn.AttributeName.Count)); responseData.Total = StringUtil.ToInt(dataElement.GetAttributeValue(SpmWebServiceDfn.AttributeName.Total)); responseData.PageNo = (uint)StringUtil.ToInt(dataElement.GetAttributeValue(SpmWebServiceDfn.AttributeName.PageNo)); responseData.PageLength = (ushort)StringUtil.ToInt(dataElement.GetAttributeValue(SpmWebServiceDfn.AttributeName.PageLength)); // Extract items. foreach (XElement listElement in dataElement.Elements()) { taskData.ThrowIfCancellationRequested(); if (listElement.Name.LocalName.EqualsCI(SpmWebServiceDfn.ElementName.Item)) responseData.Items.Add(ExtractItemData(listElement, taskData)); if (taskData.IsCancellationRequested) break; } } if (taskData.IsCancellationRequested) break; } } if (taskData.IsCancellationRequested) break; } } if (taskData.IsCancellationRequested) responseData.TaskCancelled = true; return responseData; } //------------------------------------------------------------------- // I t e m D a t a R e q u e s t //------------------------------------------------------------------- public class ItemDataResponseData : SpmWebServiceResponse { public ItemDataResponseData (SpmWebServiceResponse serviceResponse) : base(serviceResponse) { } public XElement DataElement => (ResponseElement?.GetFirstChildElement(SpmWebServiceDfn.ElementName.Data) ?? null); public XElement ItemElement => (DataElement?.GetFirstChildElement(SpmWebServiceDfn.ElementName.Item) ?? null); public SpmWebServiceItemData Item { get; set; } } public async Task RequestItemData (SpmWebServiceRequest.Config configRequest, Dictionary filters, SacsTaskData taskData) { String fn = GetType().Name + ".GetItemData"; if (SacsSystem.DebugMode && configRequest.RequestType != SpmWebServiceRequest.RequestType.Item) Debugger.Break(); // Service Request. SpmWebServiceRequest serviceRequestData = new SpmWebServiceRequest(configRequest); serviceRequestData.Filters.AddRange(filters); // Configure Web Service and make Request. RequestData requestData = new RequestData(configRequest.Context.InstanceDefinition, taskData.CancellationToken) { Debug = taskData.Debug }; requestData.Data = serviceRequestData.GenerateRequestData(); SpmWebServiceResponse serviceResponse = await DoWebServiceRequest(requestData); ItemDataResponseData responseData = new ItemDataResponseData(serviceResponse); //if (SacsSystem.DebugMode && responseData.ResponseElement != null) Logger.Debug("Response:\n\n" + responseData.ResponseElement.FormatXml()+"\n"); // Extract item properties. if (serviceResponse.ResponseElement != null && !taskData.IsCancellationRequested) { foreach (XElement responseChildElement in serviceResponse.ResponseElement.Elements()) { requestData.ThrowIfCancellationRequested(); if (responseChildElement.Name.LocalName.EqualsCI(SpmWebServiceDfn.ElementName.Data)) { foreach (XElement dataElement in responseChildElement.Elements()) { requestData.ThrowIfCancellationRequested(); if (dataElement.Name.LocalName.EqualsCI(SpmWebServiceDfn.ElementName.Item)) responseData.Item = ExtractItemData(dataElement, taskData); if (taskData.IsCancellationRequested) break; } } if (taskData.IsCancellationRequested) break; } } if (taskData.IsCancellationRequested) responseData.TaskCancelled = true; return responseData; } //------------------------------------------------------------------- // E x t r a c t I t e m //------------------------------------------------------------------- private SpmWebServiceItemData ExtractItemData (XElement itemElement, SacsTaskData taskData) { SpmWebServiceItemData itemData = new SpmWebServiceItemData(itemElement.GetAttributeValue(SpmWebServiceDfn.AttributeName.ItemType)); foreach (XElement itemDataElement in itemElement.Elements()) { if (!itemDataElement.HasElements) { // No child elements - treat as item property. itemData.AddValue(itemDataElement.Name.LocalName, itemDataElement.Value); } else if (itemDataElement.Name.LocalName.Length > 4 && itemDataElement.Name.LocalName.EndsWithCI("List")) { // Data elements has children and is a list of child items. String childItemType = itemDataElement.Name.LocalName; if (!itemData.ChildItems.ContainsKey(childItemType)) itemData.ChildItems[childItemType] = new List(); foreach (XElement childItemElement in itemDataElement.Elements()) { itemData.ChildItems[childItemType].Add(ExtractItemData(childItemElement, taskData)); if (taskData.IsCancellationRequested) break; } } if (taskData.IsCancellationRequested) break; } return itemData; } //------------------------------------------------------------------- // S t o r e I t e m R e q u e s t //------------------------------------------------------------------- public class StoreItemResponseData : SpmWebServiceResponse { public StoreItemResponseData (SpmWebServiceResponse serviceResponse) : base(serviceResponse) { } public XElement DataElement => (ResponseElement?.GetFirstChildElement("Item") ?? null); } public async Task StoreItem (SpmWebServiceRequest.Config configRequest, XElement itemElement, SacsTaskData taskData) { String fn = GetType().Name + ".StoreItem"; if (SacsSystem.DebugMode && configRequest.RequestType != SpmWebServiceRequest.RequestType.Store) Debugger.Break(); // Service Request. SpmWebServiceRequest serviceRequestData = new SpmWebServiceRequest(configRequest); serviceRequestData.ItemElement = itemElement; // Configure Web Service and make Request. RequestData requestData = new RequestData(configRequest.Context.InstanceDefinition, taskData.CancellationToken) { Debug = taskData.Debug }; requestData.Data = serviceRequestData.GenerateRequestData(); SpmWebServiceResponse serviceResponse = await DoWebServiceRequest(requestData); // Return result. return new StoreItemResponseData(serviceResponse); } //------------------------------------------------------------------- // W e b S e r v i c e R e q u e s t //------------------------------------------------------------------- /// /// Execute the web service request. /// Request data is encrypted and response data is decrypted automatically. /// public async Task DoWebServiceRequest (RequestData requestData) { String fn = GetType().Name + ".DoWebServiceRequest"; SpmWebServiceResponse serviceResponse = null; // Throw canceled exception if task has been cancelled - invoke at regular intervals. requestData.ThrowIfCancellationRequested(); // Log request. if (SacsSystem.DebugMode) Logger.Debug(fn + " Request URL:" + requestData.InstanceDefinition.URL); if (requestData.Debug) { if (requestData.Data.Length < 512) Logger.Debug(fn + " Request:\n\t" + requestData.Data); else Logger.Debug(fn + " Request length:" + requestData.Data.Length); } // Configure the request. SacsHttpRequestData httpRequestData = new SacsHttpRequestData(requestData.CancellationToken) { Url = requestData.InstanceDefinition.URL, TimeoutSecs = 1, PostData = SpmWindowsServiceCrypter.EncryptRequestData(requestData.Data) }; // Make the request. DateTime started = DateTime.Now; requestData.InstanceDefinition.LastRequestAt = started; SacsHttpResponseData httpResponseData = await SacsHttpClient.DoRequest(httpRequestData); // Construct the response. SpmWebServiceResponse.Config serviceResponseConfig = new SpmWebServiceResponse.Config() { RequestStarted = started, RequestCompleted = DateTime.Now, HttpResponseData = httpResponseData }; serviceResponse = new SpmWebServiceResponse(serviceResponseConfig); // Note if request was successful. if (httpResponseData.Success) requestData.InstanceDefinition.LastSuccessfulRequestAt = serviceResponseConfig.RequestCompleted; // Log response. if (requestData.Debug) { if (serviceResponse.ResponseData.Length < 1024) Logger.Debug(fn + " Response: " + serviceResponse.ResponseData); else Logger.Debug(fn + " Response size: " + serviceResponse.ResponseData.Length); } return serviceResponse; } //------------------------------------------------------------------- } //======================================================================= }