Skip to content

Instantly share code, notes, and snippets.

@buzzedword
Created March 23, 2011 00:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save buzzedword/882368 to your computer and use it in GitHub Desktop.
Save buzzedword/882368 to your computer and use it in GitHub Desktop.
Zendesk REST Wrapper
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
namespace MyApplication
{
/// <summary>
/// This class is a simple authentication/ticket submission program for ZenDesk.
/// </summary>
class ZenRequest
{
/// <summary>
/// AppConfigFileSettings is class that reads/updates the App.Config file for
/// simple transport of data. You should use more secure means to pass user sessions,
/// this was necessary in my desktop app for GPO purposes.
/// </summary>
AppConfigFileSettings reg = new AppConfigFileSettings();
/// <summary>
/// ZenRequest is our constructor, and for security purposes, we do not allow null at
/// build time.
/// </summary>
/// <param name="userName">Agent or Admin with all ticket access</param>
/// <param name="password">In cleartext. This is a lament's example-- please secure your code!</param>
/// <param name="debug">Are you using the sandbox URL, or the production?</param>
public ZenRequest(String userName, String password, Boolean debug)
{
if (debug == true)
{
zenURL = "<sandbox_url>"; // Replace this string with the URL of your sandbox to test the app.
}
else if (debug == false)
{
zenURL = "<production_url>"; // Replace this string with the URL of your production helpdesk.
}
zenUserName = userName;
zenPassword = password;
}
/// <summary>
/// ZenAuth is a general authentication call for your application. This is essentially your
/// "application login", and should be executed once before any commands are posted against the
/// ZenDesk API to speed up the initial call.
///
/// Remember, your user account must have "all ticket" access for this to work. This is the best
/// method I've found for authenticating a user against ZenDesk.
/// </summary>
/// <returns>
/// On a successful connection, return the ZenDesk Status code. You must handle the status
/// outside of this class.
///
/// Note: It is possible to get a Status response that does NOT authenticate you, make sure you stay
/// current with the ZenDesk REST API.
/// </returns>
public string ZenAuth()
{
WebClient zenWeb = zenClient(); // See zenClient below for further description.
// The next two lines formulate a GET request using the URL we specified at object creation
// as well as the "users.xml" file we use for simple authentication.
String zenFile = "users.xml";
String zenRequest = zenURL + zenFile;
// This gracefully executes a GET request, and will degrade without an application crash.
try
{
zenWeb.DownloadData(zenRequest); // Start the GET request.
return zenWeb.ResponseHeaders["Status"]; // Returns your status code. You want to be
// Checking for a 200 OK.
}
catch (Exception e)
{
return "Error"; // Self explanatory. Handle your error properly.
}
}
/// <summary>
/// This method performs two functions: the first is to create a new ticket, using an XML-structured POST.
/// The second function is to return the ticket number for application usage.
/// </summary>
/// <param name="userName">This is the email you will be creating a ticket for.</param>
/// <param name="displayName">How do you want to display the name in ZenDesk?</param>
/// <returns>Status Code for action.</returns>
public String ZenSubmit(String userName, String displayName)
{
WebClient zenWeb = zenClient(); // See zenClient below for more information.
// The following lines structure a POST request against tickets.xml. I've also accounted
// for an additional custom ticket field you may have in your helpdesk.
String zenFile = "tickets.xml";
String zenRequest = zenURL + zenFile;
String customTicketField = "";
// Remember, "reg" is reading from my app.config-- I strongly recommend using another
// method to store your user session.
if (reg.ReturnAppSettings("userName") == "<specificUser1>") // Check for specific user
// Enter user specific custom field.
{
customTicketField = @"<ticket-field-entry>
<ticket-field-id>[ID for custom ticket field.]</ticket-field-id>
<value>[tag for custom ticket field]</value>
</ticket-field-entry>";
}
else if (reg.ReturnAppSettings("userName") == "<specificUser2>") // See above.
{
customTicketField = @"<ticket-field-entry>
<ticket-field-id>[ID for custom ticket field.]</ticket-field-id>
<value>[tag for custom ticket field]</value>
</ticket-field-entry>";
}
else
{
customTicketField = ""; // Do not enter custom field.
}
// The String Literal below constructs the XML needed for a successful POST, based on the
// fields we've already populated above.
String createTicketXML = @"<ticket>
<subject>[Ticket Subject]</subject>
<description>[Body of ticket]</description>
<requester-name>" + displayName + @"</requester-name>
<requester-email>" + userName + @"</requester-email>
<set-tags>[tag to set, if any]</set-tags>
<ticket-field-entries type=""array"">
" + customTicketField + @"
</ticket-field-entries>
</ticket>";
zenWeb.UploadString(zenRequest, createTicketXML); // Execute the POST request, and hopefully create ticket.
// If the ticket is created successfully, return the ticket number.
// Check the REST API frequently in case the location of the ticket changes.
if (zenWeb.ResponseHeaders["Status"] == "201 Created")
{
zenTicketURL = zenWeb.ResponseHeaders["Location"]; // Returns the ticket location in XML.
// The ticket number is extracted by removing the URL we are using, followed by the tickets/ URI,
// and finally by stripping ".xml" from the remaining string. Again, all of these locations are
// documented in the REST API for ZenDesk.
int URLcount = CountChars(zenURL + "tickets/");
String returnVar = zenTicketURL;
returnVar = returnVar.Remove(0, URLcount);
char[] xmlChar = { '.', 'x', 'm', 'l' };
returnVar = returnVar.TrimEnd(xmlChar);
return returnVar;
}
else
{
return "FAIL"; // Ticket creation unsuccessful.
}
}
/// <summary>
/// This is just an accessory method to get the last ticket created, should you ever need to.
/// </summary>
/// <returns>XML return of last ticket created.</returns>
public String getTicket() {
return zenTicketURL;
}
/// <summary>
/// This method is the most important one in the entire class. Instead of creating a WebClient for every single
/// action I had to take, I opted instead to set the basics up in zenClient, which can then be object referenced
/// with every new call, and extended. This also promises proper garbage collection from the program, disposing of
/// queries after they have been executed.
/// </summary>
/// <returns>Default settings for all WebClients</returns>
private WebClient zenClient()
{
WebClient zenPrimaryClient = new WebClient(); // WebClient will provide transport for GET/POST commands.
zenPrimaryClient.Credentials = new System.Net.NetworkCredential(zenUserName, zenPassword); // Secures UserName and Password for usage.
zenPrimaryClient.Headers.Add("Content-Type", "application/xml"); // Set your MIME type.
zenPrimaryClient.BaseAddress = zenURL; // Set the Base URL for all commands.
return zenPrimaryClient; // Return all settings.
}
/// <summary>
/// Generic method to count all characters in a string, and account for all spaces.
/// Recommended for usage with URLs.
/// </summary>
/// <param name="value">URL to check</param>
/// <returns>Characters in String/URL</returns>
static int CountChars(string value)
{
int result = 0;
bool lastWasSpace = false;
foreach (char c in value)
{
if (char.IsWhiteSpace(c))
{
if (lastWasSpace == false)
{
result++;
}
lastWasSpace = true;
}
else
{
result++;
lastWasSpace = false;
}
}
return result;
}
#region Declarations
private String zenTicketURL;
private String zenUserName;
private String zenPassword;
private String zenURL;
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment