sage_one 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +14 -0
- data/.travis.yml +11 -0
- data/.yardopts +4 -0
- data/Gemfile +8 -0
- data/LICENSE +19 -0
- data/README.md +141 -0
- data/Rakefile +4 -0
- data/bin/autospec +16 -0
- data/bin/htmldiff +16 -0
- data/bin/ldiff +16 -0
- data/bin/rake +16 -0
- data/bin/rspec +16 -0
- data/bin/yard +16 -0
- data/bin/yardoc +16 -0
- data/bin/yri +16 -0
- data/lib/faraday/request/oauth2.rb +25 -0
- data/lib/faraday/response/convert_sdata_to_headers.rb +70 -0
- data/lib/faraday/response/raise_sage_one_exception.rb +42 -0
- data/lib/sage_one/client/contacts.rb +14 -0
- data/lib/sage_one/client/sales_invoices.rb +77 -0
- data/lib/sage_one/client.rb +33 -0
- data/lib/sage_one/configuration.rb +81 -0
- data/lib/sage_one/connection.rb +44 -0
- data/lib/sage_one/error.rb +39 -0
- data/lib/sage_one/oauth.rb +49 -0
- data/lib/sage_one/request.rb +72 -0
- data/lib/sage_one/version.rb +3 -0
- data/lib/sage_one.rb +31 -0
- data/sage_one.gemspec +32 -0
- data/spec/faraday/request/oauth2_spec.rb +44 -0
- data/spec/faraday/response/convert_sdata_to_headers_spec.rb +113 -0
- data/spec/faraday/response/raise_sage_one_exception_spec.rb +45 -0
- data/spec/fixtures/contact.json +28 -0
- data/spec/fixtures/invalid_sales_invoice.json +28 -0
- data/spec/fixtures/oauth/invalid_client.json +1 -0
- data/spec/fixtures/oauth/invalid_grant.json +1 -0
- data/spec/fixtures/oauth/oauth_token.json +4 -0
- data/spec/fixtures/sales_invoice.json +43 -0
- data/spec/fixtures/sales_invoices.json +90 -0
- data/spec/sage_one/client/contacts_spec.rb +19 -0
- data/spec/sage_one/client/sales_invoices_spec.rb +53 -0
- data/spec/sage_one/client_spec.rb +41 -0
- data/spec/sage_one/configuration_spec.rb +88 -0
- data/spec/sage_one/connection_spec.rb +36 -0
- data/spec/sage_one/oauth_spec.rb +44 -0
- data/spec/sage_one/request_spec.rb +113 -0
- data/spec/sage_one/version_spec.rb +7 -0
- data/spec/sage_one_spec.rb +38 -0
- data/spec/spec_helper.rb +76 -0
- metadata +301 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
{ "$totalResults":2,
|
2
|
+
"$startIndex":0,
|
3
|
+
"$itemsPerPage":20,
|
4
|
+
|
5
|
+
"$resources":[
|
6
|
+
{ "id":954380,
|
7
|
+
"invoice_number":"SI-4",
|
8
|
+
"status":{"id":1, "$key":1},
|
9
|
+
"due_date":"19/04/2013",
|
10
|
+
"date":"20/03/2013",
|
11
|
+
"void_reason":null,
|
12
|
+
"outstanding_amount":"17.0",
|
13
|
+
"total_net_amount":"14.17",
|
14
|
+
"total_tax_amount":"2.83",
|
15
|
+
"tax_scheme_period_id":30394,
|
16
|
+
"carriage":"0.0",
|
17
|
+
"carriage_tax_code":{"id":1, "$key":1},
|
18
|
+
"carriage_tax_rate_percentage":"20.0",
|
19
|
+
"contact":{"id":568085, "$key":568085},
|
20
|
+
"contact_name":"Luke corp (Luke Brown)",
|
21
|
+
"main_address":"Killer Bees",
|
22
|
+
"delivery_address":"",
|
23
|
+
"delivery_address_same_as_main":false,
|
24
|
+
"reference":"",
|
25
|
+
"notes":"future date. did email it.",
|
26
|
+
"terms_and_conditions":"",
|
27
|
+
"lock_version":0,
|
28
|
+
"line_items":[{
|
29
|
+
"id":1570294,
|
30
|
+
"description":"Some fish - herring",
|
31
|
+
"quantity":"1.0",
|
32
|
+
"unit_price":"17.0",
|
33
|
+
"net_amount":"14.17",
|
34
|
+
"tax_amount":"2.83",
|
35
|
+
"tax_code":{"id":1, "$key":1},
|
36
|
+
"tax_rate_percentage":"20.0",
|
37
|
+
"unit_price_includes_tax":true,
|
38
|
+
"ledger_account":{"id":1898509,
|
39
|
+
"$key":1898509},
|
40
|
+
"product_code":"HERRING",
|
41
|
+
"product":{"id":68656, "$key":68656},
|
42
|
+
"service":{"$key":null},
|
43
|
+
"lock_version":0,
|
44
|
+
"$key":1570294
|
45
|
+
}
|
46
|
+
],
|
47
|
+
"$key":954380},
|
48
|
+
{"id":954368,
|
49
|
+
"invoice_number":"SI-3",
|
50
|
+
"status":{"id":1, "$key":1},
|
51
|
+
"due_date":"07/12/2012",
|
52
|
+
"date":"07/11/2012",
|
53
|
+
"void_reason":null,
|
54
|
+
"outstanding_amount":"19.4",
|
55
|
+
"total_net_amount":"16.17",
|
56
|
+
"total_tax_amount":"3.23",
|
57
|
+
"tax_scheme_period_id":30394,
|
58
|
+
"carriage":"2.0",
|
59
|
+
"carriage_tax_code":{"id":1, "$key":1},
|
60
|
+
"carriage_tax_rate_percentage":"20.0",
|
61
|
+
"contact":{"id":568085, "$key":568085},
|
62
|
+
"contact_name":"Luke corp (Luke Brown)",
|
63
|
+
"main_address":"here man.",
|
64
|
+
"delivery_address":"",
|
65
|
+
"delivery_address_same_as_main":false,
|
66
|
+
"reference":"",
|
67
|
+
"notes":"this one neither emailed nor printed.",
|
68
|
+
"terms_and_conditions":"",
|
69
|
+
"lock_version":0,
|
70
|
+
"line_items":[{
|
71
|
+
"id":1570277,
|
72
|
+
"description":"Some fish - herring",
|
73
|
+
"quantity":"1.0",
|
74
|
+
"unit_price":"17.0",
|
75
|
+
"net_amount":"14.17",
|
76
|
+
"tax_amount":"2.83",
|
77
|
+
"tax_code":{"id":1, "$key":1},
|
78
|
+
"tax_rate_percentage":"20.0",
|
79
|
+
"unit_price_includes_tax":true,
|
80
|
+
"ledger_account":{"id":1898509, "$key":1898509},
|
81
|
+
"product_code":"HERRING",
|
82
|
+
"product":{"id":68656, "$key":68656},
|
83
|
+
"service":{"$key":null},
|
84
|
+
"lock_version":0,
|
85
|
+
"$key":1570277
|
86
|
+
}
|
87
|
+
],
|
88
|
+
"$key":954368}
|
89
|
+
]
|
90
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SageOne::Client::Contacts do
|
4
|
+
|
5
|
+
let(:client) { SageOne.new }
|
6
|
+
|
7
|
+
describe '.contact' do
|
8
|
+
context 'when the contact exists' do
|
9
|
+
it 'returns a Hashie::Mash of the contact' do
|
10
|
+
stub_get('contacts/1').to_return(body: fixture('contact.json'))
|
11
|
+
contact = client.contact(1)
|
12
|
+
expect(contact).to be_a(Hashie::Mash)
|
13
|
+
expect(contact.name).to eq('Luke Brown')
|
14
|
+
expect(contact.email).to eq('luke.brown@example.com')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SageOne::Client::SalesInvoices do
|
4
|
+
|
5
|
+
let(:client) { SageOne.new }
|
6
|
+
|
7
|
+
describe 'sales_invoices' do
|
8
|
+
it 'returns an array of Hashie::Mashes, representing the available invoices' do
|
9
|
+
stub_get('sales_invoices').to_return(body: fixture('sales_invoices.json'))
|
10
|
+
inv = client.sales_invoices.first
|
11
|
+
expect(inv.outstanding_amount).to eq '17.0'
|
12
|
+
expect(inv.outstanding_amount).to eq '17.0'
|
13
|
+
expect(inv.notes).to eq 'future date. did email it.'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'create_sales_invoice' do
|
18
|
+
it 'posts to the correct endpoint and returns the new invoice' do
|
19
|
+
stub_post('sales_invoices').to_return(body: fixture('sales_invoice.json'))
|
20
|
+
invoice = client.create_sales_invoice(foo: 'bar')
|
21
|
+
a_post('sales_invoices').with(body: { sales_invoice: { foo: 'bar' } }).should have_been_made.once
|
22
|
+
expect(invoice.invoice_number).to eq 'SI-4'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'sales_invoice' do
|
27
|
+
it 'gets the requested sales invoice' do
|
28
|
+
stub_get('sales_invoices/333').to_return(body: fixture('sales_invoice.json'))
|
29
|
+
invoice = client.sales_invoice(333)
|
30
|
+
a_get('sales_invoices/333').should have_been_made.once
|
31
|
+
expect(invoice.invoice_number).to eq 'SI-4'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'update_sales_invoice' do
|
36
|
+
it "makes a put and returns the new invoice" do
|
37
|
+
stub_put('sales_invoices/333').to_return(body: fixture('sales_invoice.json'))
|
38
|
+
invoice = client.update_sales_invoice(333, foo: 'bar')
|
39
|
+
a_put('sales_invoices/333').with(body: { sales_invoice: { foo: 'bar' } }).should have_been_made.once
|
40
|
+
expect(invoice.invoice_number).to eq 'SI-4'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe 'delete_sales_invoice!' do
|
45
|
+
it "makes a delete and returns the deleted invoice" do
|
46
|
+
stub_delete('sales_invoices/444').to_return(body: fixture('sales_invoice.json'))
|
47
|
+
invoice = client.delete_sales_invoice!(444)
|
48
|
+
a_delete('sales_invoices/444').should have_been_made.once
|
49
|
+
expect(invoice.invoice_number).to eq 'SI-4'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SageOne::Client do
|
4
|
+
|
5
|
+
it 'creates accessors for all the keys in the SageOne::Configuration::VALID_OPTIONS_KEYS constant' do
|
6
|
+
client = SageOne::Client.new
|
7
|
+
SageOne::Configuration::VALID_OPTIONS_KEYS.each_with_index do |method, i|
|
8
|
+
value = "Setting attribute #{i}"
|
9
|
+
client.send("#{method}=", value)
|
10
|
+
expect(client.send(method)).to eq(value)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'initialize' do
|
15
|
+
it 'copies the SageOne.options to the new client' do
|
16
|
+
client = SageOne::Client.new
|
17
|
+
SageOne::Configuration::VALID_OPTIONS_KEYS.each do |method|
|
18
|
+
expect(client.send(method)).to eq(SageOne.options[method])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
it 'allows you to override any config options' do
|
22
|
+
better_options = {
|
23
|
+
adapter: 'BestAdaptor',
|
24
|
+
faraday_config_block: 'blocky block',
|
25
|
+
api_endpoint: 'https://www.example.com/',
|
26
|
+
proxy: 'proxy.example.com',
|
27
|
+
access_token: 'let_me_in',
|
28
|
+
client_id: 'client123',
|
29
|
+
client_secret: 'secret',
|
30
|
+
user_agent: 'Gem Awesome',
|
31
|
+
request_host: 'example.com',
|
32
|
+
auto_traversal: true,
|
33
|
+
raw_response: true
|
34
|
+
}
|
35
|
+
client = SageOne::Client.new(better_options)
|
36
|
+
SageOne::Configuration::VALID_OPTIONS_KEYS.each do |method|
|
37
|
+
expect(client.send(method)).to eq(better_options[method])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SageOne::Configuration do
|
4
|
+
|
5
|
+
subject do
|
6
|
+
module TestConfigModule
|
7
|
+
extend SageOne::Configuration
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'has some default constants setup' do
|
12
|
+
expect(SageOne::Configuration::DEFAULT_ADAPTER).to eq(Faraday.default_adapter)
|
13
|
+
expect(SageOne::Configuration::DEFAULT_API_ENDPOINT).to eq('https://app.sageone.com/api/v1/')
|
14
|
+
expect(SageOne::Configuration::DEFAULT_USER_AGENT).to eq("SageOne Ruby Gem #{SageOne::VERSION}")
|
15
|
+
expect(SageOne::Configuration::DEFAULT_AUTO_TRAVERSAL).to be_false
|
16
|
+
expect(SageOne::Configuration::DEFAULT_RAW_RESPONSE).to be_false
|
17
|
+
expect(SageOne::Configuration::VALID_OPTIONS_KEYS).to be_a(Array)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'attr_accessors' do
|
21
|
+
it 'creates accessors for all the keys in the VALID_OPTIONS_KEYS constant' do
|
22
|
+
SageOne::Configuration::VALID_OPTIONS_KEYS.reject { |k| [:api_endpoint].include?(k) }.each_with_index do |method, i|
|
23
|
+
value = "Setting attribute #{i}"
|
24
|
+
subject.send("#{method}=", value)
|
25
|
+
expect(subject.send(method)).to eq(value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
it 'uses an overridden method for setting the api_endpoint which ensures a trailing forward slash is present' do
|
29
|
+
subject.api_endpoint = 'http://www.example.com'
|
30
|
+
expect(subject.api_endpoint).to eq('http://www.example.com/')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "faraday_config" do
|
35
|
+
it 'assigns the given block to faraday_config_block' do
|
36
|
+
p = lambda { 'hi' }
|
37
|
+
subject.faraday_config(&p)
|
38
|
+
expect(subject.faraday_config_block.call).to eq('hi')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'configure' do
|
43
|
+
it 'yields the given block passing in self - allowing you to config attributes' do
|
44
|
+
value = "my new client id"
|
45
|
+
|
46
|
+
subject.configure do |config|
|
47
|
+
config.client_id = value
|
48
|
+
end
|
49
|
+
|
50
|
+
expect(subject.client_id).to eq(value)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe 'options' do
|
55
|
+
it 'returns a new hash of all the config options' do
|
56
|
+
expect(subject.options).to be_a(Hash)
|
57
|
+
expect(subject.options.keys).to eq(SageOne::Configuration::VALID_OPTIONS_KEYS)
|
58
|
+
expect(subject.options[:adapter]).to eq(SageOne::Configuration::DEFAULT_ADAPTER)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'extended' do
|
63
|
+
it 'calls reset on module extending it' do
|
64
|
+
module NewMod; end
|
65
|
+
NewMod.should_receive(:reset).once
|
66
|
+
NewMod.extend(SageOne::Configuration)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe 'reset' do
|
71
|
+
it 'resets all the config options to the default values' do
|
72
|
+
# Change them first
|
73
|
+
SageOne::Configuration::VALID_OPTIONS_KEYS.each { |method| subject.send("#{method}=", 'something') }
|
74
|
+
subject.reset
|
75
|
+
expect(subject.adapter).to eq(SageOne::Configuration::DEFAULT_ADAPTER)
|
76
|
+
expect(subject.api_endpoint).to eq(SageOne::Configuration::DEFAULT_API_ENDPOINT)
|
77
|
+
expect(subject.proxy).to be_nil
|
78
|
+
expect(subject.access_token).to be_nil
|
79
|
+
expect(subject.client_id).to be_nil
|
80
|
+
expect(subject.client_secret).to be_nil
|
81
|
+
expect(subject.request_host).to be_nil
|
82
|
+
expect(subject.user_agent).to eq(SageOne::Configuration::DEFAULT_USER_AGENT)
|
83
|
+
expect(subject.auto_traversal).to be_false
|
84
|
+
expect(subject.raw_response).to be_false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SageOne::Connection do
|
4
|
+
|
5
|
+
let(:client) { SageOne.new }
|
6
|
+
let(:basic_middleware) { %w(FaradayMiddleware::EncodeJson FaradayMiddleware::OAuth2 FaradayMiddleware::RaiseSageOneException) }
|
7
|
+
let(:non_raw_middleware) { %w(FaradayMiddleware::Mashify FaradayMiddleware::ParseJson) }
|
8
|
+
|
9
|
+
it "returns a Faraday" do
|
10
|
+
expect(client.send(:connection)).to be_a(Faraday::Connection)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "sets up mandatory headers" do
|
14
|
+
headers = client.send(:connection).headers
|
15
|
+
|
16
|
+
{ "Accept" => /application\/json/,
|
17
|
+
"User-Agent" => /SageOne Ruby Gem/,
|
18
|
+
"Content-Type" => /application\/json/ }.each do |header, value|
|
19
|
+
expect(headers[header]).to match(value)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "has the correct middleware stack" do
|
24
|
+
stack = client.send(:connection).builder.handlers.map(&:name)
|
25
|
+
(basic_middleware + non_raw_middleware).each { |mw| expect(stack).to include(mw) }
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'raw connection requested' do
|
29
|
+
it 'does not include mashify or parse_json' do
|
30
|
+
client.raw_response = true
|
31
|
+
stack = client.send(:connection).builder.handlers.map(&:name)
|
32
|
+
(non_raw_middleware).each { |mw| expect(stack).to_not include(mw) }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SageOne::OAuth do
|
4
|
+
|
5
|
+
let(:client) { SageOne.new(client_id: "CLIENT_ID", client_secret: "CLIENT_SECRET") }
|
6
|
+
|
7
|
+
describe 'authorize_url' do
|
8
|
+
it 'returns a correctly formatted url' do
|
9
|
+
expect(client.authorize_url('http://www.example.com/endpoint')).to eq("https://app.sageone.com/oauth/authorize/?client_id=CLIENT_ID&redirect_uri=http%3A%2F%2Fwww.example.com%2Fendpoint&response_type=code")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'get_access_token' do
|
14
|
+
it 'returns an object containing an access_token' do
|
15
|
+
|
16
|
+
stub_post("https://app.sageone.com/oauth/token/").
|
17
|
+
with(:body => "{\"client_id\":\"CLIENT_ID\",\"client_secret\":\"CLIENT_SECRET\",\"grant_type\":\"authorization_code\",\"code\":\"uuddlrlr\",\"redirect_uri\":\"http://www.example.com/endpoint\"}",
|
18
|
+
:headers => {'Accept'=>'application/json; charset=utf-8', 'Content-Type'=>'application/json', 'User-Agent'=>'SageOne Ruby Gem 0.0.1'}).
|
19
|
+
to_return(:body => fixture("oauth/oauth_token.json"))
|
20
|
+
|
21
|
+
expect(client.get_access_token('uuddlrlr', 'http://www.example.com/endpoint').access_token).to eq('IguLPAK5VPvDSw3z4SjrkhHTyRXgnnqAPzt1mLUk')
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'failure cases' do
|
25
|
+
context 'invalid client' do
|
26
|
+
before do
|
27
|
+
client.client_secret = "SALMON"
|
28
|
+
stub_post("https://app.sageone.com/oauth/token/").to_return(status: 401, body: fixture('oauth/invalid_client.json'))
|
29
|
+
end
|
30
|
+
it { expect { client.get_access_token('uuddlrlr', 'http://www.example.com/endpoint') }.to raise_error(SageOne::Unauthorized, /invalid_client/) }
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'wrong code' do
|
34
|
+
before { stub_post("https://app.sageone.com/oauth/token/").to_return(status: 400, body: fixture('oauth/invalid_grant.json')) }
|
35
|
+
it { expect { client.get_access_token('', 'http://www.example.com/endpoint') } .to raise_error(SageOne::BadRequest, /invalid_grant/) }
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'wrong callback' do
|
39
|
+
before { stub_post("https://app.sageone.com/oauth/token/").to_return(status: 401, body: '') }
|
40
|
+
it { expect { client.get_access_token('uuddlrlr', 'http://www.example.com/notendpoint') }.to raise_error(SageOne::Unauthorized, %q{{"method":"post","url":"https://app.sageone.com/oauth/token/","status":401,"body":""}}) }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SageOne::Request do
|
4
|
+
|
5
|
+
let(:client) { SageOne.new }
|
6
|
+
|
7
|
+
describe 'helper methods' do
|
8
|
+
before { client.stub(:request) }
|
9
|
+
|
10
|
+
[:get, :delete, :post, :put].each do |meth|
|
11
|
+
it "#{meth} exists and delegates to request" do
|
12
|
+
client.should_receive(:request).with(meth, 'foo', {})
|
13
|
+
client.send(meth, 'foo')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'request' do
|
19
|
+
context 'get/delete' do
|
20
|
+
it "makes a get request" do
|
21
|
+
stub_get('sales_invoices')
|
22
|
+
client.get('sales_invoices')
|
23
|
+
a_get('sales_invoices').should have_been_made.once
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'with options' do
|
27
|
+
it 'passes the options to the get request' do
|
28
|
+
stub_get('sales_invoices?start_date=2011-12-13')
|
29
|
+
client.get('sales_invoices', { start_date: '2011-12-13' })
|
30
|
+
a_get('sales_invoices?start_date=2011-12-13').should have_been_made.once
|
31
|
+
end
|
32
|
+
it 'special-cases start_index' do
|
33
|
+
stub_delete('sales_invoices?start_date=2011-12-13&%24startIndex=10')
|
34
|
+
client.delete('sales_invoices', { start_date: '2011-12-13', start_index: 10 })
|
35
|
+
a_delete('sales_invoices?start_date=2011-12-13&%24startIndex=10').should have_been_made.once
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
context 'put/post' do
|
40
|
+
it "sets the request path" do
|
41
|
+
stub_post('sales_invoices')
|
42
|
+
client.post('sales_invoices')
|
43
|
+
a_post('sales_invoices').should have_been_made.once
|
44
|
+
end
|
45
|
+
it "stores the options in the body" do
|
46
|
+
stub_put('sales_invoices')
|
47
|
+
client.put('sales_invoices', { "void_reason" => nil, "outstanding_amount" => "17.0", "total_net_amount" => "14.17"})
|
48
|
+
a_put('sales_invoices').with(body: '{"void_reason":null,"outstanding_amount":"17.0","total_net_amount":"14.17"}').should have_been_made.once
|
49
|
+
end
|
50
|
+
describe 'auto-conversion of Date-y objects' do
|
51
|
+
it "converts any options which are passed which resemble a date into a correctly-formatted date" do
|
52
|
+
stub_put('sales_invoices/222')
|
53
|
+
client.put('sales_invoices/222', { start_date: Time.new(2012, 10, 20), contents: { another_date: Time.new(2011, 8, 31) }})
|
54
|
+
a_put('sales_invoices/222').with(body: '{"start_date":"20/10/2012","contents":{"another_date":"31/08/2011"}}').should have_been_made.once
|
55
|
+
end
|
56
|
+
it "converts for any type of request, not just puts (regression test)" do
|
57
|
+
stub_get('sales_invoices?contents%5Banother_date%5D=31/08/2011&start_date=20/10/2012')
|
58
|
+
client.get('sales_invoices', { start_date: Time.new(2012, 10, 20), contents: { another_date: Time.new(2011, 8, 31) }})
|
59
|
+
a_get('sales_invoices?contents%5Banother_date%5D=31/08/2011&start_date=20/10/2012').should have_been_made.once
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'Sets a request host if one is specified' do
|
65
|
+
client.request_host = "CustomerHost"
|
66
|
+
stub_get('sales_invoices')
|
67
|
+
client.get('sales_invoices')
|
68
|
+
a_get('sales_invoices').with(headers: { "Host" => 'CustomerHost' }).should have_been_made.once
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "response body" do
|
72
|
+
context 'raw requested' do
|
73
|
+
it "returns a faraday response" do
|
74
|
+
stub_get('sales_invoices')
|
75
|
+
client.raw_response = true
|
76
|
+
expect(client.get('sales_invoices', {})).to be_a(Faraday::Response)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
context 'not raw' do
|
80
|
+
it "returns an array of Hashie::Mash-es" do
|
81
|
+
stub_get('sales_invoices').to_return(body: fixture('sales_invoices.json'))
|
82
|
+
expect(client.get('sales_invoices')).to be_a(Array)
|
83
|
+
expect(client.get('sales_invoices').first).to be_a(Hashie::Mash)
|
84
|
+
expect(client.get('sales_invoices').first.outstanding_amount).to eq("17.0")
|
85
|
+
expect(client.get('sales_invoices').last).to be_a(Hashie::Mash)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'with auto_traversal turned on' do
|
91
|
+
before { client.auto_traversal = true }
|
92
|
+
it "recursively calls itself to build a complete result set" do
|
93
|
+
|
94
|
+
stub_get("sales_invoices").to_return(:status => 200, :body => sdata_fixture('sales_invoices.json', 30, 0, 10))
|
95
|
+
stub_get("sales_invoices?$startIndex=10").to_return(:status => 200, :body => sdata_fixture('sales_invoices.json', 30, 10, 10))
|
96
|
+
stub_get("sales_invoices?$startIndex=20").to_return(:status => 200, :body => sdata_fixture('sales_invoices.json', 30, 20, 10))
|
97
|
+
|
98
|
+
result = client.get("sales_invoices")
|
99
|
+
|
100
|
+
a_get('sales_invoices').should have_been_made.once
|
101
|
+
a_get('sales_invoices?$startIndex=10').should have_been_made.once
|
102
|
+
a_get('sales_invoices?$startIndex=20').should have_been_made.once
|
103
|
+
|
104
|
+
# The response should be three copies of the fixture file concatenated, so result[0] == result[2], etc
|
105
|
+
expect(result.size).to eq(6)
|
106
|
+
expect(result).to be_an(Array)
|
107
|
+
expect(result[0]).to be_a(Hashie::Mash)
|
108
|
+
expect(result[5]).to be_a(Hashie::Mash)
|
109
|
+
expect(result[0]).to eq(result[2])
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SageOne do
|
4
|
+
|
5
|
+
describe '.new' do
|
6
|
+
it { SageOne.new.should be_a(SageOne::Client) }
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '.respond_to?' do
|
10
|
+
it 'returns true when the method exists' do
|
11
|
+
expect(SageOne.respond_to?(:new, true)).to be_true
|
12
|
+
end
|
13
|
+
it 'returns true when a client responds to the method' do
|
14
|
+
expect(SageOne.respond_to?(:sales_invoices)).to be_true
|
15
|
+
end
|
16
|
+
it 'returns false when the method does not exist' do
|
17
|
+
expect(SageOne.respond_to?(:noway, true)).to be_false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '.method_missing' do
|
22
|
+
context "when the client does not respond to the method either" do
|
23
|
+
it 'raises a NoMethodError' do
|
24
|
+
expect(lambda{ SageOne.noway }).to raise_error(NoMethodError)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
context "when the client does respond to the method" do
|
28
|
+
it 'delegates the call to the client, passing any args and block' do
|
29
|
+
block = Proc.new { puts "hi" }
|
30
|
+
mock_client = mock('SageOne::Client', :my_method => 'called')
|
31
|
+
SageOne.should_receive(:new).twice.and_return(mock_client)
|
32
|
+
mock_client.should_receive(:my_method).with('abc', block)
|
33
|
+
SageOne.my_method('abc', block)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
unless ENV['CI']
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start do
|
4
|
+
add_filter "/spec"
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'sage_one'
|
9
|
+
require 'rspec'
|
10
|
+
require 'webmock/rspec'
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.color_enabled = true
|
14
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
15
|
+
config.filter_run :focus => true
|
16
|
+
config.run_all_when_everything_filtered = true
|
17
|
+
end
|
18
|
+
|
19
|
+
def a_delete(url)
|
20
|
+
a_request(:delete, sage_url(url))
|
21
|
+
end
|
22
|
+
|
23
|
+
def a_get(url)
|
24
|
+
a_request(:get, sage_url(url))
|
25
|
+
end
|
26
|
+
|
27
|
+
def a_patch(url)
|
28
|
+
a_request(:patch, sage_url(url))
|
29
|
+
end
|
30
|
+
|
31
|
+
def a_post(url)
|
32
|
+
a_request(:post, sage_url(url))
|
33
|
+
end
|
34
|
+
|
35
|
+
def a_put(url)
|
36
|
+
a_request(:put, sage_url(url))
|
37
|
+
end
|
38
|
+
|
39
|
+
def stub_delete(url)
|
40
|
+
stub_request(:delete, sage_url(url))
|
41
|
+
end
|
42
|
+
|
43
|
+
def stub_get(url)
|
44
|
+
stub_request(:get, sage_url(url))
|
45
|
+
end
|
46
|
+
|
47
|
+
def stub_post(url)
|
48
|
+
stub_request(:post, sage_url(url))
|
49
|
+
end
|
50
|
+
|
51
|
+
def stub_put(url)
|
52
|
+
stub_request(:put, sage_url(url))
|
53
|
+
end
|
54
|
+
|
55
|
+
def sage_url(url)
|
56
|
+
if url =~ /^http/
|
57
|
+
url
|
58
|
+
else
|
59
|
+
"https://app.sageone.com/api/v1/#{url}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def fixture_path
|
64
|
+
File.expand_path("../fixtures", __FILE__)
|
65
|
+
end
|
66
|
+
|
67
|
+
def fixture(file)
|
68
|
+
File.new(fixture_path + '/' + file)
|
69
|
+
end
|
70
|
+
|
71
|
+
def sdata_fixture(fixture, total_results, start_index, items_per_page)
|
72
|
+
raw = fixture(fixture).read
|
73
|
+
raw.sub!(/"\$totalResults":(\d+)/, %Q("$totalResults":#{total_results}))
|
74
|
+
raw.sub!(/"\$startIndex":(\d+)/, %Q("$startIndex":#{start_index}))
|
75
|
+
raw.sub!(/"\$itemsPerPage":(\d+)/, %Q("$itemsPerPage":#{items_per_page}))
|
76
|
+
end
|