public void Parse(bool simulator=true) { var rootNode = _doc.SelectNodes("ussd"); _screens = new List<UssdScreen>(); _scripts = new List<UssdScript>(); _actions = new List<UssdAction>(); _formLists = new List<UssdFormList>(); _forms = new List<UssdForm>(); _menus = new List<UssdMenu>(); _boundModels = new Dictionary<string, dynamic>(); if (rootNode==null) { throw new Exception("no \"ussd\" element found"); } //gather scripts foreach (XmlNode node in rootNode) { foreach (XmlNode ussdElement in node.ChildNodes) { if (ussdElement.Name == "script") { if (ussdElement.Attributes.GetNamedItem("id") == null) { throw new Exception("script attribute \"id\" is required"); } var scriptId = ussdElement.Attributes["id"].InnerText; Console.WriteLine("gotten script {0}", scriptId); if (_screens.Any(s => s.Id == scriptId)) { throw new Exception($"script id already exists at {scriptId}"); } var script = new UssdScript { Id = scriptId, Content = ussdElement.InnerText, BindFrom = ussdElement.Attributes.GetNamedItem("bindfrom") != null?ussdElement.Attributes["bindfrom"].InnerText:"", BindTo= ussdElement.Attributes.GetNamedItem("bindto") != null?ussdElement.Attributes["bindto"].InnerText:"", }; _scripts.Add(script); Console.WriteLine("added script {0}",scriptId); } } } foreach (XmlNode node in rootNode) { foreach (XmlNode ussdElement in node.ChildNodes) { #region screens if (ussdElement.Name=="screen") { if (ussdElement.Attributes["id"] == null) { throw new Exception("screen id is required"); } var screenId = ussdElement.Attributes["id"].InnerText; Console.WriteLine("gotten screen {0}", screenId); if (_screens.Any(s => s.Id == screenId)) { throw new Exception($"screen id already exists at {screenId}"); } var ussdScreen = new UssdScreen { Id = screenId, }; foreach (XmlNode screenItem in ussdElement.ChildNodes) { #region Menu Element if (screenItem.Name.Equals("menu")) { var menuNodes = screenItem.ChildNodes; var ussdMenu = new UssdMenu(); if (screenItem.Attributes.Count > 0) { if (screenItem.Attributes["bindfrom"] != null) { //todo: always check if there's a corresponding bindto in another screen ussdMenu.BindFrom = new UssdBoundModel { Name = screenItem.Attributes["bindfrom"].InnerText }; } if (screenItem.Attributes["bindaction"] != null) { ussdMenu.BindAction = new UssdBoundAction(screenItem.Attributes["bindaction"].InnerText); } } foreach (XmlNode menuNode in menuNodes) { if (menuNode.Name == "title") { ussdMenu.Title = menuNode.InnerText.ToUssdString(simulator); } if (menuNode.Name == "option") { var menuOption = new UssdMenuOption { Text = menuNode.InnerText, Value = menuNode.Attributes["value"].InnerText.ToUssdString(simulator), OnSelect = new UssdNavigator(menuNode.Attributes["onselect"].InnerText) }; ussdMenu.Options.Add(menuOption); } } ussdScreen.UssdItems.Add(ussdMenu); _menus.Add(ussdMenu); Console.WriteLine("added menu {0}", ussdMenu.Title); } #endregion #region Form Element if (screenItem.Name.Equals("form")) { var formNodes = screenItem.ChildNodes; var ussdForm = new UssdForm(); if (screenItem.Attributes.Count > 0) { if (screenItem.Attributes["bindto"] != null) { ussdForm.BindTo = new UssdBoundModel { Name = screenItem.Attributes["bindto"].InnerText }; //_boundModels[screenItem.Attributes["bindto"].InnerText] = new Dictionary<string, string>(); } if (screenItem.Attributes["onsubmit"] != null) { ussdForm.OnSubmit = new UssdNavigator(screenItem.Attributes["onsubmit"].InnerText); } else { throw new Exception("form attribute \"onsubmit\" is required"); } } foreach (XmlNode formNode in formNodes) { if (formNode.Name == "title") { ussdForm.Title = formNode.InnerText.ToUssdString(simulator); } if (formNode.Name == "input") { var ussdFormInput = new UssdFormInput { Id = formNode.Attributes["id"].InnerText, Type = formNode.Attributes["type"].InnerText, Display = formNode.Attributes["display"].Value.ToUssdString(simulator), Required = bool.Parse(formNode.Attributes["required"].InnerText), MaxLength = formNode.Attributes["maxlength"] == null ? (int?)null : int.Parse(formNode.Attributes["maxlength"].InnerText) }; if (ussdFormInput.Type == "list") { ussdFormInput.IsList = true; ussdFormInput.ListId = formNode.Attributes["listid"].InnerText; } ussdForm.Inputs.Add(ussdFormInput); } if (formNode.Name == "list") { var formListNodes = formNode.ChildNodes; var ussdFormList = new UssdFormList { Id = formNode.Attributes["id"].InnerText }; bool hasRepeater = formNode.Attributes.GetNamedItem("repeater") != null; if (hasRepeater) { ussdFormList.Repeater = formNode.Attributes["repeater"].InnerText; var scriptItem = _scripts.FirstOrDefault(s => s.Id == ussdFormList.RepeaterScriptId); if (scriptItem == null) { throw new Exception( $"script id \"{ussdFormList.RepeaterScriptId}\" not found"); } } else { foreach (XmlNode formListNode in formListNodes) { ussdFormList.Choices.Add(new UssdFormListChoice { Text = formListNode.InnerText, Selector = formListNode.Attributes["selector"].InnerText, Value = formListNode.Attributes["value"].InnerText }); } } _formLists.Add(ussdFormList); } } ussdScreen.UssdItems.Add(ussdForm); _forms.Add(ussdForm); Console.WriteLine("added form {0}", ussdForm.Title); } #endregion #region Text Element if (screenItem.Name.Equals("text")) { var ussdText = new UssdText(screenItem.InnerText.ToUssdString(simulator)); if (screenItem.Attributes.Count > 0) { if (screenItem.Attributes["bindfrom"] != null) { //todo: always check if there's a corresponding bindto in another screen ussdText.BindFrom = new UssdBoundModel { Name = screenItem.Attributes["bindfrom"].InnerText }; } if (screenItem.Attributes["bindaction"] != null) { ussdText.BindAction = new UssdBoundAction(screenItem.Attributes["bindaction"].InnerText); } } ussdScreen.UssdItems.Add(ussdText); } #endregion #region Action Element if (screenItem.Name.Equals("action")) { var actionNode = screenItem.ChildNodes; var ussdAction = new UssdAction(); if (screenItem.Attributes.Count > 0) { if (screenItem.Attributes["id"] != null) { ussdAction.Id = screenItem.Attributes["id"].InnerText; } else { throw new Exception("action attribute id is required"); } if (screenItem.Attributes["bindto"] != null) { ussdAction.BindTo = new UssdBoundModel { Name = screenItem.Attributes["bindto"].InnerText }; // _boundModels[screenItem.Attributes["bindto"].InnerText] = new Dictionary<string, string>(); } else { throw new Exception("action attribute bindto is required"); } if (screenItem.Attributes["cycle"] != null) { UssdActionCycle cycle; if (!Enum.TryParse(screenItem.Attributes["cycle"].InnerText, true, out cycle)) { throw new Exception(String.Format("action attribute cycle has an unknown value. Allowed values are: {0} or {1}", UssdActionCycle.Always, UssdActionCycle.Once)); } ussdAction.Http.Cycle = cycle; } } else { throw new Exception("form attribute id and bindto is required"); } foreach (XmlNode xmlNode in actionNode) { if (xmlNode.Attributes.Count > 0) { if (xmlNode.Attributes["url"] != null) { ussdAction.Http.Url = xmlNode.Attributes["url"].InnerText.ToUssdString(simulator); } else { throw new Exception("http attribute url is required"); } if (xmlNode.Attributes["method"] != null) { UssdActionHttpMethod httpMethod; if (!Enum.TryParse(xmlNode.Attributes["method"].InnerText, true, out httpMethod)) { throw new Exception("http attribute method is invalid"); } ussdAction.Http.Method = httpMethod; } else { throw new Exception("http attribute method is required"); } } if (xmlNode.Name == "http") { var httpNodes = xmlNode.ChildNodes; foreach (XmlNode httpNode in httpNodes) { if (httpNode.Name == "header") { var headers = httpNode.ChildNodes; foreach (XmlNode header in headers) { if (header.Name == "add") { var ussdActionHttpHeader = new UssdActionHttpHeader { Name = header.Attributes["key"].InnerText, Value = header.Attributes["value"].InnerText, }; ussdAction.Http.Headers.Add(ussdActionHttpHeader); } } } if (httpNode.Name == "body") { if (httpNode.Attributes["bindfrom"] != null) { ussdAction.Http.Body.BindFrom = new UssdBoundModel { Name = httpNode.Attributes["bindfrom"].InnerText }; } var headers = httpNode.ChildNodes; foreach (XmlNode header in headers) { if (header.Name == "add") { var ussdActionHttpBody = new UssdActionHttpBodyParam { Name = header.Attributes["key"].InnerText, Value = header.Attributes["value"].InnerText, }; ussdAction.Http.Body.Params.Add(ussdActionHttpBody); } } } if (httpNode.Name == "response") { var responses = httpNode.ChildNodes; foreach (XmlNode response in responses) { if (response.Name == "code") { var ussdActionHttpResponse = new UssdActionHttpResponse { Value = int.Parse(response.Attributes["value"].InnerText), Goto = new UssdNavigator(response.Attributes["goto"].InnerText), }; ussdAction.Http.Responses.Add(ussdActionHttpResponse); } } } } } } ussdScreen.UssdItems.Add(ussdAction); _actions.Add(ussdAction); Console.WriteLine("added action {0}", ussdAction.Id); } #endregion } if (ussdElement.Attributes["main"] != null ) { var mainString = ussdElement.Attributes["main"].InnerText; bool isMain; if (bool.TryParse(mainString, out isMain)) { ussdScreen.Main = isMain; } } _screens.Add(ussdScreen); Console.WriteLine("{0} screens", _screens.Count); } #endregion } } if (_screens.Count == 0) { throw new Exception("no screens"); } if (!_screens.Any(s=>s.Main)) { throw new Exception("no main attribute on any of the screens. Please include main=\"true\" as attribute on your main screen element"); } }
private async Task<Tuple<HttpStatusCode, JObject>> GrabActionResponse(UssdAction actionItem) { var resp = new Tuple<HttpStatusCode, JObject>(HttpStatusCode.Gone, JObject.FromObject(new {Name="fake"})); try { var restClient = new HttpClient(); restClient.DefaultRequestHeaders.Clear(); foreach (var ussdActionHttpHeader in actionItem.Http.Headers) { restClient.DefaultRequestHeaders.Add(ussdActionHttpHeader.Name,ussdActionHttpHeader.Value); } HttpResponseMessage result = null; var formItems = _boundModels.FirstOrDefault(b => b.Key == actionItem.Http.Body.BindFrom.Name); Dictionary<string, string> f = new Dictionary<string, string>(); if (formItems.Value!=null) { f = formItems.Value; } if (actionItem.Http.Body.Params.Any()) { foreach (var ussdActionHttpBodyParam in actionItem.Http.Body.Params) { f.Add(ussdActionHttpBodyParam.Name, ussdActionHttpBodyParam.Value); } } HttpContent content = new StringContent(JsonConvert.SerializeObject(f)); content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); //todo: document that by default content-type is application/json switch (actionItem.Http.Method) { case UssdActionHttpMethod.Post: result = await restClient.PostAsync(new Uri(actionItem.Http.Url), content); break; case UssdActionHttpMethod.Put: result = await restClient.PutAsync(new Uri(actionItem.Http.Url), content); break; case UssdActionHttpMethod.Get: result = await restClient.GetAsync(new Uri(actionItem.Http.Url)); break; case UssdActionHttpMethod.Delete: result = await restClient.DeleteAsync(new Uri(actionItem.Http.Url)); break; case UssdActionHttpMethod.Patch: break; default: throw new ArgumentOutOfRangeException(); } if (result==null) { return resp; } var serverResp = await result.Content.ReadAsStringAsync(); if (result.IsSuccessStatusCode) { resp = new Tuple<HttpStatusCode, JObject>(HttpStatusCode.OK, JObject.Parse(serverResp)); } else { resp = new Tuple<HttpStatusCode, JObject>(result.StatusCode, JObject.Parse(serverResp)); } } catch (Exception exception) { resp = new Tuple<HttpStatusCode, JObject>(HttpStatusCode.ExpectationFailed, JObject.FromObject(new {exception.Message})); } return resp; }