diff --git a/lib/freeagent/base.rb b/lib/freeagent/base.rb deleted file mode 100644 index 56718fc..0000000 --- a/lib/freeagent/base.rb +++ /dev/null @@ -1,46 +0,0 @@ -module Freeagent - - class Base - - def initialize(attributes={}) - attributes.each do |key, value| - raise "no attr_accessor set for #{key} on #{self.class}" if !respond_to?("#{key}=") - self.send("#{key}=", value) - end - end - - def self.get(path) - @@resp = APICache.get(path) do - http = Net::HTTP.new(Freeagent.domain, 443) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - http.start do |http| - request = Net::HTTP::Get.new(path, {'Content-Type' => 'application/xml', 'Accept' => 'application/xml'}) - request.basic_auth(Freeagent.username, Freeagent.password) - response = http.request(request) - case response - when Net::HTTPSuccess - @@resp = response.body - else - response.error! - raise APICache::InvalidResponse - end - end - end - end - - def self.parse(path, options) - response = [] - Nokogiri::XML(@@resp).xpath(path).each do |ts| - res = {} - options.each do |key| - res[key.underscore.to_sym] = ts.xpath(key).text - end - response << self.new(res) - end - return response - end - - end - -end \ No newline at end of file diff --git a/lib/freeagent/contact.rb b/lib/freeagent/contact.rb deleted file mode 100644 index ce89c39..0000000 --- a/lib/freeagent/contact.rb +++ /dev/null @@ -1,20 +0,0 @@ -module Freeagent - - class Contact < Base - @elements = ['id', 'organisation-name', 'first-name', 'last-name', 'address1', 'address2', 'address3', 'town', 'region', 'country', 'postcode', 'phone-number', 'email', 'contact-name-on-invoices', 'sales-tax-registration_number', 'uses-contact-invoice-sequence'] - @elements.each {|t| attr_accessor t.underscore.to_sym} - - def self.find_all - get '/contacts' - contacts = parse('contacts/contact', @elements) - return contacts - end - - def self.find(contact_id) - get '/contacts/'+contact_id - contacts = parse('contact', @elements) - return contacts[0] - end - end - -end \ No newline at end of file diff --git a/lib/freeagent/invoice.rb b/lib/freeagent/invoice.rb deleted file mode 100644 index 26c2801..0000000 --- a/lib/freeagent/invoice.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Freeagent - - class Invoice < Base - @elements = ['id', 'contact-id', 'project-id', 'dated-on', 'due-on', 'reference', 'net-value', 'sales-tax-value', 'status', 'comments', 'discount-percent', 'omit-header', 'payment-terms', 'written-off-date', 'invoice-items'] - @elements.each {|t| attr_accessor t.underscore.to_sym} - - def self.find_all(project_id = false) - if project_id - get '/projects/'+project_id+'/invoices' - else - get '/invoices' - end - invoices = parse('invoices/invoice', @elements) - return invoices.reverse - end - - def self.find(invoice_id) - get '/invoices/'+invoice_id - invoices = parse('invoice', @elements) - invoices.each do |i| - i.invoice_items = InvoiceItem.find_all(invoice_id) - end - return invoices[0] - end - end - -end \ No newline at end of file diff --git a/lib/freeagent/invoice_item.rb b/lib/freeagent/invoice_item.rb deleted file mode 100644 index be65220..0000000 --- a/lib/freeagent/invoice_item.rb +++ /dev/null @@ -1,13 +0,0 @@ -module Freeagent - - class InvoiceItem < Invoice - @elements = ['id', 'invoice-id', 'project-id', 'item-type', 'price', 'quantity', 'description', 'sales-tax-rate'] - @elements.each {|t| attr_accessor t.underscore.to_sym} - - def self.find_all(invoice_id) - items = parse('invoice/invoice-items/invoice-item', @elements) - return items - end - end - -end \ No newline at end of file diff --git a/lib/freeagent/project.rb b/lib/freeagent/project.rb deleted file mode 100644 index 4b70aac..0000000 --- a/lib/freeagent/project.rb +++ /dev/null @@ -1,20 +0,0 @@ -module Freeagent - - class Project < Base - @elements = ['id', 'contact-id', 'name', 'billing-basis', 'budget', 'budget-units', 'invoicing-reference', 'is-ir35', 'normal-billing-rate', 'payment-terms-in-days', 'starts-on', 'ends-on', 'status', 'uses-project-invoice-sequence'] - @elements.each {|t| attr_accessor t.underscore.to_sym} - - def self.find_all - get '/projects' - projects = parse('projects/project', @elements) - return projects - end - - def self.find(project_id) - get '/projects/'+project_id - projects = parse('project', @elements) - return projects[0] - end - end - -end \ No newline at end of file diff --git a/lib/freeagent/task.rb b/lib/freeagent/task.rb deleted file mode 100644 index 2cda2fd..0000000 --- a/lib/freeagent/task.rb +++ /dev/null @@ -1,14 +0,0 @@ -module Freeagent - - class Task < Base - @elements = ['id', 'project-id', 'name'] - @elements.each {|t| attr_accessor t.underscore.to_sym} - - def self.find(project_id, task_id) - get '/projects/'+project_id+'/tasks/'+task_id - tasks = parse('task', @elements) - return tasks[0] - end - end - -end \ No newline at end of file diff --git a/lib/freeagent/timeslip.rb b/lib/freeagent/timeslip.rb deleted file mode 100644 index d3e196c..0000000 --- a/lib/freeagent/timeslip.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Freeagent - - class Timeslip < Base - @elements = ['id', 'dated-on', 'project-id', 'task-id', 'task', 'user-id', 'hours', 'comment'] - @elements.each {|t| attr_accessor t.underscore.to_sym} - - def self.find_all(project_id = false) - if project_id - get '/projects/'+project_id+'/timeslips' - else - get '/timeslips' - end - timeslips = parse('timeslips/timeslip', @elements) - timeslips.each do |t| - t.task = Task.find(project_id, t.task_id) - end - return timeslips.reverse - end - - def self.find(timeslip_id) - get '/timeslips'+timeslip_id - timeslips = parse('timeslip', @elements) - return timeslips[0] - end - end - -end \ No newline at end of file diff --git a/lib/freeagent_api.rb b/lib/freeagent_api.rb index b36536f..50aae0d 100644 --- a/lib/freeagent_api.rb +++ b/lib/freeagent_api.rb @@ -1,29 +1,190 @@ require 'rubygems' -require 'nokogiri' -require 'net/https' -require 'api_cache' -require 'activesupport' require 'activeresource' -# Require Freeagent library files -Dir[File.join(File.dirname(__FILE__), "freeagent/*.rb")].each { |f| require f } - module Freeagent class << self attr_accessor :domain, :username, :password + end + + class Error < StandardError; end - def self.domain=(domain) - @domain = domain + class Base < ActiveResource::Base + def self.authenticate + self.site = "https://#{Freeagent.domain}" + self.user = Freeagent.username + self.password = Freeagent.password end + end + + # Find contacts + # + # Contact.find :all # find all contacts + # Contact.find contact_id # find specific contact by ID + # + # Create contact + # + # Required attributes + # :first_name + # :last_name + # + # contact = Contact.new :first_name => 'Joe', :last_name => 'Bloggs' + # contact.save + # + # Update contact + # + # contact = Contact.find contact_id + # contact.first_name = 'Joe' + # contact.last_name = 'Bloggs' + # contact.save + # + # Delete contact + # + # Contact.delete contact_id + # contact.destroy + # + + class Contact < Base + end + + # Find projects + # + # Project.find :all # find all projects + # Project.find project_id # find specific project by ID + # + # Create project + # + # Required attributes + # :contact_id + # :name + # :payment_term_in_days + # :billing_basis # must be 1, 7, 7.5, or 8 + # :budget_units # must be Hours, Days, or Monetary + # :status # must be Active or Completed + # + # Project = Project.new params + # contact.save + # + # Update project + # + # project = Project.find project_id + # project.name = 'Website redesign and build' + # project.save + # + # Delete project + # + # Project.delete project_id + # project.destroy + # + + class Project < Base - def self.username=(username) - @username = username + def invoices + Invoice.find :all, :params => {:project_id => id} + end + + def tasks + Task.find :all, :params => {:project_id => id} + end + + def timeslips + Timeslip.find :all, :params => {:project_id => id} end - def self.password=(password) - @password = password + end + + # Find invoices + # + # Invoice.find :all + # Invoice.find :all, :params => {:project_id => project_id} + # Invoice.find task_id + # + #TODO Create invoice + # + #TODO Update invoice + # + #TODO Delete project + # + ##TODO add Change status methods + # /invoices/invoice_id/mark_as_draft + # /invoices/invoice_id/mark_as_sent + # /invoices/invoice_id/mark_as_cancelled + + + class Invoice < Base + + def self.find(*args) + opts = args.slice!(1) || {} + self.prefix = "/projects/#{opts[:params][:project_id]}/" if opts[:params] && opts[:params][:project_id] + super + end + + end + + # Find invoice items + # + # InvoiceItem.find :all, :params => {:invoice_id => invoice_id} + # InvoiceItem.find invoice_item_id, :params => {:invoice_id => invoice_id} + # + #TODO Create invoice item + # + + class InvoiceItem < Base + self.prefix = '/invoices/:invoice_id/' + end + + # Find tasks + # + # Task.find :all + # Task.find :all, :params => {:project_id => project_id} + # Task.find task_id + # + #TODO Create task + # + #TODO Update task + # + #TODO Delete project + # + + class Task < Base + + self.prefix = '/projects/:project_id/' + +# def self.find(*args) +# opts = args.slice!(1) || {} +# self.prefix = "/projects/#{opts[:params][:project_id]}/" if opts[:params] && opts[:params][:project_id] +# super +# end + + end + + # Find timeslips + # + # Timeslip.find :all, :params => {:view => '2009-01-01_2009-10-01'} + # Timeslip.find :all, :params => {:project_id => project_id} + # Timeslip.find :timeslip_id + + + class Timeslip < Base + + def self.find(*args) + opts = args.slice!(1) || {} + self.prefix = "/projects/#{opts[:params][:project_id]}/" if opts[:params] && opts[:params][:project_id] + super end + end +# class ActiveResource::Connection +# def http +# http = Net::HTTP.new(@site.host, @site.port) +# http.use_ssl = @site.is_a?(URI::HTTPS) +# http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl +# http.read_timeout = @timeout if @timeout +# #Here's the addition that allows you to see the output +# http.set_debug_output $stderr +# return http +# end +# end + end \ No newline at end of file diff --git a/test/freeagent_api_test.rb b/test/freeagent_api_test.rb index 8e741bf..8934d77 100644 --- a/test/freeagent_api_test.rb +++ b/test/freeagent_api_test.rb @@ -1,31 +1,12 @@ require 'test_helper' -require 'yaml' class FreeagentApiTest < Test::Unit::TestCase - include Freeagent - - context "Before we do anything, we" do - setup do - @config ||= YAML.load_file 'test/user_credentials.yaml' - end - should "set the domain" do - assert Freeagent.domain = @config['domain'] - end - should "set the username" do - assert Freeagent.username = @config['username'] - end - should "set the password" do - assert Freeagent.password = @config['password'] - end - end - - context "Many projects" do - setup do - @projects = Project.find_all - end - should "be in an array" do - assert @projects.is_a? Array + context "Authentication details" do + should "match what was set" do + assert_equal Base.site, URI.parse('https://testuser.freeagentcentral.com') + assert_equal Base.user, 'testuser' + assert_equal Base.password, 'testpass' end end diff --git a/test/project_test.rb b/test/project_test.rb new file mode 100644 index 0000000..1c73b00 --- /dev/null +++ b/test/project_test.rb @@ -0,0 +1,37 @@ +require 'test_helper' + +class ProjectTest < Test::Unit::TestCase + + def setup + fake_it_all + @projects = Project.find :all + @project = Project.find 17820 + end + + context "Project class" do + should "have correct collection path" do + assert_equal Project.collection_path, '/projects.xml' + end + should "have correct element path" do + assert_equal Project.element_path(:first), '/projects/first.xml' + assert_equal Project.element_path(1), '/projects/1.xml' + end + end + + context "Projects" do + should "return an array" do + assert @projects.is_a? Array + end + should "return projects" do + assert_equal 5, @projects.size + assert @projects.first.is_a? Project + end + end + + context "Project" do + should "return a Project" do + assert @project.is_a? Project + end + end + +end \ No newline at end of file diff --git a/test/stubs/projects/get b/test/stubs/projects/get new file mode 100644 index 0000000..6e72675 --- /dev/null +++ b/test/stubs/projects/get @@ -0,0 +1,92 @@ +HTTP/1.1 200 OK +Status: 200 +Content-Type: application/xml; charset=utf-8 + + + + + 7.5 + 0 + Hours + 27314 + + 2009-08-26T17:11:22Z + + 17820 + false + Ecommerce web development + 200.0 + + Active + 2009-08-26T17:11:22Z + false + + + 1.0 + 8 + Hours + 27317 + + 2009-10-09T09:53:29Z + 2009-12-01T00:00:00Z + 20601 + false + Website multi-lingual support + 35.0 + 2009-11-30T00:00:00Z + Active + 2009-10-09T10:53:21Z + false + + + 1.0 + 0 + Hours + 27318 + + 2009-10-20T08:47:31Z + + 21356 + false + Ongoing design and maintenance work + 30.0 + + Active + 2009-10-20T08:47:31Z + false + + + 1.0 + 61 + Hours + 30530 + + 2009-10-09T10:00:52Z + 2009-12-01T00:00:00Z + 20602 + false + Website redesign and redevelopment + 35.0 + 2009-10-26T00:00:00Z + Active + 2009-10-09T10:39:08Z + false + + + 1.0 + 34 + Hours + 30530 + + 2009-10-09T10:15:26Z + 2010-01-12T00:00:00Z + 20603 + false + Single page product mini sites + 35.0 + 2009-12-08T00:00:00Z + Active + 2009-10-09T10:59:29Z + false + + \ No newline at end of file diff --git a/test/stubs/projects/get_17820 b/test/stubs/projects/get_17820 new file mode 100644 index 0000000..ff7cc27 --- /dev/null +++ b/test/stubs/projects/get_17820 @@ -0,0 +1,22 @@ +HTTP/1.1 200 OK +Status: 200 +Content-Type: application/xml; charset=utf-8 + + + + 7.5 + 0 + Hours + 27314 + + 2009-08-26T17:11:22Z + + 17820 + false + Ecommerce web development + 200.0 + + Active + 2009-08-26T17:11:22Z + false + \ No newline at end of file diff --git a/test/task_test.rb b/test/task_test.rb new file mode 100644 index 0000000..a68fa79 --- /dev/null +++ b/test/task_test.rb @@ -0,0 +1,16 @@ +require 'test_helper' + + +class TaskTest < Test::Unit::TestCase + + context "Task class" do + should "have correct collection path" do + assert Task.collection_path(:project_id => 1000) === '/projects/1000/tasks.xml' + end + should "have correct element path" do + assert Task.element_path(:first, :project_id => 1000) === '/projects/1000/tasks/first.xml' + assert Task.element_path(1000, :project_id => 1000) === '/projects/1000/tasks/1000.xml' + end + end + +end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index c31b765..4cf045a 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,10 +1,37 @@ require 'rubygems' require 'test/unit' require 'shoulda' +require 'fakeweb' +require 'pp' $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) $LOAD_PATH.unshift(File.dirname(__FILE__)) + require 'freeagent_api' +include Freeagent + +Freeagent.domain = 'testuser.freeagentcentral.com' +Freeagent.username = 'testuser' +Freeagent.password = 'testpass' + +Base.authenticate + +FakeWeb.allow_net_connect = false + +def stub_file(path) + File.join(File.dirname(__FILE__), 'stubs', path) +end + +def fake_it_all + FakeWeb.clean_registry + fakes = { + "/projects.xml" => File.join('projects', 'get'), + "/projects/17820.xml" => File.join('projects', 'get_17820') + } + fakes.each do |path, stub| + FakeWeb.register_uri(:get, 'https://testuser:testpass@testuser.freeagentcentral.com'+path, :response => stub_file(stub)) + end +end class Test::Unit::TestCase end