quilted-harvested 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +3 -0
- data/.gitignore +25 -0
- data/HISTORY +19 -0
- data/MIT-LICENSE +20 -0
- data/README.md +68 -0
- data/Rakefile +25 -0
- data/VERSION +1 -0
- data/examples/basics.rb +35 -0
- data/examples/clear_account.rb +28 -0
- data/examples/task_assignments.rb +27 -0
- data/examples/user_assignments.rb +24 -0
- data/features/account.feature +7 -0
- data/features/client_contacts.feature +23 -0
- data/features/clients.feature +29 -0
- data/features/errors.feature +25 -0
- data/features/expense_categories.feature +21 -0
- data/features/expenses.feature +55 -0
- data/features/hardy_client.feature +40 -0
- data/features/projects.feature +39 -0
- data/features/reporting.feature +72 -0
- data/features/step_definitions/account_steps.rb +7 -0
- data/features/step_definitions/assignment_steps.rb +100 -0
- data/features/step_definitions/contact_steps.rb +11 -0
- data/features/step_definitions/debug_steps.rb +3 -0
- data/features/step_definitions/error_steps.rb +113 -0
- data/features/step_definitions/expenses_steps.rb +46 -0
- data/features/step_definitions/harvest_steps.rb +8 -0
- data/features/step_definitions/model_steps.rb +90 -0
- data/features/step_definitions/people_steps.rb +4 -0
- data/features/step_definitions/report_steps.rb +91 -0
- data/features/step_definitions/time_entry_steps.rb +40 -0
- data/features/support/env.rb +37 -0
- data/features/support/error_helpers.rb +18 -0
- data/features/support/fixtures/empty_clients.xml +2 -0
- data/features/support/fixtures/over_limit.xml +8 -0
- data/features/support/fixtures/receipt.png +0 -0
- data/features/support/fixtures/under_limit.xml +8 -0
- data/features/support/harvest_credentials.example.yml +4 -0
- data/features/support/harvest_helpers.rb +11 -0
- data/features/support/inflections.rb +9 -0
- data/features/task_assignment.feature +69 -0
- data/features/tasks.feature +25 -0
- data/features/time_tracking.feature +29 -0
- data/features/user_assignments.feature +33 -0
- data/features/users.feature +55 -0
- data/harvested.gemspec +159 -0
- data/lib/harvest/api/account.rb +15 -0
- data/lib/harvest/api/base.rb +42 -0
- data/lib/harvest/api/clients.rb +10 -0
- data/lib/harvest/api/contacts.rb +19 -0
- data/lib/harvest/api/expense_categories.rb +9 -0
- data/lib/harvest/api/expenses.rb +28 -0
- data/lib/harvest/api/projects.rb +54 -0
- data/lib/harvest/api/reports.rb +39 -0
- data/lib/harvest/api/task_assignments.rb +32 -0
- data/lib/harvest/api/tasks.rb +9 -0
- data/lib/harvest/api/time.rb +32 -0
- data/lib/harvest/api/user_assignments.rb +32 -0
- data/lib/harvest/api/users.rb +21 -0
- data/lib/harvest/base.rb +258 -0
- data/lib/harvest/base_model.rb +73 -0
- data/lib/harvest/behavior/activatable.rb +31 -0
- data/lib/harvest/behavior/crud.rb +57 -0
- data/lib/harvest/client.rb +30 -0
- data/lib/harvest/contact.rb +29 -0
- data/lib/harvest/credentials.rb +21 -0
- data/lib/harvest/errors.rb +23 -0
- data/lib/harvest/expense.rb +19 -0
- data/lib/harvest/expense_category.rb +18 -0
- data/lib/harvest/hardy_client.rb +80 -0
- data/lib/harvest/project.rb +56 -0
- data/lib/harvest/rate_limit_status.rb +28 -0
- data/lib/harvest/task.rb +30 -0
- data/lib/harvest/task_assignment.rb +34 -0
- data/lib/harvest/time_entry.rb +42 -0
- data/lib/harvest/timezones.rb +149 -0
- data/lib/harvest/user.rb +66 -0
- data/lib/harvest/user_assignment.rb +34 -0
- data/lib/harvested.rb +62 -0
- data/spec/harvest/base_spec.rb +9 -0
- data/spec/harvest/credentials_spec.rb +22 -0
- data/spec/harvest/expense_spec.rb +15 -0
- data/spec/harvest/task_assignment_spec.rb +10 -0
- data/spec/harvest/time_entry_spec.rb +22 -0
- data/spec/harvest/user_assignment_spec.rb +10 -0
- data/spec/harvest/user_spec.rb +32 -0
- data/spec/spec.default.opts +1 -0
- data/spec/spec_helper.rb +10 -0
- metadata +264 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
@clean
|
2
|
+
Feature: Task Assignments
|
3
|
+
|
4
|
+
Scenario: Adding and Updating a Task on a Project
|
5
|
+
Given I am using the credentials from "./support/harvest_credentials.yml"
|
6
|
+
When I create a client with the following:
|
7
|
+
| name | Client Projects |
|
8
|
+
| details | Building API Widgets across the country |
|
9
|
+
When I create a project for the client "Client Projects" with the following:
|
10
|
+
| name | Test Project |
|
11
|
+
| active | true |
|
12
|
+
| notes | project to test the api |
|
13
|
+
Then there should be a project "Test Project"
|
14
|
+
When I create a task with the following:
|
15
|
+
| name | Test Task |
|
16
|
+
| billable | true |
|
17
|
+
| deactivated | false |
|
18
|
+
| hourly_rate | 120 |
|
19
|
+
Then there should be a task "Test Task"
|
20
|
+
When I assign the task "Test Task" to the project "Test Project"
|
21
|
+
Then the task "Test Task" should be assigned to the project "Test Project"
|
22
|
+
When I update the task "Test Task" for the project "Test Project" with the following:
|
23
|
+
| hourly_rate | 75 |
|
24
|
+
| billable | false |
|
25
|
+
Then the task "Test Task" for the project "Test Project" should have the following attributes:
|
26
|
+
| hourly_rate | 75.0 |
|
27
|
+
| billable? | false |
|
28
|
+
| active? | true |
|
29
|
+
|
30
|
+
Scenario: Removing Tasks on a project
|
31
|
+
Given I am using the credentials from "./support/harvest_credentials.yml"
|
32
|
+
When I create a client with the following:
|
33
|
+
| name | Client Projects |
|
34
|
+
| details | Building API Widgets across the country |
|
35
|
+
When I create a project for the client "Client Projects" with the following:
|
36
|
+
| name | Test Project |
|
37
|
+
| active | true |
|
38
|
+
| notes | project to test the api |
|
39
|
+
Then there should be a project "Test Project"
|
40
|
+
When I create a task with the following:
|
41
|
+
| name | Test Task1 |
|
42
|
+
When I create a task with the following:
|
43
|
+
| name | Test Task2 |
|
44
|
+
Then there should be a task "Test Task1"
|
45
|
+
Then there should be a task "Test Task2"
|
46
|
+
When I assign the task "Test Task1" to the project "Test Project"
|
47
|
+
When I assign the task "Test Task2" to the project "Test Project"
|
48
|
+
Then the task "Test Task1" should be assigned to the project "Test Project"
|
49
|
+
Then the task "Test Task2" should be assigned to the project "Test Project"
|
50
|
+
When I remove the task "Test Task1" from the project "Test Project"
|
51
|
+
Then the task "Test Task1" should not be assigned to the project "Test Project"
|
52
|
+
When I try to remove the task "Test Task2" from the project "Test Project"
|
53
|
+
Then a 400 error should be raised
|
54
|
+
|
55
|
+
Scenario: Removing a task that has recorded hours
|
56
|
+
|
57
|
+
Scenario: Creating a task and immediately assigning it
|
58
|
+
Given I am using the credentials from "./support/harvest_credentials.yml"
|
59
|
+
When I create a client with the following:
|
60
|
+
| name | Client Projects |
|
61
|
+
| details | Building API Widgets across the country |
|
62
|
+
When I create a project for the client "Client Projects" with the following:
|
63
|
+
| name | Test Project |
|
64
|
+
| active | true |
|
65
|
+
| notes | project to test the api |
|
66
|
+
Then there should be a project "Test Project"
|
67
|
+
When I create and assign a task "Created Task" to the project "Test Project"
|
68
|
+
Then there should be a task "Created Task"
|
69
|
+
Then the task "Created Task" should be assigned to the project "Test Project"
|
@@ -0,0 +1,25 @@
|
|
1
|
+
@clean
|
2
|
+
Feature: Managing Tasks
|
3
|
+
|
4
|
+
Scenario: Adding, Updating, and Removing a Task
|
5
|
+
Given I am using the credentials from "./support/harvest_credentials.yml"
|
6
|
+
When I create a task with the following:
|
7
|
+
| name | Test Task |
|
8
|
+
| billable | true |
|
9
|
+
| deactivated | false |
|
10
|
+
| hourly_rate | 120 |
|
11
|
+
Then there should be a task "Test Task"
|
12
|
+
When I update the task "Test Task" with the following:
|
13
|
+
| name | Updated Task |
|
14
|
+
| hourly_rate | 100.0 |
|
15
|
+
| deactivated | true |
|
16
|
+
| billable | false |
|
17
|
+
| default | true |
|
18
|
+
Then the task "Updated Task" should have the following attributes:
|
19
|
+
| hourly_rate | 100.0 |
|
20
|
+
| deactivated | true |
|
21
|
+
| active? | false |
|
22
|
+
| billable | false |
|
23
|
+
| default? | true |
|
24
|
+
When I delete the task "Updated Task"
|
25
|
+
Then there should not be a task "Updated Task"
|
@@ -0,0 +1,29 @@
|
|
1
|
+
@clean
|
2
|
+
Feature: Time Tracking
|
3
|
+
|
4
|
+
Scenario: Adding, Updating, and Deleting Time Entries
|
5
|
+
Given I am using the credentials from "./support/harvest_credentials.yml"
|
6
|
+
And I create a client with the following:
|
7
|
+
| name | Time Client |
|
8
|
+
| details | Client to track time against |
|
9
|
+
And I create a project for the client "Time Client" with the following:
|
10
|
+
| name | Time Project |
|
11
|
+
| active | true |
|
12
|
+
And I create a task with the following:
|
13
|
+
| name | Time Task |
|
14
|
+
| billable | true |
|
15
|
+
| hourly_rate | 120 |
|
16
|
+
And I assign the task "Time Task" to the project "Time Project"
|
17
|
+
When I create a time entry for the project "Time Project" and the task "Time Task" with the following:
|
18
|
+
| notes | Test api support |
|
19
|
+
| hours | 3.0 |
|
20
|
+
| spent_at | 12/28/2009 |
|
21
|
+
Then there should be a time entry "Test api support" on "12/28/2009"
|
22
|
+
When I update the time entry "Test api support" on "12/28/2009" with the following:
|
23
|
+
| hours | 4.0 |
|
24
|
+
Then the time entry "Test api support" on "12/28/2009" should have the following attributes:
|
25
|
+
| hours | 4.0 |
|
26
|
+
When I delete the time entry "Test api support" on "12/28/2009"
|
27
|
+
Then there should not be a time entry "Test api support" on "12/28/2009"
|
28
|
+
|
29
|
+
Scenario: Toggling Timers
|
@@ -0,0 +1,33 @@
|
|
1
|
+
@clean
|
2
|
+
Feature: User Assignments
|
3
|
+
|
4
|
+
Scenario: Adding and Updating a User on a Project
|
5
|
+
Given I am using the credentials from "./support/harvest_credentials.yml"
|
6
|
+
When I create a client with the following:
|
7
|
+
| name | Client Projects |
|
8
|
+
| details | Building API Widgets across the country |
|
9
|
+
When I create a project for the client "Client Projects" with the following:
|
10
|
+
| name | Test Project |
|
11
|
+
| active | true |
|
12
|
+
| notes | project to test the api |
|
13
|
+
Then there should be a project "Test Project"
|
14
|
+
When I create a user with the following:
|
15
|
+
| first_name | Edgar |
|
16
|
+
| last_name | Ruth |
|
17
|
+
| email | edgar@ruth.com |
|
18
|
+
| password | mypassword |
|
19
|
+
| password_confirmation | mypassword |
|
20
|
+
Then there should be a user "edgar@ruth.com"
|
21
|
+
When I assign the user "edgar@ruth.com" to the project "Test Project"
|
22
|
+
Then the user "edgar@ruth.com" should be assigned to the project "Test Project"
|
23
|
+
When I update the user "edgar@ruth.com" on the project "Test Project" with the following:
|
24
|
+
| hourly_rate | 50.0 |
|
25
|
+
| project_manager | true |
|
26
|
+
Then the user "edgar@ruth.com" on the project "Test Project" should have the following attributes:
|
27
|
+
| active? | true |
|
28
|
+
| hourly_rate | 50.0 |
|
29
|
+
| project_manager | true |
|
30
|
+
When I remove the user "edgar@ruth.com" from the project "Test Project"
|
31
|
+
Then the user "edgar@ruth.com" should not be assigned to the project "Test Project"
|
32
|
+
|
33
|
+
Scenario: Removing a user from a project that has recorded hours
|
@@ -0,0 +1,55 @@
|
|
1
|
+
@clean
|
2
|
+
Feature: Managing People
|
3
|
+
|
4
|
+
Scenario: Adding and Removing a User
|
5
|
+
Given I am using the credentials from "./support/harvest_credentials.yml"
|
6
|
+
When I create a user with the following:
|
7
|
+
| first_name | Edgar |
|
8
|
+
| last_name | Ruth |
|
9
|
+
| email | edgar@ruth.com |
|
10
|
+
| password | mypassword |
|
11
|
+
| password_confirmation | mypassword |
|
12
|
+
| timezone | cst |
|
13
|
+
| admin | false |
|
14
|
+
| telephone | 444-4444 |
|
15
|
+
Then there should be a user "edgar@ruth.com"
|
16
|
+
When I update the user "edgar@ruth.com" with the following:
|
17
|
+
| first_name | Jonah |
|
18
|
+
| timezone | pst |
|
19
|
+
Then the user "edgar@ruth.com" should have the following attributes:
|
20
|
+
| first_name | Jonah |
|
21
|
+
| timezone | Pacific Time (US & Canada) |
|
22
|
+
When I delete the user "edgar@ruth.com"
|
23
|
+
Then there should not be a user "edgar@ruth.com"
|
24
|
+
|
25
|
+
Scenario: Activating and Deactivating a User
|
26
|
+
Given I am using the credentials from "./support/harvest_credentials.yml"
|
27
|
+
Then I create a user with the following:
|
28
|
+
| first_name | Simon |
|
29
|
+
| last_name | Steel |
|
30
|
+
| email | simon@steel.com |
|
31
|
+
| password | mypassword |
|
32
|
+
| password_confirmation | mypassword |
|
33
|
+
| timezone | cst |
|
34
|
+
| admin | false |
|
35
|
+
| telephone | 444-4444 |
|
36
|
+
Then the user "simon@steel.com" should be activated
|
37
|
+
When I deactivate the user "simon@steel.com"
|
38
|
+
Then the user "simon@steel.com" should be deactivated
|
39
|
+
When I activate the user "simon@steel.com"
|
40
|
+
Then the user "simon@steel.com" should be activated
|
41
|
+
Then I delete the user "simon@steel.com"
|
42
|
+
|
43
|
+
Scenario: Resetting a User's password
|
44
|
+
Given I am using the credentials from "./support/harvest_credentials.yml"
|
45
|
+
Then I create a user with the following:
|
46
|
+
| first_name | Edgar |
|
47
|
+
| last_name | Ruth |
|
48
|
+
| email | edgar@ruth.com |
|
49
|
+
| password | mypassword |
|
50
|
+
| password_confirmation | mypassword |
|
51
|
+
| timezone | cst |
|
52
|
+
| admin | false |
|
53
|
+
| telephone | 444-4444 |
|
54
|
+
Then there should be a user "edgar@ruth.com"
|
55
|
+
Then I reset the password of "edgar@ruth.com"
|
data/harvested.gemspec
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{harvested}
|
8
|
+
s.version = "0.3.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Zach Moazeni, Michelle Moon Lee (updated)"]
|
12
|
+
s.date = %q{2010-10-20}
|
13
|
+
s.description = %q{Harvested wraps the Harvest API concisely without the use of Rails dependencies. More information about the Harvest API can be found on their website (http://www.getharvest.com/api). For support hit up the Mailing List (http://groups.google.com/group/harvested)}
|
14
|
+
s.email = %q{michelle@quilted.coop}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.md"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".document",
|
20
|
+
".gitignore",
|
21
|
+
"HISTORY",
|
22
|
+
"MIT-LICENSE",
|
23
|
+
"README.md",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"examples/basics.rb",
|
27
|
+
"examples/clear_account.rb",
|
28
|
+
"examples/task_assignments.rb",
|
29
|
+
"examples/user_assignments.rb",
|
30
|
+
"features/account.feature",
|
31
|
+
"features/client_contacts.feature",
|
32
|
+
"features/clients.feature",
|
33
|
+
"features/errors.feature",
|
34
|
+
"features/expense_categories.feature",
|
35
|
+
"features/expenses.feature",
|
36
|
+
"features/hardy_client.feature",
|
37
|
+
"features/projects.feature",
|
38
|
+
"features/reporting.feature",
|
39
|
+
"features/step_definitions/account_steps.rb",
|
40
|
+
"features/step_definitions/assignment_steps.rb",
|
41
|
+
"features/step_definitions/contact_steps.rb",
|
42
|
+
"features/step_definitions/debug_steps.rb",
|
43
|
+
"features/step_definitions/error_steps.rb",
|
44
|
+
"features/step_definitions/expenses_steps.rb",
|
45
|
+
"features/step_definitions/harvest_steps.rb",
|
46
|
+
"features/step_definitions/model_steps.rb",
|
47
|
+
"features/step_definitions/people_steps.rb",
|
48
|
+
"features/step_definitions/report_steps.rb",
|
49
|
+
"features/step_definitions/time_entry_steps.rb",
|
50
|
+
"features/support/env.rb",
|
51
|
+
"features/support/error_helpers.rb",
|
52
|
+
"features/support/fixtures/empty_clients.xml",
|
53
|
+
"features/support/fixtures/over_limit.xml",
|
54
|
+
"features/support/fixtures/receipt.png",
|
55
|
+
"features/support/fixtures/under_limit.xml",
|
56
|
+
"features/support/harvest_credentials.example.yml",
|
57
|
+
"features/support/harvest_helpers.rb",
|
58
|
+
"features/support/inflections.rb",
|
59
|
+
"features/task_assignment.feature",
|
60
|
+
"features/tasks.feature",
|
61
|
+
"features/time_tracking.feature",
|
62
|
+
"features/user_assignments.feature",
|
63
|
+
"features/users.feature",
|
64
|
+
"harvested.gemspec",
|
65
|
+
"lib/harvest/api/account.rb",
|
66
|
+
"lib/harvest/api/base.rb",
|
67
|
+
"lib/harvest/api/clients.rb",
|
68
|
+
"lib/harvest/api/contacts.rb",
|
69
|
+
"lib/harvest/api/expense_categories.rb",
|
70
|
+
"lib/harvest/api/expenses.rb",
|
71
|
+
"lib/harvest/api/projects.rb",
|
72
|
+
"lib/harvest/api/reports.rb",
|
73
|
+
"lib/harvest/api/task_assignments.rb",
|
74
|
+
"lib/harvest/api/tasks.rb",
|
75
|
+
"lib/harvest/api/time.rb",
|
76
|
+
"lib/harvest/api/user_assignments.rb",
|
77
|
+
"lib/harvest/api/users.rb",
|
78
|
+
"lib/harvest/base.rb",
|
79
|
+
"lib/harvest/base_model.rb",
|
80
|
+
"lib/harvest/behavior/activatable.rb",
|
81
|
+
"lib/harvest/behavior/crud.rb",
|
82
|
+
"lib/harvest/client.rb",
|
83
|
+
"lib/harvest/contact.rb",
|
84
|
+
"lib/harvest/credentials.rb",
|
85
|
+
"lib/harvest/errors.rb",
|
86
|
+
"lib/harvest/expense.rb",
|
87
|
+
"lib/harvest/expense_category.rb",
|
88
|
+
"lib/harvest/hardy_client.rb",
|
89
|
+
"lib/harvest/project.rb",
|
90
|
+
"lib/harvest/rate_limit_status.rb",
|
91
|
+
"lib/harvest/task.rb",
|
92
|
+
"lib/harvest/task_assignment.rb",
|
93
|
+
"lib/harvest/time_entry.rb",
|
94
|
+
"lib/harvest/timezones.rb",
|
95
|
+
"lib/harvest/user.rb",
|
96
|
+
"lib/harvest/user_assignment.rb",
|
97
|
+
"lib/harvested.rb",
|
98
|
+
"spec/harvest/base_spec.rb",
|
99
|
+
"spec/harvest/credentials_spec.rb",
|
100
|
+
"spec/harvest/expense_spec.rb",
|
101
|
+
"spec/harvest/task_assignment_spec.rb",
|
102
|
+
"spec/harvest/time_entry_spec.rb",
|
103
|
+
"spec/harvest/user_assignment_spec.rb",
|
104
|
+
"spec/harvest/user_spec.rb",
|
105
|
+
"spec/spec.default.opts",
|
106
|
+
"spec/spec_helper.rb"
|
107
|
+
]
|
108
|
+
s.homepage = %q{http://github.com/quilted/harvested}
|
109
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
110
|
+
s.require_paths = ["lib"]
|
111
|
+
s.rubygems_version = %q{1.3.7}
|
112
|
+
s.summary = %q{A Ruby Wrapper for the Harvest API http://www.getharvest.com/}
|
113
|
+
s.test_files = [
|
114
|
+
"spec/harvest/base_spec.rb",
|
115
|
+
"spec/harvest/credentials_spec.rb",
|
116
|
+
"spec/harvest/expense_spec.rb",
|
117
|
+
"spec/harvest/task_assignment_spec.rb",
|
118
|
+
"spec/harvest/time_entry_spec.rb",
|
119
|
+
"spec/harvest/user_assignment_spec.rb",
|
120
|
+
"spec/harvest/user_spec.rb",
|
121
|
+
"spec/spec_helper.rb",
|
122
|
+
"examples/basics.rb",
|
123
|
+
"examples/clear_account.rb",
|
124
|
+
"examples/task_assignments.rb",
|
125
|
+
"examples/user_assignments.rb"
|
126
|
+
]
|
127
|
+
|
128
|
+
if s.respond_to? :specification_version then
|
129
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
130
|
+
s.specification_version = 3
|
131
|
+
|
132
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
133
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
134
|
+
s.add_development_dependency(%q<cucumber>, [">= 0"])
|
135
|
+
s.add_development_dependency(%q<ruby-debug>, [">= 0"])
|
136
|
+
s.add_development_dependency(%q<fakeweb>, [">= 0"])
|
137
|
+
s.add_runtime_dependency(%q<httparty>, [">= 0"])
|
138
|
+
s.add_runtime_dependency(%q<happymapper>, [">= 0"])
|
139
|
+
s.add_runtime_dependency(%q<builder>, [">= 0"])
|
140
|
+
else
|
141
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
142
|
+
s.add_dependency(%q<cucumber>, [">= 0"])
|
143
|
+
s.add_dependency(%q<ruby-debug>, [">= 0"])
|
144
|
+
s.add_dependency(%q<fakeweb>, [">= 0"])
|
145
|
+
s.add_dependency(%q<httparty>, [">= 0"])
|
146
|
+
s.add_dependency(%q<happymapper>, [">= 0"])
|
147
|
+
s.add_dependency(%q<builder>, [">= 0"])
|
148
|
+
end
|
149
|
+
else
|
150
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
151
|
+
s.add_dependency(%q<cucumber>, [">= 0"])
|
152
|
+
s.add_dependency(%q<ruby-debug>, [">= 0"])
|
153
|
+
s.add_dependency(%q<fakeweb>, [">= 0"])
|
154
|
+
s.add_dependency(%q<httparty>, [">= 0"])
|
155
|
+
s.add_dependency(%q<happymapper>, [">= 0"])
|
156
|
+
s.add_dependency(%q<builder>, [">= 0"])
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Harvest
|
2
|
+
module API
|
3
|
+
|
4
|
+
# API Methods to contain all account actions
|
5
|
+
class Account < Base
|
6
|
+
|
7
|
+
# Returns the current rate limit information
|
8
|
+
# @return [Harvest::RateLimitStatus]
|
9
|
+
def rate_limit_status
|
10
|
+
response = request(:get, credentials, '/account/rate_limit_status')
|
11
|
+
Harvest::RateLimitStatus.parse(response.body)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Harvest
|
2
|
+
module API
|
3
|
+
class Base
|
4
|
+
attr_reader :credentials
|
5
|
+
|
6
|
+
def initialize(credentials)
|
7
|
+
@credentials = credentials
|
8
|
+
end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def api_model(klass)
|
12
|
+
class_eval <<-END
|
13
|
+
def api_model
|
14
|
+
#{klass}
|
15
|
+
end
|
16
|
+
END
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
def request(method, credentials, path, options = {})
|
22
|
+
response = HTTParty.send(method, "#{credentials.host}#{path}", :query => options[:query], :body => options[:body], :headers => {"Accept" => "application/xml", "Content-Type" => "application/xml; charset=utf-8", "Authorization" => "Basic #{credentials.basic_auth}", "User-Agent" => "Harvestable/#{Harvest::VERSION}"}.update(options[:headers] || {}), :format => :plain)
|
23
|
+
case response.code
|
24
|
+
when 200..201
|
25
|
+
response
|
26
|
+
when 400
|
27
|
+
raise Harvest::BadRequest.new(response)
|
28
|
+
when 404
|
29
|
+
raise Harvest::NotFound.new(response)
|
30
|
+
when 500
|
31
|
+
raise Harvest::ServerError.new(response)
|
32
|
+
when 502
|
33
|
+
raise Harvest::Unavailable.new(response)
|
34
|
+
when 503
|
35
|
+
raise Harvest::RateLimited.new(response)
|
36
|
+
else
|
37
|
+
raise Harvest::InformHarvest.new(response)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Harvest
|
2
|
+
module API
|
3
|
+
class Contacts < Base
|
4
|
+
api_model Harvest::Contact
|
5
|
+
|
6
|
+
include Harvest::Behavior::Crud
|
7
|
+
|
8
|
+
def all(client_id = nil)
|
9
|
+
response = if client_id
|
10
|
+
request(:get, credentials, "/clients/#{client_id}/contacts")
|
11
|
+
else
|
12
|
+
request(:get, credentials, "/contacts")
|
13
|
+
end
|
14
|
+
|
15
|
+
api_model.parse(response.body)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Harvest
|
2
|
+
module API
|
3
|
+
class Expenses < Base
|
4
|
+
api_model Harvest::Expense
|
5
|
+
|
6
|
+
include Harvest::Behavior::Crud
|
7
|
+
|
8
|
+
def all(date = ::Time.now)
|
9
|
+
date = ::Time.parse(date) if String === date
|
10
|
+
response = request(:get, credentials, "#{api_model.api_path}/#{date.yday}/#{date.year}")
|
11
|
+
api_model.parse(response.body)
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
# This is currently broken, but will come back to it
|
16
|
+
def attach(expense, filename, receipt)
|
17
|
+
body = ""
|
18
|
+
body << "------------------------------b7edea381b46\r\n"
|
19
|
+
body << %Q{Content-Disposition: form-data; name="expense[receipt]"; filename="#{filename}"\r\n}
|
20
|
+
body << "Content-Type: image/png\r\n"
|
21
|
+
body << "\r\n#{receipt.read}\r\n"
|
22
|
+
body << "------------------------------b7edea381b46\r\n"
|
23
|
+
|
24
|
+
request(:post, credentials, "#{api_model.api_path}/#{expense.to_i}/receipt", :headers => {'Content-Type' => 'multipart/form-data; boundary=------------------------------b7edea381b46'}, :body => body)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Harvest
|
2
|
+
module API
|
3
|
+
class Projects < Base
|
4
|
+
api_model Harvest::Project
|
5
|
+
|
6
|
+
include Harvest::Behavior::Crud
|
7
|
+
|
8
|
+
# Creates and Assigns a task to the project
|
9
|
+
#
|
10
|
+
# == Examples
|
11
|
+
# project = harvest.projects.find(401)
|
12
|
+
# harvest.projects.create_task(project, 'Bottling Glue') # creates and assigns a task to the project
|
13
|
+
#
|
14
|
+
# @return [Harvest::Project]
|
15
|
+
def create_task(project, task_name)
|
16
|
+
response = request(:post, credentials, "/projects/#{project.to_i}/task_assignments/add_with_create_new_task", :body => task_xml(task_name))
|
17
|
+
id = response.headers["location"].first.match(/\/.*\/(\d+)\/.*\/(\d+)/)[1]
|
18
|
+
find(id)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Deactivates the project. Does nothing if the project is already deactivated
|
22
|
+
#
|
23
|
+
# @param [Harvest::Project] project the project you want to deactivate
|
24
|
+
# @return [Harvest::Project] the deactivated project
|
25
|
+
def deactivate(project)
|
26
|
+
if project.active?
|
27
|
+
request(:put, credentials, "#{api_model.api_path}/#{project.to_i}/toggle", :headers => {'Content-Length' => '0'})
|
28
|
+
project.active = false
|
29
|
+
end
|
30
|
+
project
|
31
|
+
end
|
32
|
+
|
33
|
+
# Activates the project. Does nothing if the project is already activated
|
34
|
+
#
|
35
|
+
# @param [Harvest::Project] project the project you want to activate
|
36
|
+
# @return [Harvest::Project] the activated project
|
37
|
+
def activate(project)
|
38
|
+
if !project.active?
|
39
|
+
request(:put, credentials, "#{api_model.api_path}/#{project.to_i}/toggle", :headers => {'Content-Length' => '0'})
|
40
|
+
project.active = true
|
41
|
+
end
|
42
|
+
project
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def task_xml(name)
|
47
|
+
builder = Builder::XmlMarkup.new
|
48
|
+
builder.task do |t|
|
49
|
+
t.name(name)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Harvest
|
2
|
+
module API
|
3
|
+
class Reports < Base
|
4
|
+
|
5
|
+
def time_by_project(project, start_date, end_date, user = nil)
|
6
|
+
query = {:from => start_date.strftime("%Y%m%d"), :to => end_date.strftime("%Y%m%d")}
|
7
|
+
query[:user_id] = user.to_i if user
|
8
|
+
|
9
|
+
response = request(:get, credentials, "/projects/#{project.to_i}/entries", :query => query)
|
10
|
+
Harvest::TimeEntry.parse(massage_xml(response.body))
|
11
|
+
end
|
12
|
+
|
13
|
+
def time_by_user(user, start_date, end_date, project = nil)
|
14
|
+
query = {:from => start_date.strftime("%Y%m%d"), :to => end_date.strftime("%Y%m%d")}
|
15
|
+
query[:project_id] = project.to_i if project
|
16
|
+
|
17
|
+
response = request(:get, credentials, "/people/#{user.to_i}/entries", :query => query)
|
18
|
+
Harvest::TimeEntry.parse(massage_xml(response.body))
|
19
|
+
end
|
20
|
+
|
21
|
+
def expenses_by_user(user, start_date, end_date)
|
22
|
+
query = {:from => start_date.strftime("%Y%m%d"), :to => end_date.strftime("%Y%m%d")}
|
23
|
+
|
24
|
+
response = request(:get, credentials, "/people/#{user.to_i}/expenses", :query => query)
|
25
|
+
Harvest::Expense.parse(response.body)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def massage_xml(original_xml)
|
30
|
+
# this needs to be done because of the differences in dashes and underscores in the harvest api
|
31
|
+
xml = original_xml
|
32
|
+
%w(day-entry adjustment-record created-at project-id spent-at task-id timer-started-at updated-at user-id).each do |dash_field|
|
33
|
+
xml = xml.gsub(dash_field, dash_field.gsub("-", "_"))
|
34
|
+
end
|
35
|
+
xml
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Harvest
|
2
|
+
module API
|
3
|
+
class TaskAssignments < Base
|
4
|
+
|
5
|
+
def all(project)
|
6
|
+
response = request(:get, credentials, "/projects/#{project.to_i}/task_assignments")
|
7
|
+
Harvest::TaskAssignment.parse(response.body)
|
8
|
+
end
|
9
|
+
|
10
|
+
def find(project, id)
|
11
|
+
response = request(:get, credentials, "/projects/#{project.to_i}/task_assignments/#{id}")
|
12
|
+
Harvest::TaskAssignment.parse(response.body, :single => true)
|
13
|
+
end
|
14
|
+
|
15
|
+
def create(task_assignment)
|
16
|
+
response = request(:post, credentials, "/projects/#{task_assignment.project_id}/task_assignments", :body => task_assignment.task_xml)
|
17
|
+
id = response.headers["location"].first.match(/\/.*\/(\d+)\/.*\/(\d+)/)[2]
|
18
|
+
find(task_assignment.project_id, id)
|
19
|
+
end
|
20
|
+
|
21
|
+
def update(task_assignment)
|
22
|
+
request(:put, credentials, "/projects/#{task_assignment.project_id}/task_assignments/#{task_assignment.to_i}", :body => task_assignment.to_xml)
|
23
|
+
find(task_assignment.project_id, task_assignment.id)
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete(task_assignment)
|
27
|
+
request(:delete, credentials, "/projects/#{task_assignment.project_id}/task_assignments/#{task_assignment.to_i}")
|
28
|
+
task_assignment.id
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|