Code Examples

Curl

The ubiquitous Curl HTTP client is probably the quickest way to get started with the STACK API. It can easily be used in scripts (such as post-commit hooks) to interact with your Unfuddle Account.

HTTP GET

The following example gets the XML representation of the project with an id ZZZZ via an HTTP GET.

curl -i -u username:password -X GET \
  -H 'Accept: application/xml' \
  'https://mysubdomain.unfuddle.com/api/v1/projects/ZZZZ.xml'

HTTP POST

The following example creates a new message in the project with an id ZZZZ via an HTTP POST.

curl -i -u username:password -X POST \
  -H 'Accept: application/xml' \
  -H 'Content-type: application/xml' \
  -d "<message><title>My New Message</title><body>Body text goes here...</body><body-format>markdown</body-format><categories><category id='545'/></categories></message>" \
  'https://mysubdomain.unfuddle.com/api/v1/projects/ZZZZ/messages'

Ruby NET::HTTP

Ruby comes out of the box with a fairly robust HTTP client library. The following example code will perform two actions in sequence. First it will get an XML listing of projects within an account via an HTTP GET, then it perform an HTTP POST to create a message in a project with an id of ZZZZ.

require 'net/https'

UNFUDDLE_SETTINGS = {
  :subdomain  => 'mysubdomain',
  :username   => 'username',
  :password   => 'password',
  :ssl        => true
}

http = Net::HTTP.new("#{UNFUDDLE_SETTINGS[:subdomain]}.unfuddle.com", UNFUDDLE_SETTINGS[:ssl] ? 443 : 80)

# if using ssl, then set it up
if UNFUDDLE_SETTINGS[:ssl]
  http.use_ssl = true
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end

# perform an HTTP GET
begin
  request = Net::HTTP::Get.new('/api/v1/projects.xml')
  request.basic_auth UNFUDDLE_SETTINGS[:username], UNFUDDLE_SETTINGS[:password]

  response = http.request(request)
  if response.code == "200"
    puts response.body
  else
    # hmmm...we must have done something wrong
    puts "HTTP Status Code: #{response.code}."
  end
rescue => e
  # do something smart
end

# perform an HTTP POST
# if testing, be sure to use a valid project id below
begin
  request = Net::HTTP::Post.new('/api/v1/projects/ZZZZ/messages.xml', {'Content-type' => 'application/xml'})
  request.basic_auth UNFUDDLE_SETTINGS[:username], UNFUDDLE_SETTINGS[:password]
  request.body = "<message><title>A New Message from the API</title><body>Pretty cool, huh?</body></message>"

  response = http.request(request)
  if response.code == "201"
    puts "Message Created: #{response['Location']}"
  else
    # hmmm...we must have done something wrong
    puts "HTTP Status Code: #{response.code}."
  end
rescue => e
  # do something smart
end

PHP

HTTP GET

The following example will get a listing of projects within the account in XML format via an HTTP GET and output some basic information about them. Thanks to Tom Power for providing the example.

<?php
// Edit your values here to match your account settings.
$config_method = 'GET';
$config_userpass = 'username:password';
$config_headers[] = 'Accept: application/xml';
$config_address = 'https://subdomain.unfuddle.com/api/v1/';
$config_datasource = 'projects.xml';

// Here we set up CURL to grab the data from Unfuddle
$chandle = curl_init();
curl_setopt($chandle, CURLOPT_RETURNTRANSFER, true);
curl_setopt($chandle, CURLOPT_URL, $config_address . $config_datasource);
curl_setopt($chandle, CURLOPT_HTTPHEADER, $config_headers);
curl_setopt($chandle, CURLOPT_USERPWD, $config_userpass);
curl_setopt($chandle, CURLOPT_CUSTOMREQUEST, $config_method);
$output = curl_exec($chandle);
curl_close($chandle);

// XML in PHP is simple to use with SimpleXML, go figure!
$xml = new SimpleXMLElement($output);

foreach ($xml->project as $project) {
  echo '(ID: ' . $project->{'id'} . ') - ' . $project->{'title'} . "\n";
  echo "-------------------------------------------------------\n";
  echo 'Created: ' . $project->{'created-at'} . "\n";
  echo 'Last mod: ' . $project->{'updated-at'} . "\n";
  echo "-------------------------------------------------------\n";
  echo $project->{'description'} . "\n";
  echo "-------------------------------------------------------\n";
}
?>

Python

The following example will get a listing of projects within the account and allow the user to choose to see users' ticket information for each. Thanks to Shabda Raaj from agiliq.com for providing the example.

import getpass
import simplejson
import sys
import urllib2

from datetime import datetime, date

try:
    from settings import *
except ImportError:
    ACCOUNT_DETAILS = {
        'account': '',
        'username': '',
        'password': '',
    }
    SEND_MAIL = False

if not ACCOUNT_DETAILS['account']:
    ACCOUNT_DETAILS['account'] = raw_input('Enter Unfuddle account name: ')

if not ACCOUNT_DETAILS['username']:
    ACCOUNT_DETAILS['username'] = raw_input('Username: ')

if not ACCOUNT_DETAILS['password']:
    ACCOUNT_DETAILS['password'] = getpass.getpass()


class Unfuddle(object):
    def __init__(self):
        self.base_url = 'https://%s.unfuddle.com' % (ACCOUNT_DETAILS['account'])
        self.api_base_path = '/api/v1/'

    def get_data(self, api_end_point):
        # url = 'https://subdomain.unfuddle.com/api/v1/projects'
        url = self.base_url + self.api_base_path + api_end_point

        auth_handler = urllib2.HTTPBasicAuthHandler()
        auth_handler.add_password(realm='/stack/docs',
                                  uri=url,
                                  user=ACCOUNT_DETAILS['username'],
                                  passwd=ACCOUNT_DETAILS['password'])

        opener = urllib2.build_opener(auth_handler)
        opener.addheaders = [('Content-Type', 'application/xml'), ('Accept', 'application/json')]

        # print '\n', url, '\n'
        try:
            response = opener.open(url).read().strip()
            # print 'response:', response
            return simplejson.loads(response)
        except IOError, e:
            print IOError, e

    def get_projects(self):
        return self.get_data('projects')

    def select_project(self):
        projects = self.get_projects()
        if len(projects) == 1:
            print 'There is only one project "%s"' % (projects[0]['title'])
            return projects[0]
        for index, project in enumerate(projects):
            print '%s. %s' % (index+1, project['title'])
        project_index = int(raw_input('Enter the project number: ')) - 1
        return projects[project_index]

    def get_tickets(self, project=None):
        if not project:
            project = self.select_project()
        api_end_point = 'projects/%s/tickets' % (project['id'])
        tickets = self.get_data(api_end_point)
        return project, tickets

    def dynamic_report(self, project=None, query_string=None):
        if project:
            api_end_point = 'projects/%s/ticket_reports/dynamic' % (project['id'])
        else:
            api_end_point = 'ticket_reports/dynamic'
        if query_string:
            api_end_point += '?%s' % (query_string)
        dynamic_report = self.get_data(api_end_point)
        return dynamic_report

def get_ticket_report():
    unfuddle = Unfuddle()
    project, tickets = unfuddle.get_tickets()
    print '\n'
    print 'Total tickets in project:', len(tickets)

    today_new_tickets = []
    today_resolved_tickets = []
    today_closed_tickets = []
    total_closed_tickets = 0
    total_resolved_tickets = 0
    total_pending_tickets = 0

    for ticket in tickets:
        if datetime.strptime(ticket['created_at'], '%Y-%m-%dT%H:%M:%SZ').date() == datetime.utcnow().date():
            today_new_tickets.append(ticket)
        elif datetime.strptime(ticket['updated_at'], '%Y-%m-%dT%H:%M:%SZ').date() == datetime.utcnow().date() and ticket['status'] == 'resolved':
            today_resolved_tickets.append(ticket)
        elif datetime.strptime(ticket['updated_at'], '%Y-%m-%dT%H:%M:%SZ').date() == datetime.utcnow().date() and ticket['status'] == 'closed':
            today_closed_tickets.append(ticket)

        if ticket['status'] == 'closed':
            total_closed_tickets += 1
        elif ticket['status'] == 'resolved':
            total_resolved_tickets += 1
        else:
            total_pending_tickets += 1

    print '\n'
    print 'Today new tickets:', len(today_new_tickets)
    print 'Today resolved tickets:', len(today_resolved_tickets)
    print 'Today closed tickets:', len(today_closed_tickets)
    print '\n'
    print 'Total closed tickets:', total_closed_tickets
    print 'Total resolved tickets:', total_resolved_tickets
    print 'Total pending tickets:', total_pending_tickets

    query_string = 'group_by=assignee'
    query_string += '&conditions_string=status-neq-closed,status-neq-resolved'
    dynamic_report = unfuddle.dynamic_report(project, query_string=query_string)

    print '\n'
    print 'Number of tickets in each person queue'
    for group in sorted(dynamic_report['groups'], cmp=lambda x, y: cmp(len(y['tickets']), len(x['tickets']))):
        if not group:
            continue
        print '\t', group['title'], ' ' * (25 - len(group.get('title', ''))), '-', len(group['tickets'])

    """
    query_string = 'group_by=assignee'
    query_string += '&conditions_string=status-eq-closed|status-eq-resolved'
    # query_string += '&updated_at=%s' % (date.today().strftime('%Y-%m-%dT%H:%M:%SZ'))
    dynamic_report = unfuddle.dynamic_report(project, query_string=query_string)

    dynamic_report['new_groups'] = []
    for group in dynamic_report['groups']:
        new_group = group.copy()
        new_group['tickets'] = filter(lambda x: datetime.strptime(x['updated_at'], '%Y-%m-%dT%H:%M:%SZ').date() == datetime.utcnow().date(), group['tickets'])
        if new_group['title'] == 'Javed K.':
            print new_group
        dynamic_report['new_groups'].append(new_group)

    print '\n'
    print 'Number of tickets closed/resolved by each person today'
    for group in sorted(dynamic_report['new_groups'], cmp=lambda x, y: cmp(len(y['tickets']), len(x['tickets']))):
        if not group:
            continue
        print '\t', group['title'], ' ' * (25 - len(group.get('title', ''))), '-', len(group['tickets'])
    """

if __name__ == '__main__':
    get_ticket_report()

C# (.NET)

HTTP GET

The following example will get a listing of projects within the account in XML format via an HTTP GET. Thanks to Lance Sun for providing the example.

string url = "https://mysubdomain.unfuddle.com/api/v1/projects.xml";
string username = "username";
string password = "password";
string credentials = String.Format( "{0}:{1}", username, password );
string basicAuth = Convert.ToBase64String( Encoding.ASCII.GetBytes( credentials ) );
WebRequest request = WebRequest.Create( url );
request.Headers.Add( "Authorization", "Basic " + basicAuth );
WebResponse response = request.GetResponse();
StreamReader reader = new StreamReader( response.GetResponseStream() );
Console.WriteLine( reader.ReadToEnd() );

JavaScript

With the inclusion JSON to the STACK API, consuming it from JavaScript is a breeze. We strongly suggest using a JavaScript library such as Prototype.

Note that you must manually add the Authorization header to the request with your credentials base64 encoded. This can be easily done with any JavaScript base64 encoding/decoding library available out there (e.g. btoa).

HTTP GET

The following example will get a listing of projects within the account in JSON format via an HTTP GET. The response body is then eval'ed and stored in the variable projects_array.

var projects_array = [];
new Ajax.Request("https://mysubdomain.unfuddle.com/api/v1/projects.json", {
  method: 'get',
  requestHeaders: [ "Authorization", "Basic " + base64_encode('username:password'), "Accept", "application/json" ],
  onCreate: function(response) {
    if (response.request.isSameOrigin())
      return;
    var t = response.transport;
    t.setRequestHeader = t.setRequestHeader.wrap(function(original, k, v) {
      if (/^(accept|accept-language|content-language|authorization)$/i.test(k))
        return original(k, v);
      if (/^content-type$/i.test(k) &&
          /^(application\/x-www-form-urlencoded|multipart\/form-data|text\/plain)(;.+)?$/i.test(v))
        return original(k, v);
      return;
    });
  },
  onSuccess: function(transport) {
    if(transport.status == 200) {
      // now we can just eval the responseText and get a JavaScript object
      project_array = transport.responseText.evalJSON();
      alert('Project Total: ' + project_array.length);
    }
  },
  onFailure: function(transport) {
    alert('Something went wrong!');
  }
});

HTTP POST

The following example creates a new message in the project with an id ZZZZ via an HTTP POST. Note that the post data must be in XML format.

new Ajax.Request("https://https://mysubdomain.unfuddle.com/api/v1/projects/ZZZZ/messages.json", {
  method: 'post',
  postBody: "<message><title>A New Message from the API</title><body>Pretty cool, huh?</body></message>",
  requestHeaders: [ "Authorization", "Basic " + base64_encode('username:password'), "Accept", "application/json", "Content-type" , "application/xml"],
  onSuccess: function(transport) {
    if(transport.status == 201)
      alert("Message Created: " + transport.getHeader('Location'));
  },
  onFailure: function(transport) {
    // Identifiable errors are returned as a JSON array of strings
    alert("The following errors occurred: \n\n" + (transport.responseText.evalJSON() || []).join("\n"));
  }
});