using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Security.Policy; using System.Text; using System.Xml; using System.Xml.Serialization; namespace FogbugzWrapper { public class FogbugzClient { public string Token { get; } = "1u4thrqm93uqpfr8ptbj2bc112gseo"; public static string FogbugzURL { get; set; } private bool _loggedOn = false; public string FogbugzLogDir { get; set; } = "FogbugzLog\\"; public bool IsLoggedIn() { return _loggedOn; } public FogbugzClient() { } public FogbugzClient(string uRL, string token) { Init(uRL); Token = token; _loggedOn = LogOn(); } public FogbugzClient(string uRL, string email, string password) { Init(uRL); var doc = new XmlDocument(); var resp = Command($"logon&email={System.Net.WebUtility.UrlEncode(email)}&password={password}"); doc.LoadXml(resp); Token = doc.SelectSingleNode("response").FirstChild.InnerText; _loggedOn = LogOn(); } private void Init(string uRL) { if (!Directory.Exists($"{FogbugzLogDir}")) { Directory.CreateDirectory($"{FogbugzLogDir}"); } FogbugzURL = uRL; } private string TokenedCommand(string command) { return Command($"{command}&token={Token}"); } private string Command(string command) { // Create a request using a URL that can receive a post. WebRequest request = WebRequest.Create(FogbugzURL); request.Proxy = null; // Set the Method property of the request to POST. request.Method = "POST"; // Create POST data and convert it to a byte array. string postData = $"cmd={command}"; byte[] byteArray = Encoding.UTF8.GetBytes(postData); // Set the ContentType property of the WebRequest. request.ContentType = "application/x-www-form-urlencoded"; // Set the ContentLength property of the WebRequest. request.ContentLength = byteArray.Length; // Get the request stream. Stream dataStream = request.GetRequestStream(); // Write the data to the request stream. dataStream.Write(byteArray, 0, byteArray.Length); // Close the Stream object. dataStream.Close(); // Get the response. var response = request.GetResponse(); // Display the status. //Console.WriteLine(((HttpWebResponse)response).StatusDescription); var responseFromServer = string.Empty; // Get the stream containing content returned by the server. // The using block ensures the stream is automatically closed. using (dataStream = response.GetResponseStream()) { // Open the stream using a StreamReader for easy access. StreamReader reader = new StreamReader(dataStream); // Read the content. responseFromServer = reader.ReadToEnd(); // Display the content. //Console.WriteLine(responseFromServer); } // Close the response. response.Close(); return responseFromServer; } public string GetArea(int bugNumber) { var doc = new XmlDocument(); var resp = TokenedCommand($"search&q={bugNumber}&cols=sArea"); doc.LoadXml(resp); return doc.SelectSingleNode("response").FirstChild.FirstChild.FirstChild.InnerText; } public void CreateNewTicket(string projectName, string projArea, string title, string description, string assignedTo) { var doc = new XmlDocument(); var resp = TokenedCommand($"new&sProject={projectName}&sArea={projArea}&sTitle={title}&sEvent={description}&sPersonAssignedTo={assignedTo}&fRichText=1"); doc.LoadXml(resp); //return doc.SelectSingleNode("response").FirstChild.FirstChild.FirstChild.InnerText; } public string GetTitle(int bugNumber) { var doc = new XmlDocument(); var resp = TokenedCommand($"search&q={bugNumber}&cols=sTitle"); doc.LoadXml(resp); return doc.SelectSingleNode("response").FirstChild.FirstChild.FirstChild.InnerText; } public string GetProject(int bugNumber) { var doc = new XmlDocument(); var resp = TokenedCommand($"search&q={bugNumber}&cols=sProject"); doc.LoadXml(resp); return doc.SelectSingleNode("response").FirstChild.FirstChild.FirstChild.InnerText; } public void TouchCase(int caseIdx) { TokenedCommand($"edit&ixBug={caseIdx}&sEvent=Touched"); } public void ResolveAndClose(int caseIdx) { TokenedCommand($"resolve&ixBug={caseIdx}&sEvent=ResolvedByManuscriptEdit"); TokenedCommand($"close&ixBug={caseIdx}&sEvent=ClosedByManuscriptEdit"); } public ProjectResponse GetProjects() { var resp = TokenedCommand($"listProjects"); var serializer = new XmlSerializer(typeof(ProjectResponse)); using (var reader = new StringReader(resp)) { return (ProjectResponse)serializer.Deserialize(reader); } } public CasesResponse GetCases(int filter) { var resp = TokenedCommand($"listCases&sFilter={filter}&cols=ixBug,ixProject,fOpen,sTitle,sProject,sArea,sStatus,dtLastUpdated,events"); var serializer = new XmlSerializer(typeof(CasesResponse)); using (var reader = new StringReader(resp)) { return (CasesResponse)serializer.Deserialize(reader); } } public List GetEvents(int bugNumber) { var doc = new XmlDocument(); var resp = TokenedCommand($"search&q={bugNumber}&cols=events,plugin_customfield"); doc.LoadXml(resp); var timeStamps = doc.GetElementsByTagName("dt"); if (timeStamps.Count == 0) return new List(); var lastUpdate = DateTime.Parse(timeStamps.Item(timeStamps.Count - 1).InnerText); if (File.Exists($"{FogbugzLogDir}{bugNumber}.{lastUpdate.Ticks}.xml")) { return DeSerializeObject>($"{FogbugzLogDir}{bugNumber}.{lastUpdate.Ticks}.xml"); } var fbEventList = new List(); var events = doc.SelectSingleNode("response").FirstChild.FirstChild.FirstChild; foreach (XmlNode eventNode in doc.SelectSingleNode("response").FirstChild.FirstChild.FirstChild) { fbEventList.Add(new FBEvent(eventNode)); } fbEventList.Reverse(); SerializeObject(fbEventList, $"{FogbugzLogDir}{bugNumber}.{lastUpdate.Ticks}.xml"); return fbEventList; } public List GetZendeskCases(int updatedMinutes) { Console.WriteLine($"GetZendeskCases - updatedMinutes:{updatedMinutes}"); var rt = new List(); var doc = new XmlDocument(); if (!IsLoggedIn()) { Console.WriteLine("GetZendeskCases - Not Logged In."); return null; } var resp = TokenedCommand($"search&q=ZendeskTicketNumber:* lastupdated:\"-{updatedMinutes}m..now\"&cols=plugin_customfields_at_fogcreek_com_zendeskxissuea73,events,sStatus"); doc.LoadXml(resp); //Console.WriteLine($"Full Response: {doc.SelectSingleNode("response").InnerText}"); var zdCases = doc.SelectSingleNode("response").ChildNodes[0]; if (zdCases == null) { Console.WriteLine($"GetZendeskCases - zdCases is null"); return null; } Console.WriteLine($"GetZendeskCases - zdCases.ChildNodes.Count:{zdCases.ChildNodes.Count}"); foreach (XmlNode zdNode in zdCases) { if (0 == zdNode.SelectNodes("events").Count || string.IsNullOrEmpty(zdNode.SelectSingleNode("plugin_customfields_at_fogcreek_com_zendeskxissuea73").InnerText)) { continue; } var zdNumber = int.Parse(zdNode.SelectSingleNode("plugin_customfields_at_fogcreek_com_zendeskxissuea73").InnerText); var fbNumber = int.Parse(zdNode.Attributes["ixBug"].Value); var oldFbStatus = FBEvent.FBStatuses.UNKNOWN; foreach (XmlNode fbEvent in zdNode.SelectSingleNode("events")) { var fbLatestEvent = new FBEvent(fbEvent); if (DateTime.Parse(fbLatestEvent.TimeStamp) > DateTime.Now.AddMinutes(-updatedMinutes) && fbLatestEvent.Status != oldFbStatus && fbLatestEvent.StatusString != "Edited" && fbLatestEvent.StatusString != "Assigned") { rt.Add(new ZendeskCase(zdNumber, fbNumber, fbLatestEvent)); } else { // } oldFbStatus = fbLatestEvent.Status; } } return rt; } /// /// Serializes an object. /// /// /// /// public void SerializeObject(T serializableObject, string fileName) { if (serializableObject == null) { return; } try { XmlDocument xmlDocument = new XmlDocument(); XmlSerializer serializer = new XmlSerializer(serializableObject.GetType()); using (MemoryStream stream = new MemoryStream()) { serializer.Serialize(stream, serializableObject); stream.Position = 0; xmlDocument.Load(stream); xmlDocument.Save(fileName); } } catch (Exception ex) { //Log exception here } } /// /// Deserializes an xml file into an object list /// /// /// /// public T DeSerializeObject(string fileName) { if (string.IsNullOrEmpty(fileName)) { return default(T); } T objectOut = default(T); try { XmlDocument xmlDocument = new XmlDocument(); xmlDocument.Load(fileName); string xmlString = xmlDocument.OuterXml; using (StringReader read = new StringReader(xmlString)) { Type outType = typeof(T); XmlSerializer serializer = new XmlSerializer(outType); using (XmlReader reader = new XmlTextReader(read)) { objectOut = (T)serializer.Deserialize(reader); } } } catch (Exception) { //Log exception here } return objectOut; } public FBEvent.FBStatuses GetStatus(int bugNumber) { var fbevents = GetEvents(bugNumber); foreach (var e in fbevents) { if (e.Status != FBEvent.FBStatuses.UNKNOWN) { return e.Status; } } return FBEvent.FBStatuses.UNKNOWN; } public string GetReleaseNote(int bugNumber) { var fbevents = GetEvents(bugNumber); foreach (var e in fbevents) { if (!string.IsNullOrEmpty(e.ReleaseNote)) { return e.ReleaseNote; } } return string.Empty; } public int GetZendeskNumber(int bugNumber) { var fbevents = GetEvents(bugNumber); foreach (var e in fbevents) { if (0 != e.ZendeskNumber) { return e.ZendeskNumber; } } return 0; } public bool LogOn() { _loggedOn = true; return _loggedOn; } public void LogOff() { var resp = TokenedCommand("logoff"); _loggedOn = false; } } }