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.
- checksums.yaml +7 -0
- data/.document +3 -0
- data/.gitignore +35 -0
- data/.rspec +1 -0
- data/.rubocop.yml +34 -0
- data/.ruby-version +1 -0
- data/.travis.yml +12 -0
- data/Gemfile +20 -0
- data/HISTORY.md +118 -0
- data/MIT-LICENSE +21 -0
- data/README.md +66 -0
- data/Rakefile +24 -0
- data/harvested2.gemspec +30 -0
- data/lib/ext/array.rb +52 -0
- data/lib/ext/date.rb +9 -0
- data/lib/ext/hash.rb +17 -0
- data/lib/ext/time.rb +5 -0
- data/lib/harvest/account.rb +13 -0
- data/lib/harvest/api/account.rb +25 -0
- data/lib/harvest/api/base.rb +72 -0
- data/lib/harvest/api/clients.rb +10 -0
- data/lib/harvest/api/company.rb +12 -0
- data/lib/harvest/api/contacts.rb +9 -0
- data/lib/harvest/api/expense_categories.rb +9 -0
- data/lib/harvest/api/expenses.rb +26 -0
- data/lib/harvest/api/invoice_categories.rb +9 -0
- data/lib/harvest/api/invoice_messages.rb +86 -0
- data/lib/harvest/api/invoice_payments.rb +41 -0
- data/lib/harvest/api/invoices.rb +9 -0
- data/lib/harvest/api/projects.rb +9 -0
- data/lib/harvest/api/task_assignments.rb +75 -0
- data/lib/harvest/api/tasks.rb +9 -0
- data/lib/harvest/api/time_entry.rb +19 -0
- data/lib/harvest/api/user_assignments.rb +75 -0
- data/lib/harvest/api/users.rb +10 -0
- data/lib/harvest/base.rb +333 -0
- data/lib/harvest/behavior/activatable.rb +31 -0
- data/lib/harvest/behavior/crud.rb +80 -0
- data/lib/harvest/client.rb +23 -0
- data/lib/harvest/company.rb +8 -0
- data/lib/harvest/contact.rb +20 -0
- data/lib/harvest/credentials.rb +34 -0
- data/lib/harvest/errors.rb +27 -0
- data/lib/harvest/expense.rb +54 -0
- data/lib/harvest/expense_category.rb +10 -0
- data/lib/harvest/hardy_client.rb +80 -0
- data/lib/harvest/invoice.rb +75 -0
- data/lib/harvest/invoice_category.rb +8 -0
- data/lib/harvest/invoice_message.rb +8 -0
- data/lib/harvest/invoice_payment.rb +8 -0
- data/lib/harvest/line_item.rb +21 -0
- data/lib/harvest/model.rb +133 -0
- data/lib/harvest/project.rb +41 -0
- data/lib/harvest/receipt.rb +12 -0
- data/lib/harvest/task.rb +21 -0
- data/lib/harvest/task_assignment.rb +27 -0
- data/lib/harvest/time_entry.rb +57 -0
- data/lib/harvest/timezones.rb +130 -0
- data/lib/harvest/user.rb +58 -0
- data/lib/harvest/user_assignment.rb +27 -0
- data/lib/harvest/version.rb +3 -0
- data/lib/harvested2.rb +96 -0
- data/spec/factories/client.rb +14 -0
- data/spec/factories/contact.rb +8 -0
- data/spec/factories/expense.rb +10 -0
- data/spec/factories/expenses_category.rb +7 -0
- data/spec/factories/invoice.rb +25 -0
- data/spec/factories/invoice_category.rb +5 -0
- data/spec/factories/invoice_message.rb +9 -0
- data/spec/factories/invoice_payment.rb +7 -0
- data/spec/factories/line_item.rb +9 -0
- data/spec/factories/project.rb +15 -0
- data/spec/factories/task.rb +8 -0
- data/spec/factories/task_assignment.rb +8 -0
- data/spec/factories/time_entry.rb +13 -0
- data/spec/factories/user.rb +19 -0
- data/spec/factories/user_assigment.rb +7 -0
- data/spec/functional/clients_spec.rb +105 -0
- data/spec/functional/errors_spec.rb +42 -0
- data/spec/functional/expenses_spec.rb +97 -0
- data/spec/functional/invoice_messages_spec.rb +48 -0
- data/spec/functional/invoice_payments_spec.rb +51 -0
- data/spec/functional/invoice_spec.rb +138 -0
- data/spec/functional/project_spec.rb +76 -0
- data/spec/functional/tasks_spec.rb +119 -0
- data/spec/functional/time_entries_spec.rb +87 -0
- data/spec/functional/users_spec.rb +72 -0
- data/spec/harvest/base_spec.rb +10 -0
- data/spec/harvest/basic_auth_credentials_spec.rb +12 -0
- data/spec/harvest/expense_category_spec.rb +5 -0
- data/spec/harvest/expense_spec.rb +18 -0
- data/spec/harvest/invoice_message_spec.rb +5 -0
- data/spec/harvest/invoice_payment_spec.rb +5 -0
- data/spec/harvest/invoice_spec.rb +5 -0
- data/spec/harvest/oauth_credentials_spec.rb +11 -0
- data/spec/harvest/project_spec.rb +5 -0
- data/spec/harvest/task_assignment_spec.rb +5 -0
- data/spec/harvest/task_spec.rb +5 -0
- data/spec/harvest/time_entry_spec.rb +23 -0
- data/spec/harvest/user_assignment_spec.rb +5 -0
- data/spec/harvest/user_spec.rb +34 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/support/factory_bot.rb +5 -0
- data/spec/support/harvested_helpers.rb +28 -0
- data/spec/support/json_examples.rb +9 -0
- metadata +238 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'harvest tasks' do
|
|
4
|
+
let(:harvest) { Harvest.client(access_token: 'mytoken', account_id: '123') }
|
|
5
|
+
|
|
6
|
+
describe 'tasks' do
|
|
7
|
+
let(:task) { create(:task) }
|
|
8
|
+
|
|
9
|
+
context 'allows to add invoice tasks' do
|
|
10
|
+
before do
|
|
11
|
+
allow(harvest.tasks).to receive(:create)
|
|
12
|
+
.and_return(task)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'returns true' do
|
|
16
|
+
expect(task.default_hourly_rate).to eql(120)
|
|
17
|
+
expect(task.name).to eql('A crud task')
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
context 'allows to update invoice tasks' do
|
|
22
|
+
before do
|
|
23
|
+
allow(harvest.tasks).to receive(:update)
|
|
24
|
+
.and_return(task)
|
|
25
|
+
task.default_hourly_rate = 140
|
|
26
|
+
task = harvest.tasks.update(task)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'returns true' do
|
|
30
|
+
expect(task.default_hourly_rate).to eql(140)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context 'allows to remove invoice tasks' do
|
|
35
|
+
before do
|
|
36
|
+
allow(harvest.tasks).to receive(:delete).and_return([])
|
|
37
|
+
allow(harvest.tasks).to receive(:all).and_return([])
|
|
38
|
+
harvest.tasks.delete(task)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'returns true' do
|
|
42
|
+
expect(harvest.tasks.all.select do |t|
|
|
43
|
+
t.name == 'A crud task'
|
|
44
|
+
end).to eql([])
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe 'task assignments' do
|
|
50
|
+
let(:project) { create(:project) }
|
|
51
|
+
let(:task) do
|
|
52
|
+
create(:task, name: 'A task for joe', default_hourly_rate: 120)
|
|
53
|
+
end
|
|
54
|
+
let(:task_assignment) do
|
|
55
|
+
create(:task_assignment, project: project, task: task)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
context 'allows to add invoice task assignments' do
|
|
59
|
+
before do
|
|
60
|
+
allow(harvest.task_assignments).to receive(:create)
|
|
61
|
+
.and_return(task)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'returns true' do
|
|
65
|
+
expect(task_assignment.hourly_rate).to eql(120)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
context 'allows to update invoice task assignments' do
|
|
70
|
+
before do
|
|
71
|
+
allow(harvest.task_assignments).to receive(:update)
|
|
72
|
+
.and_return(task)
|
|
73
|
+
task_assignment.hourly_rate = 100
|
|
74
|
+
task = harvest.task_assignments.update(task_assignment)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'returns true' do
|
|
78
|
+
expect(task_assignment.hourly_rate).to eql(100)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
context 'allows to remove invoice task assignments' do
|
|
83
|
+
before do
|
|
84
|
+
allow(harvest.task_assignments).to receive(:delete)
|
|
85
|
+
.and_return([])
|
|
86
|
+
allow(harvest.task_assignments).to receive(:all)
|
|
87
|
+
.and_return([task_assignment])
|
|
88
|
+
harvest.task_assignments.delete(task_assignment)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'returns true' do
|
|
92
|
+
expect(harvest.task_assignments.all(project).count).to eql(1)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe 'projects' do
|
|
98
|
+
let(:project) { create(:project) }
|
|
99
|
+
let(:task) do
|
|
100
|
+
create(:task, name: 'A task for joe', default_hourly_rate: 120)
|
|
101
|
+
end
|
|
102
|
+
let(:task_assignment) do
|
|
103
|
+
create(:task_assignment, project: project, task: task)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
context 'allows to add project and task' do
|
|
107
|
+
before do
|
|
108
|
+
allow(harvest.projects).to receive(:create_task)
|
|
109
|
+
.and_return(task)
|
|
110
|
+
allow(harvest.task_assignments).to receive(:all)
|
|
111
|
+
.and_return([task_assignment])
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it 'returns true' do
|
|
115
|
+
expect(harvest.task_assignments.all(project).size).to eql(1)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'harvest time tracking' do
|
|
4
|
+
let(:harvest) { Harvest.client(access_token: 'mytoken', account_id: '123') }
|
|
5
|
+
|
|
6
|
+
describe 'time entries' do
|
|
7
|
+
let(:time_entry) { create(:time_entry) }
|
|
8
|
+
|
|
9
|
+
context 'allows to add time entry' do
|
|
10
|
+
before do
|
|
11
|
+
allow(harvest.time_entries).to receive(:create)
|
|
12
|
+
.and_return(time_entry)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'returns true' do
|
|
16
|
+
expect(time_entry.notes).to eql('Test api support')
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context 'allows to update time entry' do
|
|
21
|
+
before do
|
|
22
|
+
allow(harvest.time_entries).to receive(:update)
|
|
23
|
+
.and_return(time_entry)
|
|
24
|
+
time_entry.notes = 'New test api support'
|
|
25
|
+
time_entry = harvest.time_entries.update(time_entry)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'returns true' do
|
|
29
|
+
expect(time_entry.notes).to eql('New test api support')
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
context 'allows to remove time entry' do
|
|
34
|
+
before do
|
|
35
|
+
allow(harvest.time_entries).to receive(:delete).and_return([])
|
|
36
|
+
allow(harvest.time_entries).to receive(:all).and_return([])
|
|
37
|
+
harvest.time_entries.delete(time_entry)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'returns true' do
|
|
41
|
+
expect(harvest.time_entries.all.select do |t|
|
|
42
|
+
t.notes == 'New test api support'
|
|
43
|
+
end).to eql([])
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
describe 'toggle' do
|
|
49
|
+
let(:time_entry) { create(:time_entry, :started) }
|
|
50
|
+
|
|
51
|
+
before do
|
|
52
|
+
allow(harvest.time_entries).to receive(:toggle)
|
|
53
|
+
.and_return(time_entry)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'should be active' do
|
|
57
|
+
expect(time_entry.timer_started_at).not_to be_nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'should be deactive' do
|
|
61
|
+
time_entry.timer_started_at = nil
|
|
62
|
+
time_entry = harvest.time_entries.toggle(time_entry)
|
|
63
|
+
expect(time_entry.timer_started_at).to be_nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'should reactive a time_entry' do
|
|
67
|
+
time_entry = harvest.time_entries.toggle(time_entry)
|
|
68
|
+
expect(time_entry).not_to be_nil
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe 'projects' do
|
|
73
|
+
let(:user) { create(:user) }
|
|
74
|
+
let(:time_entry) { create(:time_entry) }
|
|
75
|
+
|
|
76
|
+
context 'allows to add project and task' do
|
|
77
|
+
before do
|
|
78
|
+
allow(harvest.time_entries).to receive(:trackable_projects)
|
|
79
|
+
.and_return([])
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'returns true' do
|
|
83
|
+
expect(harvest.time_entries.trackable_projects(Time.now, user)).to eql([])
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'harvest users' do
|
|
4
|
+
let(:harvest) { Harvest.client(access_token: 'mytoken', account_id: '123') }
|
|
5
|
+
|
|
6
|
+
describe 'users' do
|
|
7
|
+
let(:user) { create(:user) }
|
|
8
|
+
|
|
9
|
+
context 'allows to add users' do
|
|
10
|
+
before do
|
|
11
|
+
allow(harvest.users).to receive(:create).and_return(user)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'returns true' do
|
|
15
|
+
expect(user.id).not_to be_nil
|
|
16
|
+
expect(user.first_name).to eql('Edgar')
|
|
17
|
+
expect(user.last_name).to eql('Ruth')
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
context 'allows to update users' do
|
|
22
|
+
before do
|
|
23
|
+
allow(harvest.users).to receive(:update).and_return(user)
|
|
24
|
+
user.first_name = 'Joey'
|
|
25
|
+
user = harvest.users.update(user)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'returns true' do
|
|
29
|
+
expect(user.first_name).to eql('Joey')
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
context 'allows to remove users' do
|
|
34
|
+
before do
|
|
35
|
+
allow(harvest.users).to receive(:delete).and_return([])
|
|
36
|
+
allow(harvest.users).to receive(:all).and_return([])
|
|
37
|
+
harvest.users.delete(user)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'returns true' do
|
|
41
|
+
expect(harvest.users.all.select do |u|
|
|
42
|
+
u.first_name == 'Joey'
|
|
43
|
+
end).to eql([])
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
context 'allows activating and deactivating users' do
|
|
48
|
+
let(:user) { create(:user, :active) }
|
|
49
|
+
|
|
50
|
+
before do
|
|
51
|
+
allow(harvest.users).to receive(:update)
|
|
52
|
+
.and_return(user)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'should be active' do
|
|
56
|
+
expect(user).to be_active
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'should be deactive' do
|
|
60
|
+
user.is_active = false
|
|
61
|
+
user = harvest.users.update(user)
|
|
62
|
+
expect(user).not_to be_active
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'should reactive a user' do
|
|
66
|
+
user.is_active = true
|
|
67
|
+
user = harvest.users.update(user)
|
|
68
|
+
expect(user).to be_active
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Harvest::BasicAuthCredentials do
|
|
4
|
+
context '#set_authentication' do
|
|
5
|
+
it 'should set the headers Authorization' do
|
|
6
|
+
credentials = Harvest::BasicAuthCredentials.new(access_token: 'mytoken', account_id: '1')
|
|
7
|
+
credentials.set_authentication(options = {})
|
|
8
|
+
|
|
9
|
+
expect(options[:headers]['Authorization']).to eql('Bearer mytoken')
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Harvest::Expense do
|
|
4
|
+
it_behaves_like 'a json sanitizer', %w(cache_version)
|
|
5
|
+
|
|
6
|
+
describe "#spent_date" do
|
|
7
|
+
it "should parse strings" do
|
|
8
|
+
date = RUBY_VERSION =~ /1.8/ ? "12/01/2009" : "01/12/2009"
|
|
9
|
+
expense = Harvest::Expense.new(:spent_date => date)
|
|
10
|
+
expense.spent_date.should == Date.parse(date)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "should accept times" do
|
|
14
|
+
expense = Harvest::Expense.new(:spent_date => Time.utc(2009, 12, 1))
|
|
15
|
+
expense.spent_date.should == Date.new(2009, 12, 1)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Harvest::OAuthCredentials do
|
|
4
|
+
context '#set_authentication' do
|
|
5
|
+
it "should set the access token in the query" do
|
|
6
|
+
credentials = Harvest::OAuthCredentials.new(access_token: 'theaccesstoken')
|
|
7
|
+
credentials.set_authentication(options = {})
|
|
8
|
+
options[:query]['access_token'].should == 'theaccesstoken'
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Harvest::TimeEntry do
|
|
4
|
+
context '#as_json' do
|
|
5
|
+
it 'builds a specialized hash' do
|
|
6
|
+
entry = Harvest::TimeEntry.new(:notes => 'the notes', :project_id => 'the project id', :task_id => 'the task id', :hours => 'the hours', :spent_date => Time.utc(2009, 12, 28))
|
|
7
|
+
entry.as_json.should == {"notes" => "the notes", "hours" => "the hours", "project_id" => "the project id", "task_id" => "the task id", "spent_date" => "2009-12-28"}
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
context "#spent_date" do
|
|
12
|
+
it "should parse strings" do
|
|
13
|
+
date = RUBY_VERSION =~ /1.8/ ? "12/01/2009" : "01/12/2009"
|
|
14
|
+
entry = Harvest::TimeEntry.new(:spent_date => date)
|
|
15
|
+
entry.spent_date.should == Date.parse(date)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "should accept times" do
|
|
19
|
+
entry = Harvest::TimeEntry.new(:spent_date => Time.utc(2009, 12, 1))
|
|
20
|
+
entry.spent_date.should == Date.new(2009, 12, 1)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Harvest::User do
|
|
4
|
+
it_behaves_like 'a json sanitizer', %w(cache_version)
|
|
5
|
+
|
|
6
|
+
describe "#timezone" do
|
|
7
|
+
it "should convert friendly timezone setting" do
|
|
8
|
+
p = Harvest::User.new(:timezone => :cst)
|
|
9
|
+
p.timezone.should == 'Central Time (US & Canada)'
|
|
10
|
+
|
|
11
|
+
p = Harvest::User.new(:timezone => :est)
|
|
12
|
+
p.timezone.should == 'Eastern Time (US & Canada)'
|
|
13
|
+
|
|
14
|
+
p = Harvest::User.new(:timezone => :mst)
|
|
15
|
+
p.timezone.should == 'Mountain Time (US & Canada)'
|
|
16
|
+
|
|
17
|
+
p = Harvest::User.new(:timezone => :pst)
|
|
18
|
+
p.timezone.should == 'Pacific Time (US & Canada)'
|
|
19
|
+
|
|
20
|
+
p = Harvest::User.new(:timezone => 'pst')
|
|
21
|
+
p.timezone.should == 'Pacific Time (US & Canada)'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "should convert standard zones" do
|
|
25
|
+
p = Harvest::User.new(:timezone => 'america/chicago')
|
|
26
|
+
p.timezone.should == 'Central Time (US & Canada)'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "should leave literal zones" do
|
|
30
|
+
p = Harvest::User.new(:timezone => 'Central Time (US & Canada)')
|
|
31
|
+
p.timezone.should == 'Central Time (US & Canada)'
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|