togglv8 1.0.0 → 1.0.2
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 +4 -4
- data/README.md +15 -9
- data/lib/togglv8/clients.rb +1 -1
- data/lib/togglv8/projects.rb +2 -1
- data/lib/togglv8/tasks.rb +5 -3
- data/lib/togglv8/time_entries.rb +3 -1
- data/lib/togglv8/users.rb +7 -3
- data/lib/togglv8/version.rb +1 -1
- data/lib/togglv8.rb +6 -7
- data/spec/lib/togglv8/clients_spec.rb +5 -1
- data/spec/lib/togglv8/projects_spec.rb +2 -3
- data/spec/lib/togglv8/tasks_spec.rb +21 -18
- data/spec/lib/togglv8/time_entries_spec.rb +119 -15
- data/spec/lib/togglv8/users_spec.rb +38 -11
- data/spec/spec_helper.rb +12 -103
- data/spec/togglv8_spec_helper.rb +18 -18
- data/togglv8.gemspec +1 -3
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f78f0515e742e278468d6265c6841df07f164230
|
4
|
+
data.tar.gz: f78a57c6e31c97ddc5b54679957228e7afac77ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd36c398494606943d6ba8793a3035864e155a1aae42bfc3844289a362f82e3cf58b9cb16bbc0b72539b3913e886dda59bc9fb7200b5d58dfd1e54935f08a570
|
7
|
+
data.tar.gz: fb47297ebb39ab70d08dd80b9e6380f6fdfd306a5aa9d746de1c37ed19f93ba3afb13fb7ac5a170056ccb04613c7694bb11dc77256ff1737268e615dfdbb58e2
|
data/README.md
CHANGED
@@ -36,16 +36,18 @@ toggl_api = TogglV8::API.new(<API_TOKEN>)
|
|
36
36
|
user = toggl_api.me(all=true)
|
37
37
|
workspaces = toggl_api.my_workspaces(user)
|
38
38
|
workspace_id = workspaces.first['id']
|
39
|
-
toggl_api.create_time_entry({
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
toggl_api.create_time_entry({
|
40
|
+
'description' => "Workspace time entry",
|
41
|
+
'wid' => workspace_id,
|
42
|
+
'duration' => 1200,
|
43
|
+
'start' => "2015-08-18T01:13:40.000Z",
|
44
|
+
'created_with' => "My awesome Ruby application"
|
45
|
+
})
|
44
46
|
```
|
45
47
|
|
46
48
|
See specs for more examples.
|
47
49
|
|
48
|
-
**Note:** Requests are rate-limited. See [Toggl API docs](https://github.com/toggl/toggl_api_docs#the-api-format):
|
50
|
+
**Note:** Requests are rate-limited. The togglv8 gem will handle a 429 response by pausing for 1 second and trying again, for up to 3 attempts. See [Toggl API docs](https://github.com/toggl/toggl_api_docs#the-api-format):
|
49
51
|
|
50
52
|
> For rate limiting we have implemented a Leaky bucket. When a limit has been hit the request will get a HTTP 429 response and it's the task of the client to sleep/wait until bucket is empty. Limits will and can change during time, but a safe window will be 1 request per second. Limiting is applied per api token per IP, meaning two users from the same IP will get their rate allocated separately.
|
51
53
|
|
@@ -59,7 +61,9 @@ Also available on [DocumentUp](https://documentup.com/kanet77/togglv8)
|
|
59
61
|
|
60
62
|
Open `coverage/index.html` to see test coverage.
|
61
63
|
|
62
|
-
As of 2015-
|
64
|
+
As of 2015-12-10, coverage is "98.04% covered at 9.5 hits/line" according to [SimpleCov](https://rubygems.org/gems/simplecov).
|
65
|
+
|
66
|
+
**Note:** Coverage is 91.2% when Pro account features are not tested.
|
63
67
|
|
64
68
|
## Acknowledgements
|
65
69
|
|
@@ -68,12 +72,14 @@ As of 2015-08-21, coverage is "90.39% covered at 6.16 hits/line" according to [S
|
|
68
72
|
|
69
73
|
## Contributing
|
70
74
|
|
71
|
-
1. Fork it ( https://github.com/
|
75
|
+
1. Fork it ( https://github.com/kanet77/togglv8/fork )
|
72
76
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
73
77
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
74
78
|
4. Push to the branch (`git push origin my-new-feature`)
|
75
79
|
5. Create a new Pull Request
|
76
80
|
|
81
|
+
Pull Requests that include tests are **much** more likely to be accepted and merged quickly.
|
82
|
+
|
77
83
|
## License
|
78
84
|
|
79
|
-
Copyright (c) 2013-2015 Tom Kane. Released under the [MIT License](http://opensource.org/licenses/mit-license.php). See [LICENSE.txt](LICENSE.txt) for details.
|
85
|
+
Copyright (c) 2013-2015 Tom Kane. Released under the [MIT License](http://opensource.org/licenses/mit-license.php). See [LICENSE.txt](LICENSE.txt) for details.
|
data/lib/togglv8/clients.rb
CHANGED
data/lib/togglv8/projects.rb
CHANGED
@@ -98,13 +98,14 @@ module TogglV8
|
|
98
98
|
|
99
99
|
# [Get project tasks](https://github.com/toggl/toggl_api_docs/blob/master/chapters/projects.md#get-project-tasks)
|
100
100
|
def get_project_tasks(project_id)
|
101
|
-
get "projects/#{project_id}/
|
101
|
+
get "projects/#{project_id}/tasks"
|
102
102
|
end
|
103
103
|
|
104
104
|
# [Get workspace projects](https://github.com/toggl/toggl_api_docs/blob/master/chapters/projects.md#get-workspace-projects)
|
105
105
|
|
106
106
|
# [Delete multiple projects](https://github.com/toggl/toggl_api_docs/blob/master/chapters/projects.md#delete-multiple-projects)
|
107
107
|
def delete_projects(project_ids)
|
108
|
+
return if project_ids.nil?
|
108
109
|
delete "projects/#{project_ids.join(',')}"
|
109
110
|
end
|
110
111
|
end
|
data/lib/togglv8/tasks.rb
CHANGED
@@ -20,7 +20,7 @@ module TogglV8
|
|
20
20
|
# uname : full name of the person to whom the task is assigned to
|
21
21
|
|
22
22
|
def create_task(params)
|
23
|
-
requireParams(params, [
|
23
|
+
requireParams(params, ['name', 'pid'])
|
24
24
|
post "tasks", { 'task' => params }
|
25
25
|
end
|
26
26
|
|
@@ -30,11 +30,11 @@ module TogglV8
|
|
30
30
|
|
31
31
|
# ex: update_task(1894675, {active: true, estimated_seconds: 4500, fields: "done_seconds,uname"})
|
32
32
|
def update_task(task_id, params)
|
33
|
-
put "tasks/#{task_id
|
33
|
+
put "tasks/#{task_id}", { 'task' => params }
|
34
34
|
end
|
35
35
|
|
36
36
|
def delete_task(task_id)
|
37
|
-
delete "tasks/#{task_id
|
37
|
+
delete "tasks/#{task_id}"
|
38
38
|
end
|
39
39
|
|
40
40
|
# ------------ #
|
@@ -42,10 +42,12 @@ module TogglV8
|
|
42
42
|
# ------------ #
|
43
43
|
|
44
44
|
def update_tasks(task_ids, params)
|
45
|
+
return if task_ids.nil?
|
45
46
|
put "tasks/#{task_ids.join(',')}", { 'task' => params }
|
46
47
|
end
|
47
48
|
|
48
49
|
def delete_tasks(task_ids)
|
50
|
+
return if task_ids.nil?
|
49
51
|
delete "tasks/#{task_ids.join(',')}"
|
50
52
|
end
|
51
53
|
|
data/lib/togglv8/time_entries.rb
CHANGED
@@ -82,9 +82,11 @@ module TogglV8
|
|
82
82
|
get "time_entries%s" % [params.empty? ? "" : "?#{params.join('&')}"]
|
83
83
|
end
|
84
84
|
|
85
|
-
# Example params: {
|
85
|
+
# Example params: {'tags' =>['billed','productive'], 'tag_action' => 'add'}
|
86
86
|
# tag_action can be 'add' or 'remove'
|
87
87
|
def update_time_entries_tags(time_entry_ids, params)
|
88
|
+
return if time_entry_ids.nil?
|
89
|
+
requireParams(params, ['tags', 'tag_action'])
|
88
90
|
put "time_entries/#{time_entry_ids.join(',')}", { 'time_entry' => params }
|
89
91
|
end
|
90
92
|
end
|
data/lib/togglv8/users.rb
CHANGED
@@ -21,8 +21,8 @@ module TogglV8
|
|
21
21
|
# new_blog_post : an object with toggl blog post title and link
|
22
22
|
|
23
23
|
def me(all=nil)
|
24
|
-
#
|
25
|
-
#
|
24
|
+
# NOTE: response['since'] is discarded because it is outside response['data']
|
25
|
+
# (See TogglV8::API#get in lib/togglv8.rb)
|
26
26
|
get "me%s" % [all.nil? ? "" : "?with_related_data=#{all}"]
|
27
27
|
end
|
28
28
|
|
@@ -38,7 +38,6 @@ module TogglV8
|
|
38
38
|
projects.delete_if { |p| p['server_deleted_at'] }
|
39
39
|
end
|
40
40
|
|
41
|
-
# TODO: Test my_deleted_projects
|
42
41
|
def my_deleted_projects(user=nil)
|
43
42
|
user = me(all=true) if user.nil?
|
44
43
|
return {} unless user['projects']
|
@@ -51,6 +50,11 @@ module TogglV8
|
|
51
50
|
user['tags'] || {}
|
52
51
|
end
|
53
52
|
|
53
|
+
def my_tasks(user=nil)
|
54
|
+
user = me(all=true) if user.nil?
|
55
|
+
user['tasks'] || {}
|
56
|
+
end
|
57
|
+
|
54
58
|
def my_time_entries(user=nil)
|
55
59
|
user = me(all=true) if user.nil?
|
56
60
|
user['time_entries'] || {}
|
data/lib/togglv8/version.rb
CHANGED
data/lib/togglv8.rb
CHANGED
@@ -85,13 +85,12 @@ module TogglV8
|
|
85
85
|
loop do
|
86
86
|
i += 1
|
87
87
|
full_resp = procs[:api_call].call
|
88
|
-
@logger.ap(full_resp.env, :debug)
|
88
|
+
# @logger.ap(full_resp.env, :debug)
|
89
89
|
break if full_resp.status != 429 || i >= MAX_RETRIES
|
90
90
|
sleep(DELAY_SEC)
|
91
91
|
end
|
92
92
|
|
93
|
-
raise "HTTP Status: #{full_resp.status}" unless full_resp.
|
94
|
-
raise Oj.dump(full_resp.env) unless full_resp.success?
|
93
|
+
raise "HTTP Status: #{full_resp.status}" unless full_resp.success?
|
95
94
|
return {} if full_resp.body.nil? || full_resp.body == 'null'
|
96
95
|
|
97
96
|
full_resp
|
@@ -99,7 +98,7 @@ module TogglV8
|
|
99
98
|
|
100
99
|
def get(resource)
|
101
100
|
full_resp = _call_api(debug_output: lambda { "GET #{resource}" },
|
102
|
-
|
101
|
+
api_call: lambda { self.conn.get(resource) } )
|
103
102
|
return {} if full_resp == {}
|
104
103
|
resp = Oj.load(full_resp.body)
|
105
104
|
return resp['data'] if resp.respond_to?(:has_key?) && resp.has_key?('data')
|
@@ -108,7 +107,7 @@ module TogglV8
|
|
108
107
|
|
109
108
|
def post(resource, data='')
|
110
109
|
full_resp = _call_api(debug_output: lambda { "POST #{resource} / #{data}" },
|
111
|
-
|
110
|
+
api_call: lambda { self.conn.post(resource, Oj.dump(data)) } )
|
112
111
|
return {} if full_resp == {}
|
113
112
|
resp = Oj.load(full_resp.body)
|
114
113
|
resp['data']
|
@@ -116,7 +115,7 @@ module TogglV8
|
|
116
115
|
|
117
116
|
def put(resource, data='')
|
118
117
|
full_resp = _call_api(debug_output: lambda { "PUT #{resource} / #{data}" },
|
119
|
-
|
118
|
+
api_call: lambda { self.conn.put(resource, Oj.dump(data)) } )
|
120
119
|
return {} if full_resp == {}
|
121
120
|
resp = Oj.load(full_resp.body)
|
122
121
|
resp['data']
|
@@ -124,7 +123,7 @@ module TogglV8
|
|
124
123
|
|
125
124
|
def delete(resource)
|
126
125
|
full_resp = _call_api(debug_output: lambda { "DELETE #{resource}" },
|
127
|
-
|
126
|
+
api_call: lambda { self.conn.delete(resource) } )
|
128
127
|
return {} if full_resp == {}
|
129
128
|
full_resp.body
|
130
129
|
end
|
@@ -127,12 +127,16 @@ describe 'Clients' do
|
|
127
127
|
expect(client).to include(new_values)
|
128
128
|
end
|
129
129
|
|
130
|
-
|
130
|
+
# It appears hourly rate is no longer tied to a client despite the docs:
|
131
|
+
# https://github.com/toggl/toggl_api_docs/blob/master/chapters/clients.md#clients
|
132
|
+
xit 'updates Pro client data', :pro_account do
|
131
133
|
new_values = {
|
132
134
|
'hrate' => '7.77',
|
133
135
|
'cur' => 'USD',
|
134
136
|
}
|
135
137
|
client = @toggl.update_client(@client['id'], new_values)
|
138
|
+
|
139
|
+
client = @toggl.get_client(@client['id'])
|
136
140
|
expect(client).to include(new_values)
|
137
141
|
end
|
138
142
|
end
|
@@ -64,15 +64,14 @@ describe 'Projects' do
|
|
64
64
|
'active' => false,
|
65
65
|
'auto_estimates' => true,
|
66
66
|
}
|
67
|
-
|
68
67
|
project = @toggl.update_project(@project['id'], new_values)
|
69
68
|
expect(project).to include(new_values)
|
70
69
|
end
|
71
70
|
|
72
71
|
it 'updates Pro project data', :pro_account do
|
73
72
|
new_values = {
|
74
|
-
'template' =>
|
75
|
-
'billable' =>
|
73
|
+
'template' => true,
|
74
|
+
'billable' => true,
|
76
75
|
}
|
77
76
|
project = @toggl.update_project(@project['id'], new_values)
|
78
77
|
expect(project).to include(new_values)
|
@@ -3,7 +3,7 @@ describe 'Tasks', :pro_account do
|
|
3
3
|
@toggl = TogglV8::API.new(Testing::API_TOKEN)
|
4
4
|
@workspaces = @toggl.workspaces
|
5
5
|
@workspace_id = @workspaces.first['id']
|
6
|
-
@project = @toggl.create_project({ name
|
6
|
+
@project = @toggl.create_project({ 'name' => 'project with a task', 'wid' => @workspace_id })
|
7
7
|
end
|
8
8
|
|
9
9
|
after :all do
|
@@ -12,13 +12,13 @@ describe 'Tasks', :pro_account do
|
|
12
12
|
|
13
13
|
context 'new task' do
|
14
14
|
before :all do
|
15
|
-
@task = @toggl.create_task({ name
|
16
|
-
task_ids = @toggl.get_project_tasks(@project['id']).map { |t| t['id'] }
|
17
|
-
expect(task_ids).to eq [ @task['id'] ]
|
15
|
+
@task = @toggl.create_task({ 'name' => 'new task', 'pid' => @project['id'] })
|
16
|
+
@task_ids = @toggl.get_project_tasks(@project['id']).map { |t| t['id'] }
|
17
|
+
expect(@task_ids).to eq [ @task['id'] ]
|
18
18
|
end
|
19
19
|
|
20
20
|
after :all do
|
21
|
-
|
21
|
+
@toggl.delete_tasks(@task_ids)
|
22
22
|
tasks = @toggl.get_project_tasks(@project['id'])
|
23
23
|
expect(tasks).to be_empty
|
24
24
|
end
|
@@ -33,13 +33,15 @@ describe 'Tasks', :pro_account do
|
|
33
33
|
|
34
34
|
it 'gets a task' do
|
35
35
|
task = @toggl.get_task(@task['id'])
|
36
|
-
expect(task).to
|
36
|
+
expect(task).to include('at') # 'at' is last updated timestamp
|
37
|
+
task.delete('at') # 'at' is not included in POST response
|
38
|
+
expect(task).to eq @task # compare POST and GET responses
|
37
39
|
end
|
38
40
|
end
|
39
41
|
|
40
42
|
context 'updated task' do
|
41
43
|
before :each do
|
42
|
-
@task = @toggl.create_task({ name
|
44
|
+
@task = @toggl.create_task({ 'name' => 'task to update', 'pid' => @project['id'] })
|
43
45
|
end
|
44
46
|
|
45
47
|
after :each do
|
@@ -58,29 +60,30 @@ describe 'Tasks', :pro_account do
|
|
58
60
|
|
59
61
|
context 'multiple tasks' do
|
60
62
|
before :each do
|
61
|
-
|
62
|
-
@
|
63
|
-
@
|
63
|
+
timestamp = Time.now.strftime("%H%M%S.%9N")
|
64
|
+
@task1 = @toggl.create_task({ 'name' => "task1-#{timestamp}", 'pid' => @project['id'] })
|
65
|
+
@task2 = @toggl.create_task({ 'name' => "task2-#{timestamp}", 'pid' => @project['id'] })
|
66
|
+
@task3 = @toggl.create_task({ 'name' => "task3-#{timestamp}", 'pid' => @project['id'] })
|
67
|
+
@task_ids = [ @task1['id'], @task2['id'], @task3['id'] ]
|
64
68
|
end
|
65
69
|
|
66
70
|
after :all do
|
67
|
-
|
71
|
+
@toggl.delete_tasks(@task_ids)
|
68
72
|
end
|
69
73
|
|
70
74
|
it 'updates multiple tasks' do
|
71
75
|
# start with 3 active tasks
|
72
76
|
tasks = @toggl.get_project_tasks(@project['id'])
|
73
77
|
active_flags = tasks.map { |t| t['active'] }
|
74
|
-
expect().to match_array([
|
78
|
+
expect(active_flags).to match_array([true, true, true])
|
75
79
|
|
76
80
|
t_ids = [@task1, @task2, @task3].map { |t| t['id'] }
|
77
|
-
params = { 'active':
|
81
|
+
params = { 'active': false }
|
78
82
|
@toggl.update_tasks(t_ids, params)
|
79
83
|
|
80
|
-
# end with
|
84
|
+
# end with no active tasks
|
81
85
|
tasks = @toggl.get_project_tasks(@project['id'])
|
82
|
-
|
83
|
-
expect().to match_array(['true', 'true', 'true'])
|
86
|
+
expect(tasks).to be_empty
|
84
87
|
end
|
85
88
|
|
86
89
|
it 'deletes multiple tasks' do
|
@@ -90,8 +93,8 @@ describe 'Tasks', :pro_account do
|
|
90
93
|
t_ids = [@task1, @task2, @task3].map { |t| t['id'] }
|
91
94
|
@toggl.delete_tasks(t_ids)
|
92
95
|
|
93
|
-
# end with no tasks
|
94
|
-
expect(@toggl.get_project_tasks(@project['id'])
|
96
|
+
# end with no active tasks
|
97
|
+
expect(@toggl.get_project_tasks(@project['id'])).to be_empty
|
95
98
|
end
|
96
99
|
end
|
97
100
|
end
|
@@ -101,9 +101,7 @@ describe 'Time Entries' do
|
|
101
101
|
end
|
102
102
|
|
103
103
|
after :all do
|
104
|
-
|
105
|
-
@toggl.delete_time_entry(@now_id)
|
106
|
-
@toggl.delete_time_entry(@next_week_id)
|
104
|
+
TogglV8SpecHelper.delete_all_time_entries(@toggl)
|
107
105
|
end
|
108
106
|
|
109
107
|
it 'gets time entries (reaching back 9 days up till now)' do
|
@@ -116,11 +114,6 @@ describe 'Time Entries' do
|
|
116
114
|
expect(ids).to eq [ @now_id ]
|
117
115
|
end
|
118
116
|
|
119
|
-
it 'gets time entries between 9 days ago and end_date' do
|
120
|
-
ids = @toggl.get_time_entries({end_date: @now + 1}).map { |t| t['id']}
|
121
|
-
expect(ids).to eq [ @last_week_id, @now_id ]
|
122
|
-
end
|
123
|
-
|
124
117
|
it 'gets time entries between start_date and end_date' do
|
125
118
|
ids = @toggl.get_time_entries({start_date: @now - 1, end_date: @now + 1}).map { |t| t['id']}
|
126
119
|
expect(ids).to eq [ @now_id ]
|
@@ -181,6 +174,124 @@ describe 'Time Entries' do
|
|
181
174
|
end
|
182
175
|
end
|
183
176
|
|
177
|
+
context 'time entry tags' do
|
178
|
+
before :each do
|
179
|
+
time_entry_info = {
|
180
|
+
'wid' => @workspace_id,
|
181
|
+
'duration' => 7777
|
182
|
+
}
|
183
|
+
@now = DateTime.now
|
184
|
+
|
185
|
+
start = { 'start' => @toggl.iso8601(@now - 7) }
|
186
|
+
@time7 = @toggl.create_time_entry(time_entry_info.merge(start))
|
187
|
+
|
188
|
+
start = { 'start' => @toggl.iso8601(@now - 6) }
|
189
|
+
@time6 = @toggl.create_time_entry(time_entry_info.merge(start))
|
190
|
+
|
191
|
+
start = { 'start' => @toggl.iso8601(@now - 5) }
|
192
|
+
@time5 = @toggl.create_time_entry(time_entry_info.merge(start))
|
193
|
+
|
194
|
+
start = { 'start' => @toggl.iso8601(@now - 4) }
|
195
|
+
@time4 = @toggl.create_time_entry(time_entry_info.merge(start))
|
196
|
+
|
197
|
+
@time_entry_ids = [ @time7['id'], @time6['id'], @time5['id'], @time4['id']]
|
198
|
+
end
|
199
|
+
|
200
|
+
after :each do
|
201
|
+
TogglV8SpecHelper.delete_all_time_entries(@toggl)
|
202
|
+
TogglV8SpecHelper.delete_all_tags(@toggl)
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'adds and removes one tag' do
|
206
|
+
# Add one tag
|
207
|
+
@toggl.update_time_entries_tags(@time_entry_ids,
|
208
|
+
{'tags' =>['money'], 'tag_action' => 'add'})
|
209
|
+
|
210
|
+
time_entries = @toggl.get_time_entries
|
211
|
+
tags = time_entries.map { |t| t['tags'] }
|
212
|
+
expect(tags).to eq [
|
213
|
+
['money'],
|
214
|
+
['money'],
|
215
|
+
['money'],
|
216
|
+
['money']
|
217
|
+
]
|
218
|
+
|
219
|
+
# Remove one tag
|
220
|
+
@toggl.update_time_entries_tags(@time_entry_ids,
|
221
|
+
{'tags' =>['money'], 'tag_action' => 'remove'})
|
222
|
+
|
223
|
+
time_entries = @toggl.get_time_entries
|
224
|
+
tags = time_entries.map { |t| t['tags'] }.compact
|
225
|
+
expect(tags).to eq []
|
226
|
+
end
|
227
|
+
|
228
|
+
it '"removes" a non-existent tag' do
|
229
|
+
# Not tags to start
|
230
|
+
time_entries = @toggl.get_time_entries
|
231
|
+
tags = time_entries.map { |t| t['tags'] }.compact
|
232
|
+
expect(tags).to eq []
|
233
|
+
|
234
|
+
# "Remove" a tag
|
235
|
+
@toggl.update_time_entries_tags(@time_entry_ids,
|
236
|
+
{'tags' =>['void'], 'tag_action' => 'remove'})
|
237
|
+
|
238
|
+
# No tags to finish
|
239
|
+
time_entries = @toggl.get_time_entries
|
240
|
+
tags = time_entries.map { |t| t['tags'] }.compact
|
241
|
+
expect(tags).to eq []
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'adds and removes multiple tags' do
|
245
|
+
# Add multiple tags
|
246
|
+
@toggl.update_time_entries_tags(@time_entry_ids,
|
247
|
+
{'tags' =>['billed', 'productive'], 'tag_action' => 'add'})
|
248
|
+
|
249
|
+
time_entries = @toggl.get_time_entries
|
250
|
+
tags = time_entries.map { |t| t['tags'] }
|
251
|
+
expect(tags).to eq [
|
252
|
+
['billed', 'productive'],
|
253
|
+
['billed', 'productive'],
|
254
|
+
['billed', 'productive'],
|
255
|
+
['billed', 'productive']
|
256
|
+
]
|
257
|
+
|
258
|
+
# Remove multiple tags
|
259
|
+
@toggl.update_time_entries_tags(@time_entry_ids,
|
260
|
+
{'tags' =>['billed','productive'], 'tag_action' => 'remove'})
|
261
|
+
|
262
|
+
time_entries = @toggl.get_time_entries
|
263
|
+
tags = time_entries.map { |t| t['tags'] }.compact
|
264
|
+
expect(tags).to eq []
|
265
|
+
end
|
266
|
+
|
267
|
+
it 'manages multiple tags' do
|
268
|
+
# Add some tags
|
269
|
+
@toggl.update_time_entries_tags(@time_entry_ids,
|
270
|
+
{'tags' =>['billed', 'productive'], 'tag_action' => 'add'})
|
271
|
+
|
272
|
+
# Remove some tags
|
273
|
+
@toggl.update_time_entries_tags([ @time6['id'], @time4['id'] ],
|
274
|
+
{'tags' =>['billed'], 'tag_action' => 'remove'})
|
275
|
+
|
276
|
+
# Add some tags
|
277
|
+
@toggl.update_time_entries_tags([ @time7['id'] ],
|
278
|
+
{'tags' =>['best'], 'tag_action' => 'add'})
|
279
|
+
|
280
|
+
time7 = @toggl.get_time_entry(@time7['id'])
|
281
|
+
time6 = @toggl.get_time_entry(@time6['id'])
|
282
|
+
time5 = @toggl.get_time_entry(@time5['id'])
|
283
|
+
time4 = @toggl.get_time_entry(@time4['id'])
|
284
|
+
|
285
|
+
tags = [ time7['tags'], time6['tags'], time5['tags'], time4['tags'] ]
|
286
|
+
expect(tags).to eq [
|
287
|
+
['best', 'billed', 'productive'],
|
288
|
+
[ 'productive'],
|
289
|
+
[ 'billed', 'productive'],
|
290
|
+
[ 'productive']
|
291
|
+
]
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
184
295
|
context 'iso8601' do
|
185
296
|
before :all do
|
186
297
|
@ts = DateTime.new(2008,6,21, 13,30,2, "+09:00")
|
@@ -210,13 +321,6 @@ describe 'Time Entries' do
|
|
210
321
|
end
|
211
322
|
|
212
323
|
context 'String' do
|
213
|
-
xit 'converts Zulu to +00:00' do
|
214
|
-
ts = '2015-08-21T09:21:02Z'
|
215
|
-
expected = '2015-08-21T09:21:02+00:00'
|
216
|
-
|
217
|
-
expect(@toggl.iso8601(ts)).to eq expected
|
218
|
-
end
|
219
|
-
|
220
324
|
it 'converts +00:00 to Zulu' do
|
221
325
|
ts = '2015-08-21T09:21:02+00:00'
|
222
326
|
expected = '2015-08-21T09:21:02Z'
|
@@ -2,39 +2,66 @@ describe 'Users' do
|
|
2
2
|
before :all do
|
3
3
|
@toggl = TogglV8::API.new(Testing::API_TOKEN)
|
4
4
|
@user = @toggl.me(all=true)
|
5
|
+
@workspaces = @toggl.workspaces
|
6
|
+
@workspace_id = @workspaces.first['id']
|
5
7
|
end
|
6
8
|
|
7
9
|
it 'returns /me' do
|
8
10
|
expect(@user).to_not be_nil
|
9
|
-
expect(@user['id']).to eq
|
10
|
-
expect(@user['fullname']).to eq 'togglv8'
|
11
|
-
expect(@user['image_url']).to eq 'https://assets.toggl.com/avatars/a5d106126b6bed8df283e708af0828ee.png'
|
12
|
-
expect(@user['timezone']).to eq 'Etc/UTC'
|
13
|
-
expect(@user['workspaces'].length).to eq 1
|
14
|
-
expect(@user['workspaces'].first['name']).to eq "togglv8's workspace"
|
11
|
+
expect(@user['id']).to eq Testing::USER_ID
|
12
|
+
# expect(@user['fullname']).to eq 'togglv8'
|
13
|
+
# expect(@user['image_url']).to eq 'https://assets.toggl.com/avatars/a5d106126b6bed8df283e708af0828ee.png'
|
14
|
+
# expect(@user['timezone']).to eq 'Etc/UTC'
|
15
|
+
# expect(@user['workspaces'].length).to eq 1
|
16
|
+
# expect(@user['workspaces'].first['name']).to eq "togglv8's workspace"
|
15
17
|
end
|
16
18
|
|
17
|
-
it 'returns
|
19
|
+
it 'returns my_clients' do
|
18
20
|
my_clients = @toggl.my_clients(@user)
|
19
21
|
expect(my_clients).to be_empty
|
20
22
|
end
|
21
23
|
|
22
|
-
it 'returns
|
24
|
+
it 'returns my_projects' do
|
23
25
|
my_projects = @toggl.my_projects(@user)
|
24
26
|
expect(my_projects).to be_empty
|
25
27
|
end
|
26
28
|
|
27
|
-
it 'returns
|
29
|
+
it 'returns my_projects and my_deleted_projects' do
|
30
|
+
# Create project
|
31
|
+
project = @toggl.create_project({ 'name' => 'my project', 'wid' => @workspace_id })
|
32
|
+
|
33
|
+
my_project_ids = @toggl.my_projects.map { |p| p['id'] }
|
34
|
+
my_deleted_project_ids = @toggl.my_deleted_projects.map { |p| p['id'] }
|
35
|
+
|
36
|
+
expect(my_project_ids).to eq [ project['id'] ]
|
37
|
+
expect(my_deleted_project_ids).not_to include(project['id'])
|
38
|
+
|
39
|
+
# Delete project
|
40
|
+
@toggl.delete_project(project['id'])
|
41
|
+
|
42
|
+
my_project_ids = @toggl.my_projects.map { |p| p['id'] }
|
43
|
+
my_deleted_project_ids = @toggl.my_deleted_projects.map { |p| p['id'] }
|
44
|
+
|
45
|
+
expect(my_project_ids).to eq []
|
46
|
+
expect(my_deleted_project_ids).to include(project['id'])
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'returns my_tags' do
|
28
50
|
my_tags = @toggl.my_tags(@user)
|
29
51
|
expect(my_tags).to be_empty
|
30
52
|
end
|
31
53
|
|
32
|
-
it 'returns
|
54
|
+
it 'returns my_tasks' do
|
55
|
+
my_tasks = @toggl.my_tasks(@user)
|
56
|
+
expect(my_tasks).to be_empty
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'returns my_time_entries' do
|
33
60
|
my_time_entries = @toggl.my_time_entries(@user)
|
34
61
|
expect(my_time_entries).to be_empty
|
35
62
|
end
|
36
63
|
|
37
|
-
it 'returns
|
64
|
+
it 'returns my_workspaces' do
|
38
65
|
my_workspaces = @toggl.my_workspaces(@user)
|
39
66
|
expect(my_workspaces.length).to eq 1
|
40
67
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,45 +1,17 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require "simplecov"
|
2
|
+
require "coveralls"
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
5
|
+
SimpleCov::Formatter::HTMLFormatter,
|
6
|
+
Coveralls::SimpleCov::Formatter
|
7
|
+
]
|
8
|
+
SimpleCov.start
|
6
9
|
|
7
|
-
|
8
|
-
# SimpleCov.start do
|
9
|
-
# require 'simplecov-badge'
|
10
|
-
# add_group 'lib', 'lib/'
|
11
|
-
# add_filter '/doc/'
|
12
|
-
# add_filter '/spec/'
|
13
|
-
# add_filter '/pkg/'
|
14
|
-
# # configure any options you want for SimpleCov::Formatter::BadgeFormatter
|
15
|
-
# SimpleCov::Formatter::BadgeFormatter.generate_groups = true
|
16
|
-
# SimpleCov::Formatter::BadgeFormatter.timestamp = true
|
17
|
-
# SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
18
|
-
# SimpleCov::Formatter::HTMLFormatter,
|
19
|
-
# SimpleCov::Formatter::BadgeFormatter,
|
20
|
-
# ]
|
21
|
-
# end
|
22
|
-
# NOTE: SimpleCov.start must be issued before any application code is required!
|
23
|
-
# -----------------------------------------------------------------------------
|
10
|
+
require 'pry-byebug'
|
24
11
|
|
25
12
|
require_relative 'togglv8_spec_helper'
|
26
13
|
require_relative '../lib/togglv8'
|
27
14
|
|
28
|
-
# This file was generated by the `rspec --init` command. Conventionally, all
|
29
|
-
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
30
|
-
# The generated `.rspec` file contains `--require spec_helper` which will cause this
|
31
|
-
# file to always be loaded, without a need to explicitly require it in any files.
|
32
|
-
#
|
33
|
-
# Given that it is always loaded, you are encouraged to keep this file as
|
34
|
-
# light-weight as possible. Requiring heavyweight dependencies from this file
|
35
|
-
# will add to the boot time of your test suite on EVERY test run, even for an
|
36
|
-
# individual file that may not need all of that loaded. Instead, consider making
|
37
|
-
# a separate helper file that requires the additional dependencies and performs
|
38
|
-
# the additional setup, and require it from the spec files that actually need it.
|
39
|
-
#
|
40
|
-
# The `.rspec` file also contains a few flags that are not defaults but that
|
41
|
-
# users commonly want.
|
42
|
-
#
|
43
15
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
44
16
|
RSpec.configure do |config|
|
45
17
|
config.filter_run :focus
|
@@ -49,27 +21,12 @@ RSpec.configure do |config|
|
|
49
21
|
config.order = :random
|
50
22
|
Kernel.srand config.seed
|
51
23
|
|
52
|
-
# rspec-expectations config goes here. You can use an alternate
|
53
|
-
# assertion/expectation library such as wrong or the stdlib/minitest
|
54
|
-
# assertions if you prefer.
|
55
24
|
config.expect_with :rspec do |expectations|
|
56
|
-
# This option will default to `true` in RSpec 4. It makes the `description`
|
57
|
-
# and `failure_message` of custom matchers include text for helper methods
|
58
|
-
# defined using `chain`, e.g.:
|
59
|
-
# be_bigger_than(2).and_smaller_than(4).description
|
60
|
-
# # => "be bigger than 2 and smaller than 4"
|
61
|
-
# ...rather than:
|
62
|
-
# # => "be bigger than 2"
|
63
25
|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
64
26
|
expectations.syntax = :expect
|
65
27
|
end
|
66
28
|
|
67
|
-
# rspec-mocks config goes here. You can use an alternate test double
|
68
|
-
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
69
29
|
config.mock_with :rspec do |mocks|
|
70
|
-
# Prevents you from mocking or stubbing a method that does not exist on
|
71
|
-
# a real object. This is generally recommended, and will default to
|
72
|
-
# `true` in RSpec 4.
|
73
30
|
mocks.verify_partial_doubles = true
|
74
31
|
end
|
75
32
|
|
@@ -77,59 +34,11 @@ RSpec.configure do |config|
|
|
77
34
|
toggl = TogglV8::API.new(Testing::API_TOKEN)
|
78
35
|
TogglV8SpecHelper.setUp(toggl)
|
79
36
|
end
|
80
|
-
|
81
|
-
# The settings below are suggested to provide a good initial experience
|
82
|
-
# with RSpec, but feel free to customize to your heart's content.
|
83
|
-
=begin
|
84
|
-
# These two settings work together to allow you to limit a spec run
|
85
|
-
# to individual examples or groups you care about by tagging them with
|
86
|
-
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
87
|
-
# get run.
|
88
|
-
config.filter_run :focus
|
89
|
-
config.run_all_when_everything_filtered = true
|
90
|
-
|
91
|
-
# Limits the available syntax to the non-monkey patched syntax that is recommended.
|
92
|
-
# For more details, see:
|
93
|
-
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
94
|
-
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
95
|
-
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
96
|
-
config.disable_monkey_patching!
|
97
|
-
|
98
|
-
# This setting enables warnings. It's recommended, but in some cases may
|
99
|
-
# be too noisy due to issues in dependencies.
|
100
|
-
config.warnings = true
|
101
|
-
|
102
|
-
# Many RSpec users commonly either run the entire suite or an individual
|
103
|
-
# file, and it's useful to allow more verbose output when running an
|
104
|
-
# individual spec file.
|
105
|
-
if config.files_to_run.one?
|
106
|
-
# Use the documentation formatter for detailed output,
|
107
|
-
# unless a formatter has already been configured
|
108
|
-
# (e.g. via a command-line flag).
|
109
|
-
config.default_formatter = 'doc'
|
110
|
-
end
|
111
|
-
|
112
|
-
# Print the 10 slowest examples and example groups at the
|
113
|
-
# end of the spec run, to help surface which specs are running
|
114
|
-
# particularly slow.
|
115
|
-
config.profile_examples = 10
|
116
|
-
|
117
|
-
# Run specs in random order to surface order dependencies. If you find an
|
118
|
-
# order dependency and want to debug it, you can fix the order by providing
|
119
|
-
# the seed, which is printed after each run.
|
120
|
-
# --seed 1234
|
121
|
-
config.order = :random
|
122
|
-
|
123
|
-
# Seed global randomization in this process using the `--seed` CLI option.
|
124
|
-
# Setting this allows you to use `--seed` to deterministically reproduce
|
125
|
-
# test failures related to randomization by passing the same `--seed` value
|
126
|
-
# as the one that triggered the failure.
|
127
|
-
Kernel.srand config.seed
|
128
|
-
=end
|
129
37
|
end
|
130
38
|
|
131
39
|
class Testing
|
132
|
-
API_TOKEN = '4880adbe1bee9a241fa08070d33bd49f'
|
133
|
-
USERNAME = 'togglv8@mailinator.com'
|
134
|
-
PASSWORD = 'togglv8'
|
40
|
+
API_TOKEN = ENV['API_TOKEN'] || '4880adbe1bee9a241fa08070d33bd49f'
|
41
|
+
USERNAME = ENV['USERNAME'] || 'togglv8@mailinator.com'
|
42
|
+
PASSWORD = ENV['PASSWORD'] || 'togglv8'
|
43
|
+
USER_ID = (ENV['USER_ID'] || 1820939).to_i
|
135
44
|
end
|
data/spec/togglv8_spec_helper.rb
CHANGED
@@ -6,69 +6,69 @@ class TogglV8SpecHelper
|
|
6
6
|
@logger.level = Logger::WARN
|
7
7
|
|
8
8
|
def self.setUp(toggl)
|
9
|
-
|
10
|
-
user = @toggl.me(all=true)
|
9
|
+
user = toggl.me(all=true)
|
11
10
|
@default_workspace_id = user['default_wid']
|
12
11
|
|
13
|
-
delete_all_projects(
|
14
|
-
delete_all_clients(
|
15
|
-
delete_all_tags(
|
16
|
-
delete_all_time_entries(
|
17
|
-
delete_all_workspaces(
|
12
|
+
delete_all_projects(toggl)
|
13
|
+
delete_all_clients(toggl)
|
14
|
+
delete_all_tags(toggl)
|
15
|
+
delete_all_time_entries(toggl)
|
16
|
+
delete_all_workspaces(toggl)
|
18
17
|
end
|
19
18
|
|
20
19
|
def self.delete_all_clients(toggl)
|
21
|
-
clients =
|
20
|
+
clients = toggl.my_clients
|
22
21
|
unless clients.nil?
|
23
22
|
client_ids ||= clients.map { |c| c['id'] }
|
24
23
|
@logger.debug("Deleting #{client_ids.length} clients")
|
25
24
|
client_ids.each do |c_id|
|
26
|
-
|
25
|
+
toggl.delete_client(c_id)
|
27
26
|
end
|
28
27
|
end
|
29
28
|
end
|
30
29
|
|
31
30
|
def self.delete_all_projects(toggl)
|
32
|
-
projects =
|
31
|
+
projects = toggl.projects(@default_workspace_id)
|
33
32
|
unless projects.nil?
|
34
33
|
project_ids ||= projects.map { |p| p['id'] }
|
35
34
|
@logger.debug("Deleting #{project_ids.length} projects")
|
36
35
|
return unless project_ids.length > 0
|
37
|
-
|
36
|
+
toggl.delete_projects(project_ids)
|
38
37
|
end
|
39
38
|
end
|
40
39
|
|
41
40
|
def self.delete_all_tags(toggl)
|
42
|
-
tags =
|
41
|
+
tags = toggl.my_tags
|
43
42
|
unless tags.nil?
|
44
43
|
tag_ids ||= tags.map { |t| t['id'] }
|
45
44
|
@logger.debug("Deleting #{tag_ids.length} tags")
|
46
45
|
tag_ids.each do |t_id|
|
47
|
-
|
46
|
+
toggl.delete_tag(t_id)
|
48
47
|
end
|
49
48
|
end
|
50
49
|
end
|
51
50
|
|
52
51
|
def self.delete_all_time_entries(toggl)
|
53
|
-
time_entries =
|
52
|
+
time_entries = toggl.get_time_entries(
|
53
|
+
{ start_date: DateTime.now - 30, end_date: DateTime.now + 30 } )
|
54
54
|
unless time_entries.nil?
|
55
55
|
time_entry_ids ||= time_entries.map { |t| t['id'] }
|
56
56
|
@logger.debug("Deleting #{time_entry_ids.length} time_entries")
|
57
57
|
time_entry_ids.each do |t_id|
|
58
|
-
|
58
|
+
toggl.delete_time_entry(t_id)
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
63
|
def self.delete_all_workspaces(toggl)
|
64
|
-
user =
|
65
|
-
workspaces =
|
64
|
+
user = toggl.me(all=true)
|
65
|
+
workspaces = toggl.my_workspaces(user)
|
66
66
|
unless workspaces.nil?
|
67
67
|
workspace_ids ||= workspaces.map { |w| w['id'] }
|
68
68
|
workspace_ids.delete(user['default_wid'])
|
69
69
|
@logger.debug("Leaving #{workspace_ids.length} workspaces")
|
70
70
|
workspace_ids.each do |w_id|
|
71
|
-
|
71
|
+
toggl.leave_workspace(w_id)
|
72
72
|
end
|
73
73
|
end
|
74
74
|
end
|
data/togglv8.gemspec
CHANGED
@@ -25,10 +25,8 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.add_development_dependency "rspec-mocks"
|
26
26
|
spec.add_development_dependency "awesome_print"
|
27
27
|
spec.add_development_dependency "fivemat"
|
28
|
-
spec.add_development_dependency "simplecov"
|
29
|
-
# spec.add_development_dependency "simplecov-badge"
|
30
|
-
# spec.add_development_dependency "codeclimate-test-reporter"
|
31
28
|
spec.add_development_dependency "coveralls"
|
29
|
+
spec.add_development_dependency "pry-byebug"
|
32
30
|
|
33
31
|
spec.add_dependency "logger"
|
34
32
|
spec.add_dependency "faraday"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: togglv8
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-12-
|
11
|
+
date: 2015-12-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -95,7 +95,7 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: coveralls
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - ">="
|
@@ -109,7 +109,7 @@ dependencies:
|
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: pry-byebug
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - ">="
|