using Edge.Core.Processor; using Edge.Core.UniversalApi; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Edge.WebHost.Models; using Edge.Core.Processor.Dispatcher; using System.Diagnostics.CodeAnalysis; using Edge.Core.UniversalApi.Auditing; using Edge.Core.Processor.Dispatcher.Attributes; using Edge.Core; namespace Edge.WebHost.Controllers { public class HomeController : Controller { public const string UniversalWebApiHandlerRoute = "u"; //private readonly ILogger<HomeController> _logger; private readonly Func<IEnumerable<IProcessor>> processorFactory; private UniversalApiHub universalApiHub; private UniversalApiInvoker universalApiInvoker; private IProcessorMetaConfigAccessor processorMetaConfigAccessor; private static JsonSerializerOptions jsonSerializerOptions; static HomeController() { jsonSerializerOptions = new JsonSerializerOptions() { WriteIndented = true, PropertyNameCaseInsensitive = true, }; jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); } public HomeController( Func<IEnumerable<IProcessor>> processorFactory, UniversalApiHub universalApiHub, UniversalApiInvoker universalApiInvoker, IProcessorMetaConfigAccessor processorMetaConfigAccessor) { this.processorFactory = processorFactory; this.universalApiHub = universalApiHub; this.universalApiInvoker = universalApiInvoker; this.processorMetaConfigAccessor = processorMetaConfigAccessor; } [HttpGet] public IActionResult Index() { var clientAcceptFormatStr = Request.Headers[HeaderNames.Accept].FirstOrDefault(); var processorCompoundInfos = this.processorFactory().Select(p => { var pDesc = p.ProcessorDescriptor(); return new Tuple<ProcessorDescriptor, MetaPartsDescriptor>(pDesc, pDesc.DeviceHandlerOrApp.GetType().GetCustomAttributes<MetaPartsDescriptor>().FirstOrDefault()); }); if (string.IsNullOrEmpty(clientAcceptFormatStr)) return View("~/Views/Home/Index.cshtml", processorCompoundInfos); else if (clientAcceptFormatStr.Contains("text/html")) return View("~/Views/Home/Index.cshtml", processorCompoundInfos); else if (clientAcceptFormatStr.Contains("application/json")) return Ok(processorCompoundInfos); else if (clientAcceptFormatStr.Contains("application/xml")) return Ok(processorCompoundInfos); return View("~/Views/Home/Index.cshtml", processorCompoundInfos); } [HttpGet] public async Task<IActionResult> GetProcessorMetaConfigSchema(string simpleSourceEndpointTypeStr, string configSubSection, string format) { if (this.processorMetaConfigAccessor == null) return Content(""); var metaConfigs = await this.processorMetaConfigAccessor.GetMetaConfigAsync(null); var configs = metaConfigs.Where(c => c.SourceEndpointFullTypeStr.Contains(simpleSourceEndpointTypeStr)); if (!(configs?.Any() ?? false)) return Content(""); //sample: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7 var clientAcceptLanguageStr = Request.Headers[HeaderNames.AcceptLanguage].FirstOrDefault()?.Split(',')[0] ?? "en-us"; #region FdcServerHostApp if (simpleSourceEndpointTypeStr.Contains("FdcServerHostApp", StringComparison.OrdinalIgnoreCase)) { if (configSubSection == "Pump") { var pumpJsElements = JsonDocument.Parse(configs.FirstOrDefault().Parts.FirstOrDefault().ParametersJsonArrayStr).RootElement.EnumerateArray().First().GetProperty("PumpConfigs").EnumerateArray().ToArray(); var productElements = JsonDocument.Parse(configs.FirstOrDefault().Parts.FirstOrDefault().ParametersJsonArrayStr).RootElement.EnumerateArray().First().GetProperty("ProductConfigs").EnumerateArray().ToArray(); var siteLevelNozzleIds = new List<int>(); var siteLevelNozzleDisplayStrings = new List<string>(); foreach (var pumpJsEle in pumpJsElements) { var pumpId = pumpJsEle.GetProperty("PumpId").GetInt32(); var nzJsEles = pumpJsEle.GetProperty("NozzleConfigs").EnumerateArray().ToArray(); foreach (var nzJsEle in nzJsEles) { int nozzleLogicalId; if (!nzJsEle.GetProperty("NozzleLogicalId").TryGetInt32(out nozzleLogicalId)) nozzleLogicalId = -1; int siteLvlNozzleId; if (!nzJsEle.GetProperty("SiteLevelNozzleId").TryGetInt32(out siteLvlNozzleId)) siteLvlNozzleId = -1; int tankNumber; if (!nzJsEle.GetProperty("TankNumber").TryGetInt32(out tankNumber)) tankNumber = -1; var productCode = nzJsEle.GetProperty("ProductCode").GetInt32(); var productName = productElements.Where(p => p.GetProperty("ProductCode").GetInt32() == productCode).FirstOrDefault().GetProperty("ProductName").GetString(); siteLevelNozzleIds.Add(siteLvlNozzleId); if (string.IsNullOrEmpty(format)) siteLevelNozzleDisplayStrings.Add( $"lang-en-us:SiteLevelNozzleId: {siteLvlNozzleId}, pumpId: {pumpId}, nozzleLogicalId: {nozzleLogicalId}, productName: {productName}(code: {productCode }), tankNo.:{ tankNumber}lang-zh-cn:站级枪号: {siteLvlNozzleId}, 加油点号: {pumpId}, 逻辑枪号: {nozzleLogicalId}, 油品名: {productName}(代码: {productCode }), 油罐号.: { tankNumber}" .LocalizedContent(clientAcceptLanguageStr)); else if (format == "simple") { siteLevelNozzleDisplayStrings.Add( $"lang-en-us:SiteLevelNozzleId: {siteLvlNozzleId}, productName: {productName}(code: {productCode })lang-zh-cn:站级枪号: {siteLvlNozzleId}, 油品名: {productName}(代码: {productCode })" .LocalizedContent(clientAcceptLanguageStr)); } } } var dynamicPumpSchema = "{" + "\"$schema\": \"http://json-schema.org/draft-04/schema\"," + "\"type\": \"integer\"," + "\"enum\": [ " + siteLevelNozzleIds.Select(i => i.ToString()).Aggregate((acc, n) => acc + "," + n) + " ]," + " \"options\": {" + " \"enum_titles\": [ " + siteLevelNozzleDisplayStrings.Select(s => "\"" + s + "\"").Aggregate((acc, n) => acc + "," + n) + " ]" + " }" + "}"; return Content(dynamicPumpSchema); } } #endregion return Content(""); } /// <summary> /// /// </summary> /// <param name="apiType">localmqtt, remotemqtt, webapi</param> /// <param name="tags">, split tag string</param> /// <param name="format"></param> /// <returns></returns> [HttpGet] public IActionResult ShowMeApi(string apiType, string tags, string format) { var communicationProvider = this.universalApiHub.CommunicationProviders?.Where(p => p.GetType().Name.Contains(apiType, StringComparison.OrdinalIgnoreCase))?.FirstOrDefault(); var docs = communicationProvider?.GetApiDocuments()?.FilterByTags(tags?.Replace(" ", "")?.Split(",")) ?? Enumerable.Empty<UniversalApiInfoDoc>(); if (format == "data") return Ok(docs); else if (format == "pretty") return View("~/Views/Home/MqttApiDocPretty.cshtml", docs); return Redirect("/swagger"); } public async Task<IActionResult> ProcessorMetaDescriptor(int startIndex = 0, int count = 10, string q = "") { var clientAcceptFormatStr = Request.Headers[HeaderNames.Accept].FirstOrDefault(); //sample: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7 var clientAcceptLanguageStr = Request.Headers[HeaderNames.AcceptLanguage].FirstOrDefault()?.Split(',')[0] ?? "en-us"; IEnumerable<ProcessorMetaDescriptor> pMetaDescriptors = null; if (!string.IsNullOrEmpty(q)) { pMetaDescriptors = ObjectInstanceCreator.CurrentDomainProcessorEndPointTypes.ExtractProcessorMetaDescriptor() .Where(d => d.SourceEndpointFullTypeStr.Contains(q.Trim(), StringComparison.OrdinalIgnoreCase)); } else { pMetaDescriptors = ObjectInstanceCreator.CurrentDomainProcessorEndPointTypes.ExtractProcessorMetaDescriptor().Skip(startIndex).Take(count); } ReplaceWithLocalizedContent(pMetaDescriptors, clientAcceptLanguageStr); if (string.IsNullOrEmpty(clientAcceptFormatStr)) return View(pMetaDescriptors); else if (clientAcceptFormatStr.Contains("text/html")) return View(pMetaDescriptors); else if (clientAcceptFormatStr.Contains("application/json")) return Ok(pMetaDescriptors); else if (clientAcceptFormatStr.Contains("application/xml")) return Ok(pMetaDescriptors); return View(pMetaDescriptors); } /// <summary> /// /// </summary> /// <param name="source"></param> /// <param name="clientAcceptLanguageStr">sample: en-US or zh-CN</param> static void ReplaceWithLocalizedContent(IEnumerable<ProcessorMetaDescriptor> source, string clientAcceptLanguageStr) { foreach (var pDescriptor in source) { pDescriptor.Description = pDescriptor?.Description?.LocalizedContent(clientAcceptLanguageStr); if (pDescriptor.Tags != null) { var localizedTags = new List<string>(); foreach (var t in pDescriptor.Tags) { var compoundTag = new { TagKey = t.LocalizedContent("en"), LocalizedTagName = t.LocalizedContent(clientAcceptLanguageStr) }; localizedTags.Add(System.Text.Json.JsonSerializer.Serialize(compoundTag)); } pDescriptor.Tags = localizedTags.ToArray(); } if (string.IsNullOrEmpty(pDescriptor.DisplayName)) { //SourceEndpointFullTypeStr sample: Edge.Core.Processor.Dispatcher.DefaultDispatcher, Edge.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null pDescriptor.DisplayName = pDescriptor.SourceEndpointFullTypeStr.Substring(0, pDescriptor.SourceEndpointFullTypeStr.IndexOf(", Version=")); } else pDescriptor.DisplayName = pDescriptor.DisplayName.LocalizedContent(clientAcceptLanguageStr); foreach (var mpGroup in pDescriptor.MetaPartsGroupDescriptors) { foreach (var mpDescriptor in mpGroup.MetaPartsDescriptors) { mpDescriptor.Description = mpDescriptor?.Description?.LocalizedContent(clientAcceptLanguageStr); if (string.IsNullOrEmpty(mpDescriptor.DisplayName)) { mpDescriptor.DisplayName = mpDescriptor.TypeString; } else mpDescriptor.DisplayName = mpDescriptor.DisplayName.LocalizedContent(clientAcceptLanguageStr); foreach (var pJsons in mpDescriptor.ParametersJsonSchemaStrings) { for (int i = 0; i < pJsons.Count; i++) { pJsons[i] = pJsons[i].LocalizedJsonSchema(clientAcceptLanguageStr); } } } } } } [HttpGet] [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } //public async Task<IActionResult> Processors() //{ // return View(this.processors); //} /// <summary> /// all calls to every single processor will be routed here /// </summary> /// <param name="apiType">service, or property</param> /// <param name="targetApiName">service, or property name</param> /// <param name="targetProcessorName"></param> /// <param name="endpointName"></param> /// <param name="inputParameter"></param> /// <returns></returns> //[ApiExplorerSettings(IgnoreApi = true)] [HttpPost] [Route(UniversalWebApiHandlerRoute)] public async Task<IActionResult> UniversalWebApiHandler( [FromQuery(Name = "apitype")] string apiType, [FromQuery(Name = "an")] string targetApiName, [FromQuery(Name = "pn")] string targetProcessorName, [FromQuery(Name = "en")] string endpointName, [FromBody] JsonElement inputParameter) { try { /* empty post body will treat inputParameters==null, no content json array '[]' will treat inputParameters.Length==0*/ JsonElement[] inputParameters = null; if (inputParameter.ValueKind == JsonValueKind.Array) inputParameters = inputParameter.EnumerateArray().ToArray(); else if (inputParameter.ValueKind == JsonValueKind.Object) inputParameters = new JsonElement[] { inputParameter }; else if (inputParameter.ValueKind == JsonValueKind.Undefined) { } else inputParameters = new JsonElement[] { inputParameter }; var p = this.processorFactory().FirstOrDefault(p => p.MetaConfigName == targetProcessorName); if (p == null) return BadRequest("Could not find processor with name: " + targetProcessorName); if (string.IsNullOrEmpty(apiType) || string.IsNullOrEmpty(targetApiName)) return BadRequest("Must provide apiType and targetApiName in query"); var requestContext = new RequestContext(Request); if (apiType.ToLower() == "service") { var apiCallResult = await this.universalApiInvoker.InvokeUniversalApiServiceAsync(p, targetApiName, inputParameters, requestContext); return Json(apiCallResult, jsonSerializerOptions); } else if (apiType.ToLower() == "property") { object apiCallResult; if (inputParameters == null) { /*call 'get'*/ apiCallResult = this.universalApiInvoker.InvokeUniversalApiPropertyGet(p, targetApiName, requestContext); } else { /*call 'set'*/ JsonElement? setValue = null; if (inputParameters.Length == 0) { } else if (inputParameters.Length >= 2) { return BadRequest("Unexpected too many parameters passed in. when calling for set value for a Property, Either post a single JsonObject, or a JsonArray with single element of JsonObject"); } else setValue = inputParameters[0]; apiCallResult = this.universalApiInvoker.InvokeUniversalApiPropertySet(p, targetApiName, setValue, requestContext); } return Json(apiCallResult, jsonSerializerOptions); } return BadRequest(); } catch (Exception exx) { return StatusCode(500, exx.ToString()); } } public IActionResult Configure() { //return all descriptor IEnumerable<ProcessorMetaDescriptor> descriptors = null; descriptors = ObjectInstanceCreator.CurrentDomainProcessorEndPointTypes.ExtractProcessorMetaDescriptor().Skip(0); //sample: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7 var clientAcceptLanguageStr = Request.Headers[HeaderNames.AcceptLanguage].FirstOrDefault()?.Split(',')[0] ?? "en-us"; ReplaceWithLocalizedContent(descriptors, clientAcceptLanguageStr); return View(descriptors); } ///// <summary> ///// ///// </summary> ///// <param name="appOrHandlerFullName">like Applications.FDC.FdcServerHostApp, or Wayne_Pump_Dart.PumpGroupHandler</param> ///// <param name="input"></param> ///// <returns></returns> //[HttpPost] //public async Task<IActionResult> UniversalWebConsoleHandler( // [FromQuery(Name = "epn")] string appOrHandlerFullName, // [FromBody]ApiData input) //{ // return View("~/Home/WebConsoles/VeederRoot_ATG_Console_Handler"); //} } }