jortt 4.2.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +5 -5
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +1 -3
  4. data/README.md +45 -151
  5. data/Rakefile +0 -1
  6. data/jortt.gemspec +6 -6
  7. data/lib/jortt.rb +0 -1
  8. data/lib/jortt/client.rb +65 -16
  9. data/lib/jortt/client/customers.rb +56 -49
  10. data/lib/jortt/client/error.rb +33 -0
  11. data/lib/jortt/client/invoices.rb +41 -38
  12. data/lib/jortt/client/ledger_accounts.rb +27 -0
  13. data/lib/jortt/client/version.rb +1 -2
  14. data/spec/fixtures/vcr_cassettes/Jortt/_client/1_1_1.yml +64 -0
  15. data/spec/fixtures/vcr_cassettes/Jortt_Client/configured/_customers/1_1_1_1.yml +64 -0
  16. data/spec/fixtures/vcr_cassettes/Jortt_Client/configured/_invoices/1_1_2_1.yml +64 -0
  17. data/spec/fixtures/vcr_cassettes/Jortt_Client/configured/_ledger_accounts/1_1_3_1.yml +64 -0
  18. data/spec/fixtures/vcr_cassettes/Jortt_Client_Customers/_create/faulty_payload/shows_a_nice_error.yml +413 -0
  19. data/spec/fixtures/vcr_cassettes/Jortt_Client_Customers/_create/valid_payload/creates_the_customer.yml +505 -0
  20. data/spec/fixtures/vcr_cassettes/Jortt_Client_Customers/_delete/deletes_the_customer.yml +505 -0
  21. data/spec/fixtures/vcr_cassettes/Jortt_Client_Customers/_direct_debit_mandate/sends_direct_debit_mandate_to_the_customer_or_responds_with_an_error_when_not_possible.yml +470 -0
  22. data/spec/fixtures/vcr_cassettes/Jortt_Client_Customers/_index/query/returns_the_queried_customers.yml +464 -0
  23. data/spec/fixtures/vcr_cassettes/Jortt_Client_Customers/_index/without_params/returns_customers.yml +415 -0
  24. data/spec/fixtures/vcr_cassettes/Jortt_Client_Customers/_show/returns_the_customer.yml +464 -0
  25. data/spec/fixtures/vcr_cassettes/Jortt_Client_Customers/_update/updates_the_customer.yml +603 -0
  26. data/spec/fixtures/vcr_cassettes/Jortt_Client_Invoices/_create/creates_the_invoice.yml +170 -0
  27. data/spec/fixtures/vcr_cassettes/Jortt_Client_Invoices/_download/returns_the_invoice_download_link.yml +168 -0
  28. data/spec/fixtures/vcr_cassettes/Jortt_Client_Invoices/_index/invoice_status/returns_those_invoices.yml +776 -0
  29. data/spec/fixtures/vcr_cassettes/Jortt_Client_Invoices/_index/query/returns_the_queried_invoices.yml +315 -0
  30. data/spec/fixtures/vcr_cassettes/Jortt_Client_Invoices/_show/returns_the_invoice.yml +421 -0
  31. data/spec/fixtures/vcr_cassettes/Jortt_Client_LedgerAccounts/_index/returns_invoices.yml +118 -0
  32. data/spec/jortt/client/customers_spec.rb +82 -37
  33. data/spec/jortt/client/invoices_spec.rb +105 -26
  34. data/spec/jortt/client/ledger_accounts_spec.rb +19 -0
  35. data/spec/jortt/client_spec.rb +5 -27
  36. data/spec/jortt_spec.rb +2 -3
  37. data/spec/spec_helper.rb +18 -4
  38. metadata +72 -37
  39. data/.rubocop.yml +0 -26
  40. data/lib/jortt/client/invoice.rb +0 -50
  41. data/spec/jortt/client/invoice_spec.rb +0 -23
@@ -0,0 +1,118 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://app.jortt.nl/oauth-provider/oauth/token
6
+ body:
7
+ encoding: UTF-8
8
+ string: grant_type=client_credentials&scope=invoices%3Aread+invoices%3Awrite+customers%3Aread+customers%3Awrite
9
+ headers:
10
+ User-Agent:
11
+ - Faraday v1.0.1
12
+ Content-Type:
13
+ - application/x-www-form-urlencoded
14
+ Accept-Encoding:
15
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
16
+ Accept:
17
+ - "*/*"
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Date:
24
+ - Thu, 24 Sep 2020 15:03:35 GMT
25
+ Content-Type:
26
+ - application/json; charset=utf-8
27
+ Transfer-Encoding:
28
+ - chunked
29
+ Connection:
30
+ - keep-alive
31
+ Status:
32
+ - 200 OK
33
+ Cache-Control:
34
+ - private, no-store
35
+ Referrer-Policy:
36
+ - strict-origin-when-cross-origin
37
+ X-Permitted-Cross-Domain-Policies:
38
+ - none
39
+ Pragma:
40
+ - no-cache
41
+ X-Xss-Protection:
42
+ - 1; mode=block
43
+ X-Request-Id:
44
+ - d83ae9ab-c629-46a3-8536-3d48345bbc24
45
+ X-Download-Options:
46
+ - noopen
47
+ Etag:
48
+ - W/"0fb39157c79bce6700240fe1ea97876e"
49
+ X-Frame-Options:
50
+ - SAMEORIGIN
51
+ X-Runtime:
52
+ - '0.333943'
53
+ X-Content-Type-Options:
54
+ - nosniff
55
+ X-Powered-By:
56
+ - Phusion Passenger
57
+ Server:
58
+ - nginx + Phusion Passenger
59
+ body:
60
+ encoding: UTF-8
61
+ string: '{"access_token":"-OQjJQikSmMhA9USfhfgESp3DKCWUUOGBfclpyF0oic","token_type":"Bearer","expires_in":7200,"scope":"invoices:read
62
+ invoices:write customers:read customers:write","created_at":1600959815}'
63
+ recorded_at: Thu, 24 Sep 2020 15:03:35 GMT
64
+ - request:
65
+ method: get
66
+ uri: https://api.jortt.nl/ledger_accounts/invoices
67
+ body:
68
+ encoding: US-ASCII
69
+ string: ''
70
+ headers:
71
+ User-Agent:
72
+ - Faraday v1.0.1
73
+ Accept-Encoding:
74
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
75
+ Accept:
76
+ - "*/*"
77
+ response:
78
+ status:
79
+ code: 200
80
+ message: OK
81
+ headers:
82
+ Date:
83
+ - Thu, 24 Sep 2020 15:03:35 GMT
84
+ Content-Type:
85
+ - application/json
86
+ Content-Length:
87
+ - '1011'
88
+ Connection:
89
+ - keep-alive
90
+ Server:
91
+ - Apache
92
+ Status:
93
+ - 200 OK
94
+ Content-Security-Policy:
95
+ - 'default-src ''self'' ''unsafe-inline'' blob: data: *.jortt.nl; script-src
96
+ ''self'' ''unsafe-eval'' blob: ''unsafe-inline'' files.jortt.nl *.googletagmanager.com
97
+ *.uservoice.com inlinemanual.com *.google-analytics.com *.googleadservices.com
98
+ bat.bing.com tapfiliate.com static.ads-twitter.com tagmanager.google.com analytics.twitter.com
99
+ https://connect.facebook.net https://app.inlinemanual.com; connect-src
100
+ ''self'' https://*.jortt.nl wss://*.jortt.nl analytics.inlinemanual.com files.jortt.nl
101
+ www.google-analytics.com stats.g.doubleclick.net file-storage-app-production.s3.eu-central-1.amazonaws.com
102
+ https://app.inlinemanual.com; style-src ''self'' ''unsafe-inline'' fonts.googleapis.com
103
+ files.jortt.nl tagmanager.google.com https://app.inlinemanual.com; font-src
104
+ ''self'' data: fonts.gstatic.com files.jortt.nl https://app.inlinemanual.com; frame-src
105
+ ''self'' *.jortt.nl files.jortt.nl b.frstre.com beacon.tapfiliate.com jortt.uservoice.com
106
+ *.vimeo.com https://connect.facebook.net; img-src ''self'' blob: data:
107
+ *.jortt.nl https://www.facebook.com files.jortt.nl www.google-analytics.com
108
+ stats.g.doubleclick.net www.google.nl www.google.com bat.bing.com googleads.g.doubleclick.net
109
+ https://www.googletagmanager.com https://widget.uservoice.com www.google.de
110
+ t.co *.gstatic.com https://ssl.google-analytics.com https://app.inlinemanual.com'
111
+ body:
112
+ encoding: UTF-8
113
+ string: '{"data":[{"ledger_account_id":"05a59e27-489f-466d-adf7-fc06f576d4ec","parent_ledger_account_id":"105ea7d7-8bb5-499e-9823-8324826b6563","name":"Omzet","selectable":false},{"ledger_account_id":"fcabf6bf-cccc-4a6d-b04e-7b2369d04a79","parent_ledger_account_id":"05a59e27-489f-466d-adf7-fc06f576d4ec","name":"Diverse
114
+ omzet","selectable":true},{"ledger_account_id":"05ba2a61-a0cc-4736-9000-89fb361e85c8","parent_ledger_account_id":"05a59e27-489f-466d-adf7-fc06f576d4ec","name":"Omzet
115
+ uit facturen","selectable":true},{"ledger_account_id":"92cb5ad4-e2e7-4c41-8e17-bf7db69c7b9a","parent_ledger_account_id":"986e5b45-dae6-48cc-b4c9-a2f30fbd7bfe","name":"Inkoopkosten","selectable":false},{"ledger_account_id":"424d222d-f8cb-4d38-82c0-24674a2dcf75","parent_ledger_account_id":"92cb5ad4-e2e7-4c41-8e17-bf7db69c7b9a","name":"Inkoop","selectable":true},{"ledger_account_id":"a5c08fb4-0a22-4f53-9ee2-d7682bba10f7","parent_ledger_account_id":"92cb5ad4-e2e7-4c41-8e17-bf7db69c7b9a","name":"Uitbesteed
116
+ werk","selectable":true}]}'
117
+ recorded_at: Thu, 24 Sep 2020 15:03:35 GMT
118
+ recorded_with: VCR 6.0.0
@@ -1,59 +1,104 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
2
 
4
- describe Jortt::Client::Customers do
5
- let(:customers) do
6
- described_class.new(
7
- double(base_url: 'foo', app_name: 'app', api_key: 'secret'),
8
- )
3
+ describe Jortt::Client::Customers, :vcr do
4
+ let(:client) { Jortt.client(ENV['JORTT_CLIENT_ID'], ENV['JORTT_CLIENT_SECRET']) }
5
+
6
+ let(:params) do
7
+ {
8
+ is_private: false,
9
+ customer_name: 'Nuka-Cola Corporation',
10
+ address_street: 'Vault 11',
11
+ address_postal_code: '1111AA',
12
+ address_city: 'Mojave Wasteland'
13
+ }
14
+ end
15
+
16
+ let!(:jane) { client.customers.create(is_private: true, customer_name: 'Jane Doe')['id'] }
17
+ let!(:john) { client.customers.create(is_private: true, customer_name: 'John Doe')['id'] }
18
+
19
+ after do
20
+ client.customers.delete(jane)
21
+ client.customers.delete(john)
9
22
  end
10
23
 
11
- describe '#all' do
24
+ describe '#index' do
12
25
  context 'without params' do
13
- subject { customers.all }
26
+ subject { client.customers.index.to_a }
14
27
 
15
- before do
16
- url = 'http://app:secret@foo/customers/all?page=1&per_page=50'
17
- stub_request(:get, url).
18
- to_return(status: 200, body: '{"customers": ["foo"]}')
28
+ it "returns customers" do
29
+ expect(subject.count).to eq(3)
30
+ expect(subject[0]['customer_name']).to eq('Jane Doe')
31
+ expect(subject[1]['customer_name']).to eq('John Doe')
32
+ expect(subject[2]['customer_name']).to eq('Search target')
19
33
  end
20
-
21
- it { should eq('customers' => ['foo']) }
22
34
  end
23
35
 
24
- context 'with params' do
25
- subject { customers.all(page: page, per_page: per_page) }
26
- let(:page) { 3 }
27
- let(:per_page) { 25 }
36
+ context 'query' do
37
+ subject { client.customers.index(query: 'Search target') }
28
38
 
29
- before do
30
- url = 'http://app:secret@foo/customers/all?page=3&per_page=25'
31
- stub_request(:get, url).
32
- to_return(status: 200, body: '{"customers": ["bar"]}')
39
+ it "returns the queried customers" do
40
+ expect(subject.count).to eq(1)
41
+ expect(subject.first['customer_name']).to eq("Search target")
33
42
  end
43
+ end
44
+ end
45
+
46
+ describe '#show' do
47
+ subject { client.customers.show(jane) }
34
48
 
35
- it { should eq('customers' => ['bar']) }
49
+ it "returns the customer" do
50
+ expect(subject['customer_name']).to eq("Jane Doe")
36
51
  end
37
52
  end
38
53
 
39
54
  describe '#create' do
40
- let(:request_body) { JSON.generate(customer: {line_items: []}) }
41
- let(:response_body) { JSON.generate(customer_id: 'abc') }
42
- subject { customers.create(line_items: []) }
43
- before do
44
- stub_request(:post, 'http://app:secret@foo/customers').
45
- with(body: request_body).
46
- to_return(status: 200, body: response_body)
55
+ context "valid payload" do
56
+ subject { client.customers.create(params) }
57
+ after { client.customers.delete(subject['id']) }
58
+
59
+ it "creates the customer" do
60
+ uuid_length = 36
61
+ expect(subject['id'].length).to eq(uuid_length)
62
+ end
63
+ end
64
+
65
+ context "faulty payload" do
66
+ subject { client.customers.create({}) }
67
+
68
+ it "shows a nice error" do
69
+ expect { subject }.to raise_error(Jortt::Client::Error)
70
+ end
71
+ end
72
+ end
73
+
74
+ describe '#update' do
75
+ let(:uuid) { client.customers.create(params).fetch('id') }
76
+ subject { client.customers.update(uuid, params.merge(address_extra_information: 'Extra...')) }
77
+ after { client.customers.delete(uuid) }
78
+
79
+ it "updates the customer" do
80
+ expect(subject).to eq(true)
47
81
  end
48
- it { should eq('customer_id' => 'abc') }
49
82
  end
50
83
 
51
- describe '#search' do
52
- subject { customers.search('terms') }
53
- before do
54
- stub_request(:get, 'http://app:secret@foo/customers?query=terms').
55
- to_return(status: 200, body: '{"customers": []}')
84
+ describe '#delete' do
85
+ let(:uuid) { client.customers.create(params).fetch('id') }
86
+ subject { client.customers.delete(uuid) }
87
+
88
+ it "deletes the customer" do
89
+ expect(subject).to eq(true)
90
+ end
91
+ end
92
+
93
+ describe '#direct_debit_mandate' do
94
+ subject { client.customers.direct_debit_mandate(jane) }
95
+
96
+ it "sends direct debit mandate to the customer or responds with an error when not possible" do
97
+ begin
98
+ subject
99
+ rescue Jortt::Client::Error => e
100
+ expect(e.details.first['key']).to eq("DirectDebit::NotEnabled")
101
+ end
56
102
  end
57
- it { should eq('customers' => []) }
58
103
  end
59
104
  end
@@ -1,40 +1,119 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
2
 
4
- describe Jortt::Client::Invoices do
5
- let(:invoices) do
6
- described_class.new(
7
- double(base_url: 'foo', app_name: 'app', api_key: 'secret'),
8
- )
3
+ describe Jortt::Client::Invoices, :vcr do
4
+ let(:client) { Jortt.client(ENV['JORTT_CLIENT_ID'], ENV['JORTT_CLIENT_SECRET']) }
5
+
6
+ let(:customer) { client.customers.index(query: 'Search target').first }
7
+
8
+ let(:params) do
9
+ {
10
+ customer_id: customer.fetch('id'),
11
+ send_method: 'self',
12
+ line_items: [{
13
+ vat: 21,
14
+ amount_per_unit: {
15
+ value: 499,
16
+ currency: 'EUR'
17
+ },
18
+ units: 4,
19
+ description: 'Your product'
20
+ }]
21
+ }
22
+ end
23
+
24
+ describe '#index' do
25
+ context 'pagination', vcr: false do
26
+ subject { client.invoices.index }
27
+
28
+ before do
29
+ VCR.turn_off!
30
+
31
+ stub_request(:any, "https://app.jortt.nl/oauth-provider/oauth/token").
32
+ to_return(
33
+ headers: {content_type: 'application/json'},
34
+ body: {access_token: 'abc'}.to_json)
35
+
36
+ stub_request(:get, "https://api.jortt.nl/invoices?invoice_status&page=1&query").
37
+ to_return(
38
+ headers: {content_type: 'application/json'},
39
+ body: {
40
+ 'data': [{id: 1}, {id: 2}],
41
+ _links: {next: "https://api.jortt.nl/invoices?page=2"}
42
+ }.to_json
43
+ )
44
+
45
+ stub_request(:get, "https://api.jortt.nl/invoices?invoice_status&page=2&query").
46
+ to_return(
47
+ headers: {content_type: 'application/json'},
48
+ body: {
49
+ data: [{id: 3}, {id: 4}],
50
+ _links: {next: "https://api.jortt.nl/invoices?page=3"}
51
+ }.to_json
52
+ )
53
+
54
+ stub_request(:get, "https://api.jortt.nl/invoices?invoice_status&page=3&query").
55
+ to_return(
56
+ headers: {content_type: 'application/json'},
57
+ body: {
58
+ data: [{id: 5}],
59
+ _links: {next: nil}
60
+ }.to_json
61
+ )
62
+ end
63
+
64
+ after { VCR.turn_on! }
65
+
66
+ it "returns the first page" do
67
+ expect(subject.first.fetch('id')).to eq(1)
68
+ end
69
+
70
+ it "seamlessly returns results from the other pages" do
71
+ expect(subject.to_a.count).to eq(5)
72
+ end
73
+ end
74
+
75
+ context 'invoice_status' do
76
+ subject { client.invoices.index(invoice_status: 'sent') }
77
+
78
+ it "returns those invoices" do
79
+ expect(subject.count).to be > 0
80
+ expect(subject.first.fetch('invoice_status')).to eq('sent')
81
+ end
82
+ end
83
+
84
+ context 'query' do
85
+ subject { client.invoices.index(query: 'Search target') }
86
+
87
+ it "returns the queried invoices" do
88
+ expect(subject.first.dig('invoice_due_amount', 'value')).to eq("2415.16")
89
+ end
90
+ end
9
91
  end
10
92
 
11
93
  describe '#create' do
12
- let(:request_body) { JSON.generate(invoice: {line_items: []}) }
13
- let(:response_body) { JSON.generate(invoice_id: 'abc') }
14
- subject { invoices.create(line_items: []) }
15
- before do
16
- stub_request(:post, 'http://app:secret@foo/invoices').
17
- with(body: request_body).
18
- to_return(status: 200, body: response_body)
94
+ subject { client.invoices.create(params) }
95
+
96
+ it "creates the invoice" do
97
+ uuid_length = 36
98
+ expect(subject['id'].length).to eq(uuid_length)
19
99
  end
20
- it { should eq('invoice_id' => 'abc') }
21
100
  end
22
101
 
23
- describe '#get' do
24
- subject { invoices.get('foo') }
25
- before do
26
- stub_request(:get, 'http://app:secret@foo/invoices/id/foo').
27
- to_return(status: 200, body: '{"id": "foo"}')
102
+ describe '#show' do
103
+ let(:uuid) { client.invoices.index(query: 'Search target').first.fetch('id') }
104
+ subject { client.invoices.show(uuid) }
105
+
106
+ it "returns the invoice" do
107
+ expect(subject.dig('invoice_due_amount', 'value')).to eq("2415.16")
28
108
  end
29
- it { should eq('id' => 'foo') }
30
109
  end
31
110
 
32
- describe '#search' do
33
- subject { invoices.search('terms') }
34
- before do
35
- stub_request(:get, 'http://app:secret@foo/invoices/search?query=terms').
36
- to_return(status: 200, body: '{"invoices": []}')
111
+ describe '#download' do
112
+ let(:uuid) { client.invoices.index(query: 'Download test').first.fetch('id') }
113
+ subject { client.invoices.download(uuid) }
114
+
115
+ it "returns the invoice download link" do
116
+ expect(subject.fetch('download_location')).to match(/https:\/\/files\.jortt\.nl\/.*/)
37
117
  end
38
- it { should eq('invoices' => []) }
39
118
  end
40
119
  end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe Jortt::Client::LedgerAccounts, :vcr do
4
+ let(:client) { Jortt.client(ENV['JORTT_CLIENT_ID'], ENV['JORTT_CLIENT_SECRET']) }
5
+
6
+ describe '#index' do
7
+ subject { client.ledger_accounts.index }
8
+
9
+ it "returns invoices" do
10
+ expect(subject.count).to be > 0
11
+ expect(subject.first).to eq(
12
+ "ledger_account_id" => "05a59e27-489f-466d-adf7-fc06f576d4ec",
13
+ "name" => "Omzet",
14
+ "parent_ledger_account_id" => "105ea7d7-8bb5-499e-9823-8324826b6563",
15
+ "selectable" => false
16
+ )
17
+ end
18
+ end
19
+ end
@@ -1,30 +1,8 @@
1
- # encoding: UTF-8
2
1
  require 'spec_helper'
3
2
 
4
- describe Jortt::Client do
5
- describe '#initialize' do
6
- subject { described_class.new(opts) }
7
- let(:opts) { {} }
8
-
9
- specify { expect { subject }.to raise_error(KeyError) }
10
-
11
- context 'given app_name' do
12
- before { opts[:app_name] = 'name' }
13
-
14
- specify { expect { subject }.to raise_error(KeyError) }
15
-
16
- context 'and api_key' do
17
- before { opts[:api_key] = 'secret' }
18
- it { should be_instance_of(described_class) }
19
- its(:base_url) { should eq(described_class::BASE_URL) }
20
- its(:app_name) { should eq('name') }
21
- its(:api_key) { should eq('secret') }
22
- end
23
- end
24
- end
25
-
3
+ describe Jortt::Client, :vcr do
26
4
  context 'configured' do
27
- let(:client) { described_class.new(app_name: 'app', api_key: 'secret') }
5
+ let(:client) { described_class.new(ENV['JORTT_CLIENT_ID'], ENV['JORTT_CLIENT_SECRET']) }
28
6
 
29
7
  describe '#customers' do
30
8
  subject { client.customers }
@@ -36,9 +14,9 @@ describe Jortt::Client do
36
14
  it { should be_instance_of(described_class::Invoices) }
37
15
  end
38
16
 
39
- describe '#invoice' do
40
- subject { client.invoice('foo') }
41
- it { should be_instance_of(described_class::Invoice) }
17
+ describe '#ledger_accounts' do
18
+ subject { client.ledger_accounts }
19
+ it { should be_instance_of(described_class::LedgerAccounts) }
42
20
  end
43
21
  end
44
22
  end