bgeek.net

Slides from My OSLO “PDC Fireworks” Presentation

Posted by Owen Evans on Wednesday, November 5th, 2008

The lightning talks went well, everyone was reasonably happy to sit through the presentations and I’m hoping people took away some nuggets of information that will help them divulge deeper.
My talk spawned a small drinking game (although with mimed drinks) from James. A drink for every time I said “model”. For an 8-10 minute talk [...]

continue reading

Getting Oslo’s Intellipad to show MGrammar Mode

Posted by Owen Evans on Tuesday, October 28th, 2008

[EDIT: You can just launch Intellipad with Samples Enabled, from the Start Menu. For some reason my first port of call was to run Intellipad from the command line]
Ok I’m beginning to dive into Oslo, so I can at least Appear knowledgeable for the PDC Fireworks talk next week, but there was one thing that [...]

continue reading

Like footprints on the moon: beware your software legacy

Posted by Owen Evans on Wednesday, October 22nd, 2008

This post has been brewing for some time:
A couple of weeks ago I was very amused to get a tweet from Daniel Cazzulino (the guy behind Moq)

The reason I found the text funny was the idea that Daniel found it strange to have legacy parts of an application within a year of starting the application. [...]

continue reading

Connecting to the Xero API: Part 1 [Ruby, GET, Contacts]

Posted by Owen Evans on Wednesday, July 30th, 2008

Hey hey hey, so I finally get an excuse to post some ruby code, yay!

As part of my foray into the Xero API I wanted to get a feel for how easy it would be to connect from non .net languages and as my Ruby was getting a bit rusty I thought I’d pull out the old text editor and write up some simple methods and models for interacting with the API, so here’s the first part. How to connect to the GET methods for contacts.

The first step was to write down the specifications in RSpec, I’m glad to say they’re all green and for your reading pleasure here they are:

RSpec Results

26 examples, 0 failures

Finished in 3.567 seconds

Address
should have correct country
should have correct postal code
should have correct region
should have correct city
should have correct address line 4
should have correct address line 3
should have correct address line 2
should have correct address line 1
should have correct type
Phone
should have correct country code
should have correct area code
should have correct number
should have correct type
Contact fetch without specifying a contact id should fetch all
should fetch the correct number of contacts
Contact fetch, specifying an id
should populate phone numbers
should populate addresses
should fetch the correct contact
Contact
should populate phone data correctly
should have the correct number of phone numbers
should have the correct address
should have the correct number of addresses
should have the correct updated date
should have the correct email address
should have the correct status
should have the correct xero id
should have the correct name

Ok once those were defined it was time to build up the models, I cheated and used Rails to create some ActiveRecord classes really easily, the migrations look like this:

Phones Migration

class CreatePhones < ActiveRecord::Migration
  def self.up
    create_table :phones do |t|
      t.string :phone_type
      t.string :number
      t.string :area_code
      t.string :country_code

      t.timestamps
    end
  end

  def self.down
    drop_table :phones
  end
end

Addresses Migration

class CreateAddresses < ActiveRecord::Migration
  def self.up
    create_table :addresses do |t|
      t.string :addresstype
      t.string :line1
      t.string :line2
      t.string :line3
      t.string :line4
      t.string :city
      t.string :region
      t.string :postal_code
      t.string :country

      t.timestamps
    end
  end

  def self.down
    drop_table :addresses
  end
end

Contacts Migration

class CreateContacts < ActiveRecord::Migration
  def self.up
    create_table :contacts do |t|
      t.string :name
      t.string :xero_id
      t.string :status
      t.string :email_address
      t.datetime :updated_date_utc

      t.timestamps
    end
  end

  def self.down
    drop_table :contacts
  end
end

 

this should give you your 3 models, but of course we don’t want them to be populated from the database we want them to be populated from the xero api, as such each model has a from_xml method defined, which takes in the xml for the contact/address/phone as appropriate

Also the contact model has a fetch method which actually goes away and gets the xml stream either for an individual contact (if :xero_id is passed in) otherwise for all contacts

address.rb

require 'hpricot'
class Address < ActiveRecord::Base
  include XmlHelper
  has_one :contact
   def self.from_xml(address_xml)
    doc = Hpricot(address_xml)
    mappings = {
    :addresstype => "/address/addresstype",
    :line1 => "/address/addressline1",
    :line2 => "/address/addressline2",
    :line3 => "/address/addressline3",
    :line4 => "/address/addressline4",
    :city => "/address/city",
    :region => "/address/region",
    :postal_code => "/address/postalcode",
    :country => "/address/country",
    }
    attr = XmlHelper.populate_from_xml(doc,mappings)
    return Address.new(attr)
  end
end

phone.rb

require "hpricot"
class Phone < ActiveRecord::Base
  include XmlHelper
  has_one :contact
  def self.from_xml(phone_xml)
    doc = Hpricot(phone_xml)
    mappings = {
    :phone_type => "/phone/phonetype",
    :number => "/phone/phonenumber",
    :area_code => "/phone/phoneareacode",
    :country_code => "/phone/phonecountrycode",
    }
    attr=XmlHelper.populate_from_xml(doc,mappings)
    return Phone.new(attr)
  end
end

contact.rb

require 'hpricot'
require 'open-uri'
class Contact < ActiveRecord::Base
  include XmlHelper

  has_many :addresses
  has_many :phones
  def self.from_xml(contact_xml)
    doc = Hpricot(contact_xml)
    mappings = {
    :name=>"/contact/name",
    :xero_id=>"/contact/contactid",
    :status=>"/contact/contactstatus",
    :email_address=>"/contact/emailaddress",
    :updated_date_utc=>"/contact/updateddateutc"
    }
    attr = XmlHelper.populate_from_xml(doc,mappings)
    contact =  Contact.new(attr)
    (doc/"/contact/addresses/address").each do |address_xml|
      contact.addresses << Address.from_xml(address_xml.to_html)
    end
    (doc/"/contact/phones/phone").each do |phone_xml|
      contact.phones << Phone.from_xml(phone_xml.to_html)
    end
    return contact
  end
  def self.fetch(params)
    if(params[:xero_id]!=nil)
      response=Hpricot(open("#{params[:url]}/contact?contactID=#{params[:xero_id]}&apiKey=#{params[:api_key]}&xeroKey=#{params[:customer_key]}"))
      return Contact.from_xml(response.at("/response/contact").to_html)
    else
      contacts = Array.new
      response=Hpricot(open("#{params[:url]}/contacts?apiKey=#{params[:api_key]}&xeroKey=#{params[:customer_key]}"))
      (response/"/response/contacts/contact").each do |contact_xml|
        contacts << Contact.from_xml(contact_xml.to_html)
      end
      return contacts
    end
  end
end

There’s also a helper module that takes in a hash of property names mapped to the xml path for the property, and defaults to an empty string if the xml isn’t found

xml_helper.rb

module XmlHelper
  def self.populate_from_xml(xml,mappings)
    params={}

    mappings.each { |key,value|
      if(!xml.at(value).nil?)
        params[key]=xml.at(value).inner_text
      else
        params[key]=""
      end
    }
    return params
  end
end

And here’s the specs that should show how everything is used, and glues together (the xml is contained in fixture files to remove the need to go to the api each time)

address_spec.rb

require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe Address do
  before(:each) do
    @xml = File.read(RAILS_ROOT+"/spec/fixtures/xero_xml_objects/address.xml")
    @address = Address.from_xml(@xml)
  end

  it "should have correct type" do
    @address.addresstype.should=="POBOX"
  end
  it "should have correct address line 1" do
    @address.line1.should=="line 1"
  end
  it "should have correct address line 2" do
    @address.line2.should=="line 2"
  end
  it "should have correct address line 3" do
    @address.line3.should=="line 3 text"
  end
  it "should have correct address line 4" do
    @address.line4.should=="line 4"
  end
  it  "should have correct city" do
    @address.city.should=="Wellington"
  end
  it "should have correct region" do
    @address.region.should=="Region"
  end
  it "should have correct postal code" do
    @address.postal_code.should=="6011"
  end
  it "should have correct country" do
    @address.country.should=="COUNTRY/NZ"
  end

end

phone_spec.rb

require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe Phone do

   before(:each) do
    @xml = File.read(RAILS_ROOT+"/spec/fixtures/xero_xml_objects/phone.xml")
    @phone = Phone.from_xml(@xml)

  end

  it "should have correct type" do
    @phone.phone_type.should == "DDI"
  end
  it "should have correct number" do
    @phone.number.should =="1223 (2304)"
  end
  it "should have correct area code" do
    @phone.area_code.should == "(1023)"
  end
  it "should have correct country code" do
    @phone.country_code.should == "10"
  end
end

contact_spec.rb

require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe Contact do
  before(:each) do
    @xml = File.read(RAILS_ROOT+"/spec/fixtures/xero_xml_objects/contact.xml")
    @contact = Contact.from_xml(@xml)
  end

  it "should have the correct name" do
    @contact.name.should == "A. Dutchess"
  end
  it "should have the correct xero id" do
    @contact.xero_id.should == "bd2270c3-8706-4c11-9cfb-000b551c3f51"
  end
  it "should have the correct status" do
    @contact.status.should == "ACTIVE"
  end
  it "should have the correct email address" do
    @contact.email_address.should == "user@domain.com"
  end
  it "should have the correct updated date" do
    @contact.updated_date_utc.should == DateTime.parse("2008-07-12T01:44:26.747")
  end
  it "should have the correct number of addresses" do
    @contact.addresses.size.should == 1
  end
  it "should have the correct address" do
    @contact.addresses[0].line1.should == "P O Box 123"
  end
  it "should have the correct number of phone numbers" do
    @contact.phones.size.should == 4
  end
  it "should populate phone data correctly" do
    @contact.phones[0].number.should == "12345690"
  end
  describe "fetch, specifying an id" do
    before(:each) do
      @xero_contact_id="bd2270c3-8706-4c11-9cfb-000b551c3f51"
      @base_url="http://networkurl"
      @api_key="B5920D82BC0A40C89C16E8E2217875"
      @customer_key = "BF7BC56EE7D9457BB6F7685B00A955"
      @params={
      :xero_id => @xero_contact_id,
      :url => @base_url,
      :api_key=>@api_key,
      :customer_key=>@customer_key
      }
      xml=File.read(RAILS_ROOT+"/spec/fixtures/xero_api_responses/get_contact_response.xml")
      Contact.should_receive(:open).with("http://networkurl/contact?contactID=bd2270c3-8706-4c11-9cfb-000b551c3f51&apiKey=B5920D82BC0A40C89C16E8E2217875&xeroKey=BF7BC56EE7D9457BB6F7685B00A955").and_return(xml)
      @contact = Contact.fetch(@params)

    end
    it "should fetch the correct contact" do
      @contact.xero_id.should == "bd2270c3-8706-4c11-9cfb-000b551c3f51"
    end
    it "should populate addresses" do
      @contact.addresses.size.should == 1
    end
    it "should populate phone numbers" do
      @contact.phones.size.should == 4
    end

  end
  describe "fetch without specifying a contact id should fetch all" do
     before(:each) do
      @base_url="http://networkurl"
      @api_key="B5920D82BC0A40C89C16E8E2217875"
      @customer_key = "BF7BC56EE7D9457BB6F7685B00A955"
      @params={
      :url => @base_url,
      :api_key=>@api_key,
      :customer_key=>@customer_key
      }
      xml=File.read(RAILS_ROOT+"/spec/fixtures/xero_api_responses/get_contacts_response.xml")
      Contact.should_receive(:open).with("http://networkurl/contacts?apiKey=B5920D82BC0A40C89C16E8E2217875&xeroKey=BF7BC56EE7D9457BB6F7685B00A955").and_return(xml)
      @contacts = Contact.fetch(@params)
    end
    it "should fetch the correct number of contacts" do
      @contacts.size.should == 63
    end
  end
end

Wow that’s a lot of code for one post and may not make sense, as such i’m going to see if Xero will let me borrow some equipment and record a screen cast to show how i got to this point from the beginning.

Attached is the full rails project for this so you can peruse at your leisure.

If you’re interested in becoming a Xero Network partner and have a compelling solution, check out http://network.xero.com and apply to become part of our beta program.

Next I’ll go through modifying and adding a new contact to Xero

then the same treatment for invoices.

Also I want to try this out in other languages, might have to dust of my Java as it’s been a while, are there any other examples people want?

Source Files

Sphere: Related Content

Posted in: [||].

Viewing 3 Comments

 

Trackbacks

(Trackback URL)

close Reblog this comment
blog comments powered by Disqus