harvested2 5.0.3

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.
Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.gitignore +35 -0
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +34 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +12 -0
  8. data/Gemfile +20 -0
  9. data/HISTORY.md +118 -0
  10. data/MIT-LICENSE +21 -0
  11. data/README.md +66 -0
  12. data/Rakefile +24 -0
  13. data/harvested2.gemspec +30 -0
  14. data/lib/ext/array.rb +52 -0
  15. data/lib/ext/date.rb +9 -0
  16. data/lib/ext/hash.rb +17 -0
  17. data/lib/ext/time.rb +5 -0
  18. data/lib/harvest/account.rb +13 -0
  19. data/lib/harvest/api/account.rb +25 -0
  20. data/lib/harvest/api/base.rb +72 -0
  21. data/lib/harvest/api/clients.rb +10 -0
  22. data/lib/harvest/api/company.rb +12 -0
  23. data/lib/harvest/api/contacts.rb +9 -0
  24. data/lib/harvest/api/expense_categories.rb +9 -0
  25. data/lib/harvest/api/expenses.rb +26 -0
  26. data/lib/harvest/api/invoice_categories.rb +9 -0
  27. data/lib/harvest/api/invoice_messages.rb +86 -0
  28. data/lib/harvest/api/invoice_payments.rb +41 -0
  29. data/lib/harvest/api/invoices.rb +9 -0
  30. data/lib/harvest/api/projects.rb +9 -0
  31. data/lib/harvest/api/task_assignments.rb +75 -0
  32. data/lib/harvest/api/tasks.rb +9 -0
  33. data/lib/harvest/api/time_entry.rb +19 -0
  34. data/lib/harvest/api/user_assignments.rb +75 -0
  35. data/lib/harvest/api/users.rb +10 -0
  36. data/lib/harvest/base.rb +333 -0
  37. data/lib/harvest/behavior/activatable.rb +31 -0
  38. data/lib/harvest/behavior/crud.rb +80 -0
  39. data/lib/harvest/client.rb +23 -0
  40. data/lib/harvest/company.rb +8 -0
  41. data/lib/harvest/contact.rb +20 -0
  42. data/lib/harvest/credentials.rb +34 -0
  43. data/lib/harvest/errors.rb +27 -0
  44. data/lib/harvest/expense.rb +54 -0
  45. data/lib/harvest/expense_category.rb +10 -0
  46. data/lib/harvest/hardy_client.rb +80 -0
  47. data/lib/harvest/invoice.rb +75 -0
  48. data/lib/harvest/invoice_category.rb +8 -0
  49. data/lib/harvest/invoice_message.rb +8 -0
  50. data/lib/harvest/invoice_payment.rb +8 -0
  51. data/lib/harvest/line_item.rb +21 -0
  52. data/lib/harvest/model.rb +133 -0
  53. data/lib/harvest/project.rb +41 -0
  54. data/lib/harvest/receipt.rb +12 -0
  55. data/lib/harvest/task.rb +21 -0
  56. data/lib/harvest/task_assignment.rb +27 -0
  57. data/lib/harvest/time_entry.rb +57 -0
  58. data/lib/harvest/timezones.rb +130 -0
  59. data/lib/harvest/user.rb +58 -0
  60. data/lib/harvest/user_assignment.rb +27 -0
  61. data/lib/harvest/version.rb +3 -0
  62. data/lib/harvested2.rb +96 -0
  63. data/spec/factories/client.rb +14 -0
  64. data/spec/factories/contact.rb +8 -0
  65. data/spec/factories/expense.rb +10 -0
  66. data/spec/factories/expenses_category.rb +7 -0
  67. data/spec/factories/invoice.rb +25 -0
  68. data/spec/factories/invoice_category.rb +5 -0
  69. data/spec/factories/invoice_message.rb +9 -0
  70. data/spec/factories/invoice_payment.rb +7 -0
  71. data/spec/factories/line_item.rb +9 -0
  72. data/spec/factories/project.rb +15 -0
  73. data/spec/factories/task.rb +8 -0
  74. data/spec/factories/task_assignment.rb +8 -0
  75. data/spec/factories/time_entry.rb +13 -0
  76. data/spec/factories/user.rb +19 -0
  77. data/spec/factories/user_assigment.rb +7 -0
  78. data/spec/functional/clients_spec.rb +105 -0
  79. data/spec/functional/errors_spec.rb +42 -0
  80. data/spec/functional/expenses_spec.rb +97 -0
  81. data/spec/functional/invoice_messages_spec.rb +48 -0
  82. data/spec/functional/invoice_payments_spec.rb +51 -0
  83. data/spec/functional/invoice_spec.rb +138 -0
  84. data/spec/functional/project_spec.rb +76 -0
  85. data/spec/functional/tasks_spec.rb +119 -0
  86. data/spec/functional/time_entries_spec.rb +87 -0
  87. data/spec/functional/users_spec.rb +72 -0
  88. data/spec/harvest/base_spec.rb +10 -0
  89. data/spec/harvest/basic_auth_credentials_spec.rb +12 -0
  90. data/spec/harvest/expense_category_spec.rb +5 -0
  91. data/spec/harvest/expense_spec.rb +18 -0
  92. data/spec/harvest/invoice_message_spec.rb +5 -0
  93. data/spec/harvest/invoice_payment_spec.rb +5 -0
  94. data/spec/harvest/invoice_spec.rb +5 -0
  95. data/spec/harvest/oauth_credentials_spec.rb +11 -0
  96. data/spec/harvest/project_spec.rb +5 -0
  97. data/spec/harvest/task_assignment_spec.rb +5 -0
  98. data/spec/harvest/task_spec.rb +5 -0
  99. data/spec/harvest/time_entry_spec.rb +23 -0
  100. data/spec/harvest/user_assignment_spec.rb +5 -0
  101. data/spec/harvest/user_spec.rb +34 -0
  102. data/spec/spec_helper.rb +22 -0
  103. data/spec/support/factory_bot.rb +5 -0
  104. data/spec/support/harvested_helpers.rb +28 -0
  105. data/spec/support/json_examples.rb +9 -0
  106. metadata +238 -0
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'harvest errors' do
4
+ let(:harvest) { Harvest.client(access_token: 'mytoken', account_id: '123') }
5
+
6
+ before { WebMock.disable_net_connect! }
7
+
8
+ context 'wraps errors' do
9
+ it 'sould returns a Harvest::BadRequest error' do
10
+ stub_request(:get, /\/clients/).to_return({ status: ['400', 'Bad Request']},
11
+ { body: '[]', status: 200})
12
+ expect { harvest.clients.all }.to raise_error(Harvest::BadRequest)
13
+ end
14
+
15
+ it 'sould returns a Harvest::NotFound error' do
16
+ stub_request(:get, /\/clients/)
17
+ .to_return({ status: ['404', 'Not Found'] }, { body: '[]', status: 200})
18
+ expect { harvest.clients.all }.to raise_error(Harvest::NotFound)
19
+ end
20
+
21
+ it 'sould returns a Harvest::ServerError error' do
22
+ stub_request(:get, /\/clients/)
23
+ .to_return({ status: ['500', 'Server Error'] },
24
+ { body: '[]', status: 200 })
25
+ expect { harvest.clients.all }.to raise_error(Harvest::ServerError)
26
+ end
27
+
28
+ it 'sould returns a Harvest::Unavailable error' do
29
+ stub_request(:get, /\/clients/)
30
+ .to_return({ status: ['502', 'Bad Gateway'] },
31
+ { body: '[]', status: 200 })
32
+ expect { harvest.clients.all }.to raise_error(Harvest::Unavailable)
33
+ end
34
+
35
+ it 'sould returns a Harvest::RateLimited error' do
36
+ stub_request(:get, /\/clients/)
37
+ .to_return({ status: ['503', 'Rate Limited'] },
38
+ { body: '[]', status: 200 })
39
+ expect { harvest.clients.all }.to raise_error(Harvest::RateLimited)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'harvest expenses' do
4
+ let(:harvest) { Harvest.client(access_token: 'mytoken', account_id: '123') }
5
+
6
+ describe 'categories' do
7
+ let(:expense_category) { create(:expense_category) }
8
+ let(:expense_category_attributes) do
9
+ FactoryBot.attributes_for(:expense_category)
10
+ end
11
+
12
+ context 'allows to add clients' do
13
+ before do
14
+ allow(harvest.expense_categories).to receive(:create)
15
+ .and_return(expense_category)
16
+ end
17
+
18
+ it 'returns true' do
19
+ expect(expense_category.name).to eql('Mileage')
20
+ end
21
+ end
22
+
23
+ context 'allows to update clients' do
24
+ before do
25
+ allow(harvest.expense_categories).to receive(:update)
26
+ .and_return(expense_category)
27
+ expense_category.name = 'Travel'
28
+ expense_category = harvest.expense_categories.update(expense_category)
29
+ end
30
+
31
+ it 'returns true' do
32
+ expect(expense_category.name).to eql('Travel')
33
+ end
34
+ end
35
+
36
+ context 'allows to delete clients' do
37
+ before do
38
+ allow(harvest.expense_categories).to receive(:delete).and_return([])
39
+ allow(harvest.expense_categories).to receive(:all).and_return([])
40
+ harvest.expense_categories.delete(expense_category)
41
+ end
42
+
43
+ it 'returns true' do
44
+ expect(harvest.expense_categories.all.select do |e|
45
+ e.name == 'Travel'
46
+ end).to eql([])
47
+ end
48
+ end
49
+ end
50
+
51
+ describe 'expenses' do
52
+ let(:client) { create(:client) }
53
+ let(:project) { create(:project, client: client) }
54
+ let(:expense_category) { create(:expense_category) }
55
+ let(:expense) do
56
+ create(:expense, project: project, expense_category: expense_category)
57
+ end
58
+
59
+ context 'allows to add expense' do
60
+ before do
61
+ allow(harvest.expenses).to receive(:create)
62
+ .and_return(expense)
63
+ end
64
+
65
+ it 'returns true' do
66
+ expect(expense.notes).to eql('Drive to Chicago')
67
+ end
68
+ end
69
+
70
+ context 'allows to update expense' do
71
+ before do
72
+ allow(harvest.expenses).to receive(:update)
73
+ .and_return(expense)
74
+ expense.notes = 'Off to Chicago'
75
+ expense = harvest.expenses.update(expense)
76
+ end
77
+
78
+ it 'returns true' do
79
+ expect(expense.notes).to eql('Off to Chicago')
80
+ end
81
+ end
82
+
83
+ context 'allows to delete clients' do
84
+ before do
85
+ allow(harvest.expenses).to receive(:delete).and_return([])
86
+ allow(harvest.expenses).to receive(:all).and_return([])
87
+ harvest.expenses.delete(expense)
88
+ end
89
+
90
+ it 'returns true' do
91
+ expect(harvest.expenses.all(Time.utc(2009, 12, 28)).select do |e|
92
+ e.notes == 'Off to Chicago'
93
+ end).to eql([])
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'harvest invoice messages' do
4
+ let(:harvest) { Harvest.client(access_token: 'mytoken', account_id: '123') }
5
+
6
+ describe 'invoice messages' do
7
+ let(:invoice) { create(:invoice) }
8
+ let(:invoice_message) { create(:invoice_message) }
9
+
10
+ context 'allows to add invoice_messages' do
11
+ before do
12
+ allow(harvest.invoice_messages).to receive(:create)
13
+ .and_return(invoice_message)
14
+ end
15
+
16
+ it 'returns true' do
17
+ expect(invoice_message.body).to eql('The message body goes here')
18
+ end
19
+ end
20
+
21
+ context 'allows to update invoice_messages' do
22
+ before do
23
+ allow(harvest.invoice_messages).to receive(:update)
24
+ .and_return(invoice_message)
25
+ invoice_message.body = 'New body text'
26
+ invoice_message = harvest.invoice_messages.update(invoice_message)
27
+ end
28
+
29
+ it 'returns true' do
30
+ expect(invoice_message.body).to eql('New body text')
31
+ end
32
+ end
33
+
34
+ context 'allows to remove invoice_messages' do
35
+ before do
36
+ allow(harvest.invoice_messages).to receive(:delete).and_return([])
37
+ allow(harvest.invoice_messages).to receive(:all).and_return([])
38
+ harvest.invoice_messages.delete(invoice_message)
39
+ end
40
+
41
+ it 'returns true' do
42
+ expect(harvest.invoice_messages.all.select do |i|
43
+ i.body == 'New body text'
44
+ end).to eql([])
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'harvest invoice payments' do
4
+ let(:harvest) { Harvest.client(access_token: 'mytoken', account_id: '123') }
5
+
6
+ describe 'invoice payments' do
7
+ let(:invoice) { create(:invoice) }
8
+ let(:invoice_payment) { create(:invoice_payment, amount: invoice.amount) }
9
+ let(:invoice_payment_attributes) do
10
+ FactoryBot.attributes_for(:invoice_payment)
11
+ end
12
+
13
+ context 'allows to add invoice_payments' do
14
+ before do
15
+ allow(harvest.invoice_payments).to receive(:create)
16
+ .and_return(invoice_payment)
17
+ end
18
+
19
+ it 'returns true' do
20
+ expect(invoice_payment.amount).to eql(invoice.amount)
21
+ end
22
+ end
23
+
24
+ context 'allows to update invoice_payments' do
25
+ before do
26
+ allow(harvest.invoice_payments).to receive(:update)
27
+ .and_return(invoice_payment)
28
+ invoice_payment.amount = 200
29
+ invoice_payment = harvest.invoice_payments.update(invoice_payment)
30
+ end
31
+
32
+ it 'returns true' do
33
+ expect(invoice_payment.amount).to eql(200)
34
+ end
35
+ end
36
+
37
+ context 'allows to remove invoice_payments' do
38
+ before do
39
+ allow(harvest.invoice_payments).to receive(:delete).and_return([])
40
+ allow(harvest.invoice_payments).to receive(:all).and_return([])
41
+ harvest.invoice_payments.delete(invoice_payment)
42
+ end
43
+
44
+ it 'returns true' do
45
+ expect(harvest.invoice_payments.all.select do |i|
46
+ i.amount == 200
47
+ end).to eql([])
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,138 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'harvest invoices' do
4
+ let(:harvest) { Harvest.client(access_token: 'mytoken', account_id: '123') }
5
+
6
+ describe 'categories' do
7
+ let(:invoice_category) { create(:invoice_category) }
8
+
9
+ context 'allows to add invoice categories' do
10
+ before do
11
+ allow(harvest.invoice_categories).to receive(:create)
12
+ .and_return(invoice_category)
13
+ end
14
+
15
+ it 'returns true' do
16
+ expect(invoice_category.name).to eql('New Category')
17
+ end
18
+ end
19
+
20
+ context 'allows to update invoice categories' do
21
+ before do
22
+ allow(harvest.invoice_categories).to receive(:update)
23
+ .and_return(invoice_category)
24
+ invoice_category.name = 'Updated Category'
25
+ invoice_category = harvest.invoice_categories.update(invoice_category)
26
+ end
27
+
28
+ it 'returns true' do
29
+ expect(invoice_category.name).to eql('Updated Category')
30
+ end
31
+ end
32
+
33
+ context 'allows to remove invoice categories' do
34
+ before do
35
+ allow(harvest.invoice_categories).to receive(:delete).and_return([])
36
+ allow(harvest.invoice_categories).to receive(:all).and_return([])
37
+ harvest.invoice_categories.delete(invoice_category)
38
+ end
39
+
40
+ it 'returns true' do
41
+ expect(harvest.invoice_categories.all.select do |p|
42
+ p.name == 'Updated Category'
43
+ end).to eql([])
44
+ end
45
+ end
46
+ end
47
+
48
+ describe 'invoices' do
49
+ let(:invoice_category) { create(:invoice_category) }
50
+ let(:invoice) { create(:invoice) }
51
+ let(:line_item) { create(:line_item) }
52
+
53
+ context 'allows to add invoice invoices' do
54
+ before do
55
+ allow(harvest.invoices).to receive(:create)
56
+ .and_return(invoice)
57
+ end
58
+
59
+ it 'returns true' do
60
+ expect(invoice.subject)
61
+ .to eql("Invoice for Joe's Stream Cleaning")
62
+ expect(invoice.amount).to eql(2400.0)
63
+ expect(invoice.line_items.size).to eql(0)
64
+ end
65
+ end
66
+
67
+ context 'allows to update invoices' do
68
+ before do
69
+ allow(harvest.invoices).to receive(:update)
70
+ .and_return(invoice)
71
+ invoice.subject = 'Updated Invoice for Joe'
72
+ invoice.line_items << line_item
73
+ invoice.update_line_items = true
74
+ invoice.amount = 4800.0
75
+ invoice = harvest.invoices.update(invoice)
76
+ end
77
+
78
+ it 'returns true' do
79
+ expect(invoice.subject).to eql('Updated Invoice for Joe')
80
+ expect(invoice.amount).to eql(4800.0)
81
+ expect(invoice.line_items.size).to eql(1)
82
+ end
83
+ end
84
+
85
+ context 'allows to remove invoices' do
86
+ before do
87
+ allow(harvest.invoices).to receive(:delete).and_return([])
88
+ allow(harvest.invoices).to receive(:all).and_return([])
89
+ harvest.invoices.delete(invoice)
90
+ end
91
+
92
+ it 'returns true' do
93
+ expect(harvest.invoices.all.select do |i|
94
+ i.number == '1000'
95
+ end).to eql([])
96
+ end
97
+ end
98
+ end
99
+
100
+ describe 'read' do
101
+ let(:invoice_category) { create(:invoice_category) }
102
+ let(:invoice) { create(:invoice) }
103
+ let(:line_item) { create(:line_item) }
104
+ let(:invoice_attributes) { FactoryBot.attributes_for(:invoice) }
105
+ let(:invoices) { [invoice] }
106
+
107
+ context 'allows finding one invoice with parameters' do
108
+ before do
109
+ allow(harvest.invoices).to receive(:create)
110
+ .and_return(invoice)
111
+ allow(harvest.invoices).to receive(:find)
112
+ .and_return(invoice)
113
+ allow(harvest.invoices).to receive(:all).and_return(invoices)
114
+
115
+ invoice = harvest.invoices.create(invoice_attributes)
116
+ invoice.line_items << line_item
117
+ result = harvest.invoices.find(invoice.id)
118
+ end
119
+
120
+ it 'returns a specific invoice' do
121
+ expect(invoice.subject).to eql("Invoice for Joe's Stream Cleaning")
122
+ expect(invoice.amount).to eql(2400.0)
123
+ expect(invoice.line_items.size).to eql(1)
124
+ expect(invoice.due_at).to eql('2011-03-31')
125
+ expect(invoices.count).to eql(1)
126
+ end
127
+
128
+ it 'returns invoices' do
129
+ invoices = harvest.invoices.all(status: 'draft')
130
+ expect(invoice.subject).to eql("Invoice for Joe's Stream Cleaning")
131
+ expect(invoice.amount).to eql(2400.0)
132
+ expect(invoice.line_items.size).to eql(1)
133
+ expect(invoice.due_at).to eql('2011-03-31')
134
+ expect(invoices.count).to eql(1)
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'harvest projects' do
4
+ let(:harvest) { Harvest.client(access_token: 'mytoken', account_id: '123') }
5
+
6
+ describe 'projects' do
7
+ let(:client) { create(:client) }
8
+ let(:project) { create(:project, client: client) }
9
+ let(:project_attributes) { FactoryBot.attributes_for(:project) }
10
+
11
+ context 'allows to add projects' do
12
+ before do
13
+ allow(harvest.projects).to receive(:create).and_return(project)
14
+ project = harvest.projects.create(project_attributes)
15
+ end
16
+
17
+ it 'should return true' do
18
+ expect(project.name).to eql('Project Test')
19
+ end
20
+ end
21
+
22
+ context 'allows to update projects' do
23
+ before do
24
+ allow(harvest.projects).to receive(:update).and_return(project)
25
+ project.name = 'Updated Project'
26
+ project = harvest.projects.update(project)
27
+ end
28
+
29
+ it 'should return true' do
30
+ expect(project.name).to eql('Updated Project')
31
+ end
32
+ end
33
+
34
+ context 'allows to remove projects' do
35
+ before do
36
+ allow(harvest.projects).to receive(:delete).and_return([])
37
+ allow(harvest.projects).to receive(:all).and_return([])
38
+ harvest.projects.delete(project)
39
+ end
40
+
41
+ it 'returns true' do
42
+ expect(harvest.projects.all.select do |p|
43
+ p.name == 'Updated Project'
44
+ end).to eql([])
45
+ end
46
+ end
47
+ end
48
+
49
+ describe 'clients' do
50
+ context 'allows activating and deactivating clients' do
51
+ let(:active_project) { create(:project, :active) }
52
+ let(:deactive_project) { create(:project, :deactive) }
53
+
54
+ before do
55
+ allow(harvest.projects).to receive(:deactivate)
56
+ .and_return(deactive_project)
57
+ allow(harvest.projects).to receive(:activate)
58
+ .and_return(active_project)
59
+ end
60
+
61
+ it 'should be active' do
62
+ expect(active_project).to be_active
63
+ end
64
+
65
+ it 'should be deactive' do
66
+ project = harvest.projects.deactivate(project)
67
+ expect(deactive_project).not_to be_active
68
+ end
69
+
70
+ it 'should reactive a project' do
71
+ project = harvest.projects.activate(project)
72
+ expect(active_project).to be_active
73
+ end
74
+ end
75
+ end
76
+ end