using Sarand.BusinessMgr.Net.Standard.Base.Data; using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Core; using Sarand.Net.Standard.Base.Data; using Sarand.Net.Standard.Base.Net; using Sarand.Net.Standard.Base.Tasks; using Sarand.Net.Standard.Base.Utils; using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; namespace Sarand.BusinessMgr.Net.Standard.Base.WebService { //======================================================================= // Business Web Service Client // Make requests to BusinessMgr Web Service hosted by Windows Service application. //----------------------------------------------------------------------- // Threading: // Ref. https://blog.xamarin.com/getting-started-with-async-await/ //======================================================================= public class SbmWebServiceClient { //------------------------------------------------------------------- // Definitions. public class RequestData : SacsTaskData { /// /// Web Service Request data. /// Cancellation token can optionally be supplied if available higher up the chain. /// public RequestData(SbmWebServiceInstanceDefinition instanceDefinition, CancellationToken? cancellationToken = null) : base(cancellationToken) { InstanceDefinition = instanceDefinition; } public SbmWebServiceInstanceDefinition InstanceDefinition { get; } public String Data { get; set; } = ""; } //------------------------------------------------------------------- // Data members. //------------------------------------------------------------------- // Construction. public SbmWebServiceClient() { } //------------------------------------------------------------------- // Properties. //------------------------------------------------------------------- // I s A l i v e //------------------------------------------------------------------- public async Task RequestIsAlive(SbmWebServiceInstanceDefinition instanceDefinition) { try { RequestData requestData = new RequestData(instanceDefinition); SbmWebServiceResponse serviceResponse = await DoWebServiceRequest(requestData); if (serviceResponse.ResponseData.EqualsCI(SbmWebServiceDfn.IsAliveResponse)) return true; } catch (Exception exc) { SaLogger.Warn($"{SysUtil.Fn} " + exc.GetLogMessage()); } return false; } //------------------------------------------------------------------- // I t e m C o u n t R e q u e s t //------------------------------------------------------------------- public class ItemCountResponseData : SbmWebServiceResponse { public ItemCountResponseData(SbmWebServiceResponse serviceResponse) : base(serviceResponse) { } public long ItemCount { get; set; } } public async Task RequestItemCount(SbmWebServiceRequest.Config configRequest, Dictionary filters, SacsTaskData taskData) { if (SacsSystem.DebugMode && configRequest.RequestType != SbmWebServiceRequest.RequestType.Count) Debugger.Break(); // Service Request. SbmWebServiceRequest serviceRequestData = new SbmWebServiceRequest(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(); SbmWebServiceResponse 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(SbmWebServiceDfn.ElementName.Data)) { foreach (XElement dataElement in responseChildElement.Elements()) { if (dataElement.Name.LocalName.EqualsCI(SbmWebServiceDfn.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 : SbmWebServiceResponse { public QueryListResponseData(SbmWebServiceResponse 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(SbmWebServiceRequest.Config configRequest, Dictionary filters, List columns, List sortColumns, SacsTaskData taskData) { if (SacsSystem.DebugMode && configRequest.RequestType != SbmWebServiceRequest.RequestType.QueryList) Debugger.Break(); // Service Request. SbmWebServiceRequest serviceRequestData = new SbmWebServiceRequest(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(); SbmWebServiceResponse serviceResponse = await DoWebServiceRequest(requestData); QueryListResponseData responseData = new QueryListResponseData(serviceResponse); //if (SacsSystem.DebugMode && responseData.ResponseElement != null) SaLogger.Debug($"{SysUtil.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(SbmWebServiceDfn.ElementName.Data)) { foreach (XElement dataElement in responseChildElement.Elements()) { if (dataElement.Name.LocalName.EqualsCI(SbmWebServiceDfn.ElementName.List)) { // Paging info. // responseData.Count = StringUtil.ToInt(dataElement.GetAttributeValue(SbmWebServiceDfn.AttributeName.Count)); responseData.Total = StringUtil.ToInt(dataElement.GetAttributeValue(SbmWebServiceDfn.AttributeName.Total)); responseData.PageNo = (uint)StringUtil.ToInt(dataElement.GetAttributeValue(SbmWebServiceDfn.AttributeName.PageNo)); responseData.PageLength = (ushort)StringUtil.ToInt(dataElement.GetAttributeValue(SbmWebServiceDfn.AttributeName.PageLength)); // Extract items. foreach (XElement listElement in dataElement.Elements()) { taskData.ThrowIfCancellationRequested(); if (listElement.Name.LocalName.EqualsCI(SbmWebServiceDfn.ElementName.Item)) { SbmWebServiceItemData itemData = new SbmWebServiceItemData(""); 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 : SbmWebServiceResponse { public ItemListResponseData(SbmWebServiceResponse 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(SbmWebServiceRequest.Config configRequest, Dictionary filters, List sortColumns, SacsTaskData taskData) { if (SacsSystem.DebugMode && configRequest.RequestType != SbmWebServiceRequest.RequestType.ItemList) Debugger.Break(); // Service Request. SbmWebServiceRequest serviceRequestData = new SbmWebServiceRequest(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(); SbmWebServiceResponse serviceResponse = await DoWebServiceRequest(requestData); ItemListResponseData responseData = new ItemListResponseData(serviceResponse); //if (SacsSystem.DebugMode && responseData.ResponseElement != null) SaLogger.Debug($"{SysUtil.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(SbmWebServiceDfn.ElementName.Data)) { foreach (XElement dataElement in responseChildElement.Elements()) { if (dataElement.Name.LocalName.EqualsCI(SbmWebServiceDfn.ElementName.List)) { // Paging info. // responseData.Count = StringUtil.ToInt(dataElement.GetAttributeValue(SbmWebServiceDfn.AttributeName.Count)); responseData.Total = StringUtil.ToInt(dataElement.GetAttributeValue(SbmWebServiceDfn.AttributeName.Total)); responseData.PageNo = (uint)StringUtil.ToInt(dataElement.GetAttributeValue(SbmWebServiceDfn.AttributeName.PageNo)); responseData.PageLength = (ushort)StringUtil.ToInt(dataElement.GetAttributeValue(SbmWebServiceDfn.AttributeName.PageLength)); // Extract items. foreach (XElement listElement in dataElement.Elements()) { taskData.ThrowIfCancellationRequested(); if (listElement.Name.LocalName.EqualsCI(SbmWebServiceDfn.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 : SbmWebServiceResponse { public ItemDataResponseData(SbmWebServiceResponse serviceResponse) : base(serviceResponse) { } public XElement DataElement => (ResponseElement?.GetFirstChildElement(SbmWebServiceDfn.ElementName.Data) ?? null); public XElement ItemElement => (DataElement?.GetFirstChildElement(SbmWebServiceDfn.ElementName.Item) ?? null); public SbmWebServiceItemData Item { get; set; } } public async Task RequestItemData(SbmWebServiceRequest.Config configRequest, Dictionary filters, SacsTaskData taskData) { if (SacsSystem.DebugMode && configRequest.RequestType != SbmWebServiceRequest.RequestType.Item) Debugger.Break(); // Service Request. SbmWebServiceRequest serviceRequestData = new SbmWebServiceRequest(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(); SbmWebServiceResponse serviceResponse = await DoWebServiceRequest(requestData); ItemDataResponseData responseData = new ItemDataResponseData(serviceResponse); //if (SacsSystem.DebugMode && responseData.ResponseElement != null) SaLogger.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(SbmWebServiceDfn.ElementName.Data)) { foreach (XElement dataElement in responseChildElement.Elements()) { requestData.ThrowIfCancellationRequested(); if (dataElement.Name.LocalName.EqualsCI(SbmWebServiceDfn.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 SbmWebServiceItemData ExtractItemData(XElement itemElement, SacsTaskData taskData) { SbmWebServiceItemData itemData = new SbmWebServiceItemData(itemElement.GetAttributeValue(SbmWebServiceDfn.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 : SbmWebServiceResponse { public StoreItemResponseData(SbmWebServiceResponse serviceResponse) : base(serviceResponse) { } public XElement DataElement => (ResponseElement?.GetFirstChildElement("Item") ?? null); } public async Task StoreItem(SbmWebServiceRequest.Config configRequest, XElement itemElement, SacsTaskData taskData) { if (SacsSystem.DebugMode && configRequest.RequestType != SbmWebServiceRequest.RequestType.Store) Debugger.Break(); // Service Request. SbmWebServiceRequest serviceRequestData = new SbmWebServiceRequest(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(); SbmWebServiceResponse 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) { SbmWebServiceResponse serviceResponse = null; // Throw canceled exception if task has been cancelled - invoke at regular intervals. requestData.ThrowIfCancellationRequested(); // Log request. //if (SacsSystem.DebugMode) SaLogger.Debug($"{SysUtil.Fn} Request URL:" + requestData.InstanceDefinition.URL); if (requestData.Debug) { if (requestData.Data.Length < 512) SaLogger.Debug($"{SysUtil.Fn} Request:\n\t" + requestData.Data); else SaLogger.Debug($"{SysUtil.Fn} Request length:" + requestData.Data.Length); } // Configure the request. SacsHttpRequestData httpRequestData = new SacsHttpRequestData(requestData.CancellationToken) { Url = requestData.InstanceDefinition.URL, TimeoutSecs = 1, PostData = SbmWindowsServiceCrypter.EncryptRequestData(requestData.Data) }; // Make the request. DateTime started = DateTime.Now; requestData.InstanceDefinition.LastRequestAt = started; SacsHttpResponseData httpResponseData = await SacsHttpClient.DoRequest(httpRequestData); // Construct the response. SbmWebServiceResponse.Config serviceResponseConfig = new SbmWebServiceResponse.Config() { RequestStarted = started, RequestCompleted = DateTime.Now, HttpResponseData = httpResponseData }; serviceResponse = new SbmWebServiceResponse(serviceResponseConfig); // Note if request was successful. if (httpResponseData.Success) requestData.InstanceDefinition.LastSuccessfulRequestAt = serviceResponseConfig.RequestCompleted; // Log response. if (requestData.Debug) { if (serviceResponse.ResponseData.Length < 1024) SaLogger.Debug($"{SysUtil.Fn} Response: " + serviceResponse.ResponseData); else SaLogger.Debug($"{SysUtil.Fn} Response size: " + serviceResponse.ResponseData.Length); } return serviceResponse; } //------------------------------------------------------------------- } //======================================================================= }