freshbooks.rb 3.0.18 → 3.0.25
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/History.txt +8 -0
- data/Manifest.txt +21 -1
- data/README.md +61 -0
- data/Rakefile +8 -13
- data/freshbooks.rb.gemspec +43 -0
- data/lib/freshbooks.rb +16 -2
- data/lib/freshbooks/address.rb +7 -0
- data/lib/freshbooks/api.rb +7 -0
- data/lib/freshbooks/autobill.rb +9 -0
- data/lib/freshbooks/base.rb +35 -9
- data/lib/freshbooks/budget.rb +7 -0
- data/lib/freshbooks/card.rb +9 -0
- data/lib/freshbooks/client.rb +3 -0
- data/lib/freshbooks/connection.rb +34 -25
- data/lib/freshbooks/contact.rb +9 -0
- data/lib/freshbooks/credits.rb +7 -0
- data/lib/freshbooks/estimate.rb +3 -3
- data/lib/freshbooks/expiration.rb +8 -0
- data/lib/freshbooks/gateway.rb +10 -0
- data/lib/freshbooks/gateway_transaction.rb +8 -0
- data/lib/freshbooks/invoice.rb +2 -2
- data/lib/freshbooks/language.rb +9 -0
- data/lib/freshbooks/line.rb +2 -0
- data/lib/freshbooks/links.rb +1 -1
- data/lib/freshbooks/payment.rb +2 -1
- data/lib/freshbooks/project.rb +4 -3
- data/lib/freshbooks/recurring.rb +5 -2
- data/lib/freshbooks/schema/definition.rb +2 -2
- data/lib/freshbooks/staff.rb +5 -0
- data/lib/freshbooks/system.rb +14 -0
- data/lib/freshbooks/tax.rb +12 -0
- data/lib/freshbooks/time_entry.rb +1 -0
- data/lib/freshbooks/xml_serializer.rb +5 -0
- data/lib/freshbooks/xml_serializer/serializers.rb +2 -2
- data/test/fixtures/freshbooks_credentials.yml +3 -0
- data/test/fixtures/recurring_create_response.xml +4 -0
- data/test/fixtures/recurring_get_response.xml +59 -0
- data/test/fixtures/recurring_list_response.xml +61 -0
- data/test/live_connection_test.rb +28 -0
- data/test/test_base.rb +59 -0
- data/test/test_connection.rb +39 -32
- data/test/test_helper.rb +20 -5
- data/test/test_invoice.rb +23 -37
- data/test/test_recurring.rb +98 -0
- metadata +67 -46
- data/README +0 -44
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
data.tar.gz: 298b0e8370286fe4a1ab625edf137ecf67cc7873
|
4
|
+
metadata.gz: 1cd14616e90a9f14b70982739d87b7e728b65bf2
|
5
|
+
SHA512:
|
6
|
+
data.tar.gz: 5d7c77d67d5bc1773ccc33580c1ce21fe115be11e4e51002218555df4f50be8d1919fa45ae7c11bf008887950d9dd66105c4c157829e3af43b5acbf12a9c7a36
|
7
|
+
metadata.gz: ada57b413deb19cfcf39756563d5c2a42681fd01345d2377156c6325bc9d8a19c6e7b1e9fdf7f0cd865144d4222a6675f1eda01c27b5aba58d6e52e3c653b662
|
data/.gemtest
ADDED
File without changes
|
data/History.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
== 2011-11-20
|
2
|
+
* Updated API to include currency code & language (In most API calls) [anlek]
|
3
|
+
* Updated time_entry to include "billed" attribute [anlek]
|
4
|
+
* Added new API calls: Gateway, Language, System, Tax [anlek]
|
5
|
+
* Added Staff.current [anlek]
|
6
|
+
* Added gateway_transaction to payment [anlek]
|
7
|
+
* Ensured that deprecated items are no longer included in the results (This MIGHT break some existing code) [anlek]
|
8
|
+
|
1
9
|
== 0.0.1 2009-02-25
|
2
10
|
|
3
11
|
* 3.0 major enhancement:
|
data/Manifest.txt
CHANGED
@@ -1,17 +1,29 @@
|
|
1
1
|
History.txt
|
2
2
|
LICENSE
|
3
3
|
Manifest.txt
|
4
|
-
README
|
4
|
+
README.md
|
5
5
|
Rakefile
|
6
|
+
freshbooks.rb.gemspec
|
6
7
|
lib/freshbooks.rb
|
8
|
+
lib/freshbooks/address.rb
|
9
|
+
lib/freshbooks/api.rb
|
10
|
+
lib/freshbooks/autobill.rb
|
7
11
|
lib/freshbooks/base.rb
|
12
|
+
lib/freshbooks/budget.rb
|
13
|
+
lib/freshbooks/card.rb
|
8
14
|
lib/freshbooks/category.rb
|
9
15
|
lib/freshbooks/client.rb
|
10
16
|
lib/freshbooks/connection.rb
|
17
|
+
lib/freshbooks/contact.rb
|
18
|
+
lib/freshbooks/credits.rb
|
11
19
|
lib/freshbooks/estimate.rb
|
12
20
|
lib/freshbooks/expense.rb
|
21
|
+
lib/freshbooks/expiration.rb
|
22
|
+
lib/freshbooks/gateway.rb
|
23
|
+
lib/freshbooks/gateway_transaction.rb
|
13
24
|
lib/freshbooks/invoice.rb
|
14
25
|
lib/freshbooks/item.rb
|
26
|
+
lib/freshbooks/language.rb
|
15
27
|
lib/freshbooks/line.rb
|
16
28
|
lib/freshbooks/links.rb
|
17
29
|
lib/freshbooks/list_proxy.rb
|
@@ -22,7 +34,9 @@ lib/freshbooks/response.rb
|
|
22
34
|
lib/freshbooks/schema/definition.rb
|
23
35
|
lib/freshbooks/schema/mixin.rb
|
24
36
|
lib/freshbooks/staff.rb
|
37
|
+
lib/freshbooks/system.rb
|
25
38
|
lib/freshbooks/task.rb
|
39
|
+
lib/freshbooks/tax.rb
|
26
40
|
lib/freshbooks/time_entry.rb
|
27
41
|
lib/freshbooks/xml_serializer.rb
|
28
42
|
lib/freshbooks/xml_serializer/serializers.rb
|
@@ -30,10 +44,15 @@ script/console
|
|
30
44
|
script/destroy
|
31
45
|
script/generate
|
32
46
|
test/fixtures/freshbooks_credentials.sample.yml
|
47
|
+
test/fixtures/freshbooks_credentials.yml
|
33
48
|
test/fixtures/invoice_create_response.xml
|
34
49
|
test/fixtures/invoice_get_response.xml
|
35
50
|
test/fixtures/invoice_list_response.xml
|
51
|
+
test/fixtures/recurring_create_response.xml
|
52
|
+
test/fixtures/recurring_get_response.xml
|
53
|
+
test/fixtures/recurring_list_response.xml
|
36
54
|
test/fixtures/success_response.xml
|
55
|
+
test/live_connection_test.rb
|
37
56
|
test/mock_connection.rb
|
38
57
|
test/schema/test_definition.rb
|
39
58
|
test/schema/test_mixin.rb
|
@@ -43,3 +62,4 @@ test/test_helper.rb
|
|
43
62
|
test/test_invoice.rb
|
44
63
|
test/test_list_proxy.rb
|
45
64
|
test/test_page.rb
|
65
|
+
test/test_recurring.rb
|
data/README.md
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
## Home
|
2
|
+
|
3
|
+
https://github.com/bcurren/freshbooks.rb
|
4
|
+
|
5
|
+
## Todo
|
6
|
+
|
7
|
+
Please send me a message if you are interested in contributing. Here is a quick overview of some things I'd like to add to the gem:
|
8
|
+
* Merge in forked versions of this gem. (Any idea what forks are worth while?)
|
9
|
+
* Provide a way to retrieve the error message with actions beside list are called.
|
10
|
+
* Make ListProxy optional on list actions. Some people have valid reason to deal with pagination.
|
11
|
+
* Add OAuth.
|
12
|
+
* Add WebHooks.
|
13
|
+
* More tests, including integration tests.
|
14
|
+
* Write some documentation and examples.
|
15
|
+
* Launch an official 3.0 (maybe call it 4.0) version of the gem.
|
16
|
+
* Post the official version to rubygems.
|
17
|
+
* Ensure rubygems aren't required for this gem.
|
18
|
+
|
19
|
+
## About
|
20
|
+
|
21
|
+
FreshBooks.rb is a Ruby interface to the FreshBooks API. It exposes easy-to-use classes and methods for interacting with your FreshBooks account.
|
22
|
+
|
23
|
+
***NOTE:** These examples are out of date and need to be updated. I will be writing documentation for all the updates soon and will be pushing the changes to rubyforge in the near future.*
|
24
|
+
|
25
|
+
## Examples
|
26
|
+
|
27
|
+
Initialization:
|
28
|
+
|
29
|
+
FreshBooks::Base.establish_connection('sample.freshbooks.com', 'mytoken')
|
30
|
+
|
31
|
+
Updating a client name:
|
32
|
+
|
33
|
+
clients = FreshBooks::Client.list
|
34
|
+
client = clients[0]
|
35
|
+
client.first_name = 'Suzy'
|
36
|
+
client.update
|
37
|
+
|
38
|
+
Updating an invoice:
|
39
|
+
|
40
|
+
invoice = FreshBooks::Invoice.get(4)
|
41
|
+
invoice.lines[0].quantity += 1
|
42
|
+
invoice.update
|
43
|
+
|
44
|
+
Creating a new item
|
45
|
+
|
46
|
+
item = FreshBooks::Item.new
|
47
|
+
item.name = 'A sample item'
|
48
|
+
item.create
|
49
|
+
|
50
|
+
## License
|
51
|
+
|
52
|
+
This work is distributed under the MIT License. Use/modify the code however you like.
|
53
|
+
|
54
|
+
## Download
|
55
|
+
|
56
|
+
`sudo gem install bcurren-freshbooks.rb`
|
57
|
+
|
58
|
+
## Credits
|
59
|
+
|
60
|
+
FreshBooks.rb is written and maintained by [Ben Curren](https://github.com/bcurren) at [Outright.com](http://outright.com/). [Ben Vinegar](http://benv.ca/) was the original developer and we have taken over maintenance of the gem from now on.
|
61
|
+
|
data/Rakefile
CHANGED
@@ -1,27 +1,22 @@
|
|
1
|
-
%w[rubygems rake rake/clean fileutils
|
1
|
+
%w[rubygems rake rake/clean fileutils rubigen hoe].each { |f| require f }
|
2
2
|
require File.dirname(__FILE__) + '/lib/freshbooks'
|
3
3
|
|
4
|
+
Hoe.plugin :newgem
|
5
|
+
Hoe.plugin :gemcutter
|
6
|
+
|
4
7
|
# Generate all the Rake tasks
|
5
8
|
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
6
9
|
$hoe = Hoe.spec('freshbooks.rb') do |p|
|
7
|
-
p.developer('Ben Curren', 'ben@
|
10
|
+
p.developer('Ben Curren', 'ben@thecurrens.com')
|
8
11
|
p.summary = ''
|
9
12
|
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
10
|
-
p.
|
11
|
-
p.
|
13
|
+
p.readme_file = "README.md"
|
14
|
+
p.rubyforge_name = p.name
|
15
|
+
p.extra_deps = [ ['activesupport', '>= 3.0.0'] ]
|
12
16
|
p.extra_dev_deps = [
|
13
17
|
['newgem', ">= #{::Newgem::VERSION}"],
|
14
18
|
['mocha', ">= 0.9.4"]
|
15
19
|
]
|
16
20
|
|
17
21
|
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
18
|
-
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
19
|
-
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
20
|
-
p.rsync_args = '-av --delete --ignore-errors'
|
21
22
|
end
|
22
|
-
|
23
|
-
require 'newgem/tasks' # load /tasks/*.rake
|
24
|
-
Dir['tasks/**/*.rake'].each { |t| load t }
|
25
|
-
|
26
|
-
# TODO - want other tests/tasks run by default? Add them to the list
|
27
|
-
# task :default => [:spec, :features]
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "freshbooks.rb"
|
5
|
+
s.version = "3.0.25"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Ben Curren"]
|
9
|
+
s.date = "2014-02-13"
|
10
|
+
s.description = ""
|
11
|
+
s.email = ["ben@thecurrens.com"]
|
12
|
+
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.md"]
|
13
|
+
s.files = ["History.txt", "LICENSE", "Manifest.txt", "README.md", "Rakefile", "freshbooks.rb.gemspec", "lib/freshbooks.rb", "lib/freshbooks/address.rb", "lib/freshbooks/api.rb", "lib/freshbooks/autobill.rb", "lib/freshbooks/base.rb", "lib/freshbooks/budget.rb", "lib/freshbooks/card.rb", "lib/freshbooks/category.rb", "lib/freshbooks/client.rb", "lib/freshbooks/connection.rb", "lib/freshbooks/contact.rb", "lib/freshbooks/credits.rb", "lib/freshbooks/estimate.rb", "lib/freshbooks/expense.rb", "lib/freshbooks/expiration.rb", "lib/freshbooks/gateway.rb", "lib/freshbooks/gateway_transaction.rb", "lib/freshbooks/invoice.rb", "lib/freshbooks/item.rb", "lib/freshbooks/language.rb", "lib/freshbooks/line.rb", "lib/freshbooks/links.rb", "lib/freshbooks/list_proxy.rb", "lib/freshbooks/payment.rb", "lib/freshbooks/project.rb", "lib/freshbooks/recurring.rb", "lib/freshbooks/response.rb", "lib/freshbooks/schema/definition.rb", "lib/freshbooks/schema/mixin.rb", "lib/freshbooks/staff.rb", "lib/freshbooks/system.rb", "lib/freshbooks/task.rb", "lib/freshbooks/tax.rb", "lib/freshbooks/time_entry.rb", "lib/freshbooks/xml_serializer.rb", "lib/freshbooks/xml_serializer/serializers.rb", "script/console", "script/destroy", "script/generate", "test/fixtures/freshbooks_credentials.sample.yml", "test/fixtures/freshbooks_credentials.yml", "test/fixtures/invoice_create_response.xml", "test/fixtures/invoice_get_response.xml", "test/fixtures/invoice_list_response.xml", "test/fixtures/recurring_create_response.xml", "test/fixtures/recurring_get_response.xml", "test/fixtures/recurring_list_response.xml", "test/fixtures/success_response.xml", "test/live_connection_test.rb", "test/mock_connection.rb", "test/schema/test_definition.rb", "test/schema/test_mixin.rb", "test/test_base.rb", "test/test_connection.rb", "test/test_helper.rb", "test/test_invoice.rb", "test/test_list_proxy.rb", "test/test_page.rb", "test/test_recurring.rb", ".gemtest"]
|
14
|
+
s.homepage = "https://github.com/bcurren/freshbooks.rb"
|
15
|
+
s.licenses = ["MIT"]
|
16
|
+
s.rdoc_options = ["--main", "README.md"]
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.rubyforge_project = "freshbooks.rb"
|
19
|
+
s.rubygems_version = "2.0.14"
|
20
|
+
s.summary = ""
|
21
|
+
s.test_files = ["test/schema/test_definition.rb", "test/schema/test_mixin.rb", "test/test_base.rb", "test/test_connection.rb", "test/test_helper.rb", "test/test_invoice.rb", "test/test_list_proxy.rb", "test/test_page.rb", "test/test_recurring.rb", "test/live_connection_test.rb"]
|
22
|
+
|
23
|
+
if s.respond_to? :specification_version then
|
24
|
+
s.specification_version = 4
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
27
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 3.0.0"])
|
28
|
+
s.add_development_dependency(%q<newgem>, [">= 1.5.3"])
|
29
|
+
s.add_development_dependency(%q<mocha>, [">= 0.9.4"])
|
30
|
+
s.add_development_dependency(%q<hoe>, ["~> 3.9"])
|
31
|
+
else
|
32
|
+
s.add_dependency(%q<activesupport>, [">= 3.0.0"])
|
33
|
+
s.add_dependency(%q<newgem>, [">= 1.5.3"])
|
34
|
+
s.add_dependency(%q<mocha>, [">= 0.9.4"])
|
35
|
+
s.add_dependency(%q<hoe>, ["~> 3.9"])
|
36
|
+
end
|
37
|
+
else
|
38
|
+
s.add_dependency(%q<activesupport>, [">= 3.0.0"])
|
39
|
+
s.add_dependency(%q<newgem>, [">= 1.5.3"])
|
40
|
+
s.add_dependency(%q<mocha>, [">= 0.9.4"])
|
41
|
+
s.add_dependency(%q<hoe>, ["~> 3.9"])
|
42
|
+
end
|
43
|
+
end
|
data/lib/freshbooks.rb
CHANGED
@@ -8,15 +8,27 @@ rescue LoadError
|
|
8
8
|
gem 'activesupport'
|
9
9
|
require 'active_support'
|
10
10
|
end
|
11
|
+
require 'active_support/all'
|
11
12
|
|
12
13
|
require 'freshbooks/base'
|
14
|
+
require 'freshbooks/address'
|
15
|
+
require 'freshbooks/api'
|
16
|
+
require 'freshbooks/autobill'
|
17
|
+
require 'freshbooks/budget'
|
18
|
+
require 'freshbooks/card'
|
13
19
|
require 'freshbooks/category'
|
14
20
|
require 'freshbooks/client'
|
15
21
|
require 'freshbooks/connection'
|
22
|
+
require 'freshbooks/credits'
|
16
23
|
require 'freshbooks/estimate'
|
17
24
|
require 'freshbooks/expense'
|
25
|
+
require 'freshbooks/expiration'
|
26
|
+
require 'freshbooks/gateway'
|
27
|
+
require 'freshbooks/gateway_transaction'
|
18
28
|
require 'freshbooks/invoice'
|
29
|
+
require 'freshbooks/contact'
|
19
30
|
require 'freshbooks/item'
|
31
|
+
require 'freshbooks/language'
|
20
32
|
require 'freshbooks/line'
|
21
33
|
require 'freshbooks/links'
|
22
34
|
require 'freshbooks/list_proxy'
|
@@ -25,7 +37,9 @@ require 'freshbooks/project'
|
|
25
37
|
require 'freshbooks/recurring'
|
26
38
|
require 'freshbooks/response'
|
27
39
|
require 'freshbooks/staff'
|
40
|
+
require 'freshbooks/system'
|
28
41
|
require 'freshbooks/task'
|
42
|
+
require 'freshbooks/tax'
|
29
43
|
require 'freshbooks/time_entry'
|
30
44
|
|
31
45
|
require 'net/https'
|
@@ -60,7 +74,7 @@ require 'logger'
|
|
60
74
|
#
|
61
75
|
#==============================================================================
|
62
76
|
module FreshBooks
|
63
|
-
VERSION = '3.0.
|
77
|
+
VERSION = '3.0.25' # Gem version
|
64
78
|
API_VERSION = '2.1' # FreshBooks API version
|
65
79
|
SERVICE_URL = "/api/#{API_VERSION}/xml-in"
|
66
80
|
|
@@ -72,7 +86,7 @@ module FreshBooks
|
|
72
86
|
class ApiAccessNotEnabledError < Error; end;
|
73
87
|
class InvalidAccountUrlError < Error; end;
|
74
88
|
class AccountDeactivatedError < Error; end;
|
75
|
-
|
89
|
+
|
76
90
|
class ParseError < StandardError
|
77
91
|
attr_accessor :original_error, :xml
|
78
92
|
|
data/lib/freshbooks/base.rb
CHANGED
@@ -4,6 +4,11 @@ require 'freshbooks/xml_serializer'
|
|
4
4
|
module FreshBooks
|
5
5
|
class Base
|
6
6
|
include FreshBooks::Schema::Mixin
|
7
|
+
attr_reader :error_msg
|
8
|
+
|
9
|
+
def initialize(args = {})
|
10
|
+
args.each_pair {|k, v| send("#{k}=", v)}
|
11
|
+
end
|
7
12
|
|
8
13
|
@@connection = nil
|
9
14
|
def self.connection
|
@@ -15,11 +20,12 @@ module FreshBooks
|
|
15
20
|
end
|
16
21
|
|
17
22
|
def self.new_from_xml(xml_root)
|
18
|
-
object = self.new
|
23
|
+
object = self.new()
|
19
24
|
|
20
25
|
self.schema_definition.members.each do |member_name, member_options|
|
21
|
-
node = xml_root.elements[member_name]
|
26
|
+
node = xml_root.elements[member_name.dup]
|
22
27
|
next if node.nil?
|
28
|
+
next if XmlSerializer.deprecated? node
|
23
29
|
|
24
30
|
value = FreshBooks::XmlSerializer.to_value(node, member_options[:type])
|
25
31
|
object.send("#{member_name}=", value)
|
@@ -28,7 +34,10 @@ module FreshBooks
|
|
28
34
|
return object
|
29
35
|
|
30
36
|
rescue => e
|
31
|
-
|
37
|
+
error = ParseError.new(e, xml_root.to_s)
|
38
|
+
error.set_backtrace(e.backtrace)
|
39
|
+
raise error
|
40
|
+
# raise ParseError.new(e, xml_root.to_s)
|
32
41
|
end
|
33
42
|
|
34
43
|
def to_xml(elem_name = nil)
|
@@ -81,10 +90,15 @@ module FreshBooks
|
|
81
90
|
end
|
82
91
|
end
|
83
92
|
|
84
|
-
def self.
|
85
|
-
|
93
|
+
def self.metaclass
|
94
|
+
class << self; self; end
|
86
95
|
end
|
87
96
|
|
97
|
+
def self.define_class_method(symbol, &block)
|
98
|
+
metaclass.instance_eval do
|
99
|
+
define_method symbol, &block
|
100
|
+
end
|
101
|
+
end
|
88
102
|
|
89
103
|
def self.actions(*operations)
|
90
104
|
operations.each do |operation|
|
@@ -145,7 +159,7 @@ module FreshBooks
|
|
145
159
|
|
146
160
|
def api_action(action_name)
|
147
161
|
response = FreshBooks::Base.connection.call_api(
|
148
|
-
"#{self.class.api_class_name}.#{action_name}",
|
162
|
+
"#{self.class.api_class_name}.#{action_name}",
|
149
163
|
"#{self.class.api_class_name}_id" => primary_key_value)
|
150
164
|
response.success?
|
151
165
|
end
|
@@ -154,15 +168,27 @@ module FreshBooks
|
|
154
168
|
response = FreshBooks::Base.connection.call_api(
|
155
169
|
"#{self.class.api_class_name}.#{action_name}",
|
156
170
|
self.class.api_class_name => self)
|
157
|
-
|
158
|
-
|
171
|
+
if response.success?
|
172
|
+
self.primary_key_value = response.elements[1].text.to_i
|
173
|
+
@error_msg = nil
|
174
|
+
true
|
175
|
+
else
|
176
|
+
@error_msg = response.error_msg
|
177
|
+
false
|
178
|
+
end
|
159
179
|
end
|
160
180
|
|
161
181
|
def api_update_action(action_name)
|
162
182
|
response = FreshBooks::Base.connection.call_api(
|
163
183
|
"#{self.class.api_class_name}.#{action_name}",
|
164
184
|
self.class.api_class_name => self)
|
165
|
-
response.success?
|
185
|
+
if response.success?
|
186
|
+
@error_msg = nil
|
187
|
+
true
|
188
|
+
else
|
189
|
+
@error_msg = response.error_msg
|
190
|
+
false
|
191
|
+
end
|
166
192
|
end
|
167
193
|
|
168
194
|
end
|
data/lib/freshbooks/client.rb
CHANGED
@@ -6,10 +6,13 @@ module FreshBooks
|
|
6
6
|
s.string :mobile, :fax, :notes, :p_street1, :p_street2, :p_city
|
7
7
|
s.string :p_state, :p_country, :p_code, :s_street1, :s_street2
|
8
8
|
s.string :s_city, :s_state, :s_country, :s_code
|
9
|
+
s.string :language, :currency_code
|
9
10
|
s.float :credit, :read_only => true
|
11
|
+
s.object :credits, :read_only => true
|
10
12
|
s.date_time :updated, :read_only => true
|
11
13
|
s.fixnum :client_id
|
12
14
|
s.object :links, :read_only => true
|
15
|
+
s.array :contacts
|
13
16
|
end
|
14
17
|
|
15
18
|
actions :list, :get, :create, :update, :delete
|
@@ -5,37 +5,46 @@ require 'logger'
|
|
5
5
|
module FreshBooks
|
6
6
|
class Connection
|
7
7
|
attr_reader :account_url, :auth_token, :utc_offset, :request_headers
|
8
|
-
|
8
|
+
|
9
9
|
@@logger = Logger.new(STDOUT)
|
10
10
|
def logger
|
11
11
|
@@logger
|
12
12
|
end
|
13
13
|
|
14
|
+
def logger=(value)
|
15
|
+
@@logger = value
|
16
|
+
end
|
17
|
+
|
14
18
|
def self.log_level=(level)
|
15
19
|
@@logger.level = level
|
16
20
|
end
|
17
21
|
self.log_level = Logger::WARN
|
18
|
-
|
22
|
+
|
19
23
|
def initialize(account_url, auth_token, request_headers = {}, options = {})
|
20
|
-
raise InvalidAccountUrlError.new unless account_url =~ /^[0-9a-zA-Z\-_]+\.freshbooks\.com$/
|
21
|
-
|
24
|
+
raise InvalidAccountUrlError.new("account_url is expected to be in the form www.example.com without any protocol string or trailing query parameters") unless account_url =~ /^[0-9a-zA-Z\-_]+\.(freshbooks|billingarm)\.com$/
|
25
|
+
|
22
26
|
@account_url = account_url
|
23
27
|
@auth_token = auth_token
|
24
28
|
@request_headers = request_headers
|
25
29
|
@utc_offset = options[:utc_offset] || -4
|
26
30
|
@start_session_count = 0
|
27
31
|
end
|
28
|
-
|
32
|
+
|
29
33
|
def call_api(method, elements = [])
|
30
34
|
request = create_request(method, elements)
|
31
35
|
result = post(request)
|
32
36
|
Response.new(result)
|
33
37
|
end
|
34
|
-
|
38
|
+
|
39
|
+
def direct_post(xml)
|
40
|
+
result = post(xml)
|
41
|
+
Response.new(result)
|
42
|
+
end
|
43
|
+
|
35
44
|
def start_session(&block)
|
36
45
|
@connection = obtain_connection if @start_session_count == 0
|
37
46
|
@start_session_count = @start_session_count + 1
|
38
|
-
|
47
|
+
|
39
48
|
begin
|
40
49
|
block.call(@connection)
|
41
50
|
ensure
|
@@ -43,45 +52,45 @@ module FreshBooks
|
|
43
52
|
close if @start_session_count == 0
|
44
53
|
end
|
45
54
|
end
|
46
|
-
|
55
|
+
|
47
56
|
protected
|
48
|
-
|
57
|
+
|
49
58
|
def create_request(method, elements = [])
|
50
59
|
doc = REXML::Document.new '<?xml version="1.0" encoding="UTF-8"?>'
|
51
60
|
request = doc.add_element('request')
|
52
61
|
request.attributes['method'] = method
|
53
|
-
|
62
|
+
|
54
63
|
elements.each do |element|
|
55
64
|
if element.kind_of?(Hash)
|
56
65
|
element = element.to_a
|
57
66
|
end
|
58
67
|
key = element.first
|
59
68
|
value = element.last
|
60
|
-
|
69
|
+
|
61
70
|
if value.kind_of?(Base)
|
62
71
|
request.add_element(REXML::Document.new(value.to_xml))
|
63
72
|
else
|
64
73
|
request.add_element(REXML::Element.new(key.to_s)).text = value.to_s
|
65
74
|
end
|
66
75
|
end
|
67
|
-
|
76
|
+
|
68
77
|
doc.to_s
|
69
78
|
end
|
70
|
-
|
79
|
+
|
71
80
|
def obtain_connection(force = false)
|
72
81
|
return @connection if @connection && !force
|
73
|
-
|
82
|
+
|
74
83
|
@connection = Net::HTTP.new(@account_url, 443)
|
75
84
|
@connection.use_ssl = true
|
76
85
|
@connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
77
86
|
@connection.start
|
78
87
|
end
|
79
|
-
|
88
|
+
|
80
89
|
def reconnect
|
81
90
|
close
|
82
91
|
obtain_connection(true)
|
83
92
|
end
|
84
|
-
|
93
|
+
|
85
94
|
def close
|
86
95
|
begin
|
87
96
|
@connection.finish if @connection
|
@@ -90,7 +99,7 @@ module FreshBooks
|
|
90
99
|
end
|
91
100
|
@connection = nil
|
92
101
|
end
|
93
|
-
|
102
|
+
|
94
103
|
def post(request_body)
|
95
104
|
result = nil
|
96
105
|
request = Net::HTTP::Post.new(FreshBooks::SERVICE_URL)
|
@@ -100,19 +109,19 @@ module FreshBooks
|
|
100
109
|
@request_headers.each_pair do |name, value|
|
101
110
|
request[name.to_s] = value
|
102
111
|
end
|
103
|
-
|
112
|
+
|
104
113
|
result = post_request(request)
|
105
|
-
|
114
|
+
|
106
115
|
if logger.debug?
|
107
116
|
logger.debug "Request:"
|
108
117
|
logger.debug request_body
|
109
118
|
logger.debug "Response:"
|
110
119
|
logger.debug result.body
|
111
120
|
end
|
112
|
-
|
121
|
+
|
113
122
|
check_for_api_error(result)
|
114
123
|
end
|
115
|
-
|
124
|
+
|
116
125
|
# For connections that take a long time, we catch EOFError's and reconnect seamlessly
|
117
126
|
def post_request(request)
|
118
127
|
response = nil
|
@@ -122,7 +131,7 @@ module FreshBooks
|
|
122
131
|
response = connection.request(request)
|
123
132
|
rescue EOFError => e
|
124
133
|
raise e if has_reconnected
|
125
|
-
|
134
|
+
|
126
135
|
has_reconnected = true
|
127
136
|
connection = reconnect
|
128
137
|
retry
|
@@ -130,10 +139,10 @@ module FreshBooks
|
|
130
139
|
end
|
131
140
|
response
|
132
141
|
end
|
133
|
-
|
142
|
+
|
134
143
|
def check_for_api_error(result)
|
135
144
|
return result.body if result.kind_of?(Net::HTTPSuccess)
|
136
|
-
|
145
|
+
|
137
146
|
case result
|
138
147
|
when Net::HTTPRedirection
|
139
148
|
if result["location"] =~ /loginSearch/
|
@@ -146,7 +155,7 @@ module FreshBooks
|
|
146
155
|
when Net::HTTPBadRequest
|
147
156
|
raise ApiAccessNotEnabledError.new("API not enabled.")
|
148
157
|
end
|
149
|
-
|
158
|
+
|
150
159
|
raise InternalError.new("Invalid HTTP code: #{result.class}")
|
151
160
|
end
|
152
161
|
end
|