freshbooks.rb 3.0.18 → 3.0.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|