togglv8-lastobelus 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,58 @@
1
+ require_relative 'clients'
2
+ require_relative 'dashboard'
3
+ require_relative 'project_users'
4
+ require_relative 'projects'
5
+ require_relative 'tags'
6
+ require_relative 'tasks'
7
+ require_relative 'time_entries'
8
+ require_relative 'users'
9
+ require_relative 'version'
10
+ require_relative 'workspaces'
11
+
12
+ module TogglV8
13
+ TOGGL_API_URL = 'https://www.toggl.com/api/'
14
+
15
+ class API
16
+ include TogglV8::Connection
17
+
18
+ TOGGL_API_V8_URL = TOGGL_API_URL + 'v8/'
19
+ TOGGL_API_V9_URL = TOGGL_API_URL + 'v9/'
20
+
21
+ attr_reader :conn
22
+
23
+ def initialize(username=nil, password=API_TOKEN, opts={})
24
+ debug(false)
25
+ if username.nil? && password == API_TOKEN
26
+ toggl_api_file = File.join(Dir.home, TOGGL_FILE)
27
+ # logger.debug("toggl_api_file = #{toggl_api_file}")
28
+ if File.exist?(toggl_api_file) then
29
+ username = IO.read(toggl_api_file).strip
30
+ else
31
+ raise "Expecting one of:\n" +
32
+ " 1) api_token in file #{toggl_api_file}, or\n" +
33
+ " 2) parameter: (api_token), or\n" +
34
+ " 3) parameters: (username, password).\n" +
35
+ "\n\tSee https://github.com/kanet77/togglv8#togglv8api" +
36
+ "\n\tand https://github.com/toggl/toggl_api_docs/blob/master/chapters/authentication.md"
37
+ end
38
+ end
39
+
40
+ @conn = TogglV8::Connection.open(username, password,
41
+ TOGGL_API_V8_URL, opts)
42
+ @v9_conn = TogglV8::Connection.open(username, password,
43
+ TOGGL_API_V9_URL, opts)
44
+ end
45
+ end
46
+ end
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
@@ -0,0 +1,74 @@
1
+ module TogglV8
2
+ class API
3
+
4
+ ##
5
+ # ---------
6
+ # :section: Users
7
+ #
8
+ # api_token : (string)
9
+ # default_wid : default workspace id (integer)
10
+ # email : (string)
11
+ # jquery_timeofday_format : (string)
12
+ # jquery_date_format : (string)
13
+ # timeofday_format : (string)
14
+ # date_format : (string)
15
+ # store_start_and_stop_time : whether start and stop time are saved on time entry (boolean)
16
+ # beginning_of_week : (integer, Sunday=0)
17
+ # language : user's language (string)
18
+ # image_url : url with the user's profile picture(string)
19
+ # sidebar_piechart : should a piechart be shown on the sidebar (boolean)
20
+ # at : timestamp of last changes
21
+ # new_blog_post : an object with toggl blog post title and link
22
+
23
+ def me(all=nil)
24
+ # NOTE: response['since'] is discarded because it is outside response['data']
25
+ # (See TogglV8::API#get in lib/togglv8.rb)
26
+ get "me%s" % [all.nil? ? "" : "?with_related_data=#{all}"]
27
+ end
28
+
29
+ def my_clients(user=nil)
30
+ user = me(all=true) if user.nil?
31
+ user['clients'] || {}
32
+ end
33
+
34
+ def my_projects(user=nil)
35
+ user = me(all=true) if user.nil?
36
+ return {} unless user['projects']
37
+ projects = user['projects']
38
+ projects.delete_if { |p| p['server_deleted_at'] }
39
+ end
40
+
41
+ def my_deleted_projects(user=nil)
42
+ user = me(all=true) if user.nil?
43
+ return {} unless user['projects']
44
+ projects = user['projects']
45
+ projects.keep_if { |p| p['server_deleted_at'] }
46
+ end
47
+
48
+ def my_tags(user=nil)
49
+ user = me(all=true) if user.nil?
50
+ user['tags'] || {}
51
+ end
52
+
53
+ def my_tasks(user=nil)
54
+ user = me(all=true) if user.nil?
55
+ user['tasks'] || {}
56
+ end
57
+
58
+ def my_time_entries(user=nil)
59
+ user = me(all=true) if user.nil?
60
+ user['time_entries'] || {}
61
+ end
62
+
63
+ def my_workspaces(user=nil)
64
+ user = me(all=true) if user.nil?
65
+ user['workspaces'] || {}
66
+ end
67
+
68
+ def create_user(params)
69
+ params['created_with'] = TogglV8::NAME unless params.has_key?('created_with')
70
+ requireParams(params, ['email', 'password', 'timezone', 'created_with'])
71
+ post "signups", { 'user' => params }
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,4 @@
1
+ module TogglV8
2
+ # :section:
3
+ VERSION = "1.2.2"
4
+ end
@@ -0,0 +1,47 @@
1
+ module TogglV8
2
+ class API
3
+
4
+ ##
5
+ # ---------
6
+ # :section: Workspaces
7
+ #
8
+ # name : (string, required)
9
+ # premium : If it's a pro workspace or not.
10
+ # Shows if someone is paying for the workspace or not (boolean, not required)
11
+ # at : timestamp that is sent in the response, indicates the time item was last updated
12
+
13
+ def workspaces
14
+ get "workspaces"
15
+ end
16
+
17
+ def clients(workspace_id=nil)
18
+ if workspace_id.nil?
19
+ get "clients"
20
+ else
21
+ get "workspaces/#{workspace_id}/clients"
22
+ end
23
+ end
24
+
25
+ def projects(workspace_id, params={})
26
+ active = params.has_key?(:active) ? "?active=#{params[:active]}" : ""
27
+ get "workspaces/#{workspace_id}/projects#{active}"
28
+ end
29
+
30
+ def users(workspace_id)
31
+ get "workspaces/#{workspace_id}/users"
32
+ end
33
+
34
+ def tasks(workspace_id, params={})
35
+ active = params.has_key?(:active) ? "?active=#{params[:active]}" : ""
36
+ get "workspaces/#{workspace_id}/tasks#{active}"
37
+ end
38
+
39
+ def tags(workspace_id)
40
+ get "workspaces/#{workspace_id}/tags"
41
+ end
42
+
43
+ def leave_workspace(workspace_id)
44
+ delete "workspaces/#{workspace_id}/leave"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,254 @@
1
+ require 'fileutils'
2
+
3
+ describe 'ReportsV2' do
4
+ it 'initializes with api_token' do
5
+ reports = TogglV8::ReportsV2.new(api_token: Testing::API_TOKEN)
6
+ env = reports.env
7
+ expect(env).to_not be nil
8
+ expect(env['user']['api_token']).to eq Testing::API_TOKEN
9
+ end
10
+
11
+ it 'does not initialize with bogus api_token' do
12
+ reports = TogglV8::ReportsV2.new(api_token: '4880nqor1orr9n241sn08070q33oq49s')
13
+ expect { reports.env }.to raise_error(RuntimeError, "HTTP Status: 401")
14
+ end
15
+
16
+ context '.toggl file' do
17
+ before :each do
18
+ @tmp_home = mktemp_dir
19
+ @original_home = Dir.home
20
+ ENV['HOME'] = @tmp_home
21
+ end
22
+
23
+ after :each do
24
+ FileUtils.rm_rf(@tmp_home)
25
+ ENV['HOME'] = @original_home
26
+ end
27
+
28
+ it 'initializes with .toggl file' do
29
+ toggl_file = File.join(@tmp_home, '.toggl')
30
+ File.open(toggl_file, 'w') { |file| file.write(Testing::API_TOKEN) }
31
+
32
+ reports = TogglV8::ReportsV2.new
33
+ env = reports.env
34
+ expect(env).to_not be nil
35
+ expect(env['user']['api_token']).to eq Testing::API_TOKEN
36
+ end
37
+
38
+ it 'initializes with custom toggl file' do
39
+ toggl_file = File.join(@tmp_home, 'my_toggl')
40
+ File.open(toggl_file, 'w') { |file| file.write(Testing::API_TOKEN) }
41
+
42
+ reports = TogglV8::ReportsV2.new(toggl_api_file: toggl_file)
43
+ env = reports.env
44
+ expect(env).to_not be nil
45
+ expect(env['user']['api_token']).to eq Testing::API_TOKEN
46
+ end
47
+
48
+ it 'raises error if .toggl file is missing' do
49
+ expect{ reports = TogglV8::ReportsV2.new }.to raise_error(RuntimeError)
50
+ end
51
+ end
52
+
53
+ context 'handles errors' do
54
+ before :all do
55
+ @reports = TogglV8::ReportsV2.new(api_token: Testing::API_TOKEN)
56
+ @reports.workspace_id = @workspace_id
57
+ end
58
+
59
+ it 'surfaces a Warning HTTP header in case of 400 error' do
60
+ # https://github.com/toggl/toggl_api_docs/blob/master/reports.md#failed-requests
61
+ expect { @reports.error400 }.to raise_error(RuntimeError,
62
+ "This URL is intended only for testing")
63
+ end
64
+
65
+ it 'retries a request up to 3 times if a 429 is received' do
66
+ expect(@reports.conn).to receive(:get).exactly(3).times.and_return(
67
+ MockResponse.new(429, {}, 'body'))
68
+ expect { @reports.env }.to raise_error(RuntimeError, "HTTP Status: 429")
69
+ end
70
+
71
+ it 'retries a request after 429' do
72
+ expect(@reports.conn).to receive(:get).twice.and_return(
73
+ MockResponse.new(429, {}, 'body'),
74
+ MockResponse.new(200, {}, 'rev1.2.3'))
75
+ expect(@reports.revision).to eq('rev1.2.3')
76
+ end
77
+ end
78
+
79
+ context 'miscellaneous' do
80
+ it 'env returns environment' do
81
+ reports = TogglV8::ReportsV2.new(api_token: Testing::API_TOKEN)
82
+ reports.workspace_id = @workspace_id
83
+ env = reports.env
84
+ expect(env['workspace']).to_not be nil
85
+ expect(env['user']).to_not be nil
86
+ expect(env['user']['id']).to eq Testing::USER_ID
87
+ end
88
+
89
+ it 'index returns endpoints' do
90
+ reports = TogglV8::ReportsV2.new(api_token: Testing::API_TOKEN)
91
+ reports.workspace_id = @workspace_id
92
+ index = reports.index
93
+ expect(index['Welcome to reports api V2. VALID requests are:']).to_not be nil
94
+ end
95
+
96
+ it 'revision has not changed' do
97
+ reports = TogglV8::ReportsV2.new(api_token: Testing::API_TOKEN)
98
+ reports.workspace_id = @workspace_id
99
+ expect(reports.revision).to start_with "0.0.38\n"
100
+ end
101
+ end
102
+
103
+ context 'project', :pro_account do
104
+ before :all do
105
+ @toggl = TogglV8::API.new(Testing::API_TOKEN)
106
+ @project_name = "Project #{Time.now.iso8601}"
107
+ @project = @toggl.create_project({
108
+ 'name' => @project_name,
109
+ 'wid' => @workspace_id
110
+ })
111
+ end
112
+
113
+ after :all do
114
+ @toggl.delete_project(@project['id'])
115
+ end
116
+
117
+ it 'dashboard' do
118
+ reports = TogglV8::ReportsV2.new(api_token: Testing::API_TOKEN)
119
+ reports.workspace_id = @toggl.workspaces.first['id']
120
+ project_dashboard = reports.project(@project['id'])
121
+
122
+ expect(project_dashboard['name']).to eq @project_name
123
+ end
124
+ end
125
+
126
+ context 'blank reports' do
127
+ before :all do
128
+ @toggl = TogglV8::API.new(Testing::API_TOKEN)
129
+ @workspaces = @toggl.workspaces
130
+ @workspace_id = @workspaces.first['id']
131
+ @reports = TogglV8::ReportsV2.new(api_token: Testing::API_TOKEN)
132
+ @reports.workspace_id = @workspace_id
133
+ end
134
+
135
+ it 'summary' do
136
+ expect(@reports.summary).to eq []
137
+ end
138
+
139
+ it 'weekly' do
140
+ expect(@reports.weekly).to eq []
141
+ end
142
+
143
+ it 'details' do
144
+ expect(@reports.details).to eq []
145
+ end
146
+ end
147
+
148
+ context 'reports' do
149
+ before :all do
150
+ @toggl = TogglV8::API.new(Testing::API_TOKEN)
151
+ @workspaces = @toggl.workspaces
152
+ @workspace_id = @workspaces.first['id']
153
+ time_entry_info = {
154
+ 'wid' => @workspace_id,
155
+ 'start' => @toggl.iso8601(DateTime.now),
156
+ 'duration' => 77
157
+ }
158
+
159
+ @time_entry = @toggl.create_time_entry(time_entry_info)
160
+
161
+ @reports = TogglV8::ReportsV2.new(api_token: Testing::API_TOKEN)
162
+ @reports.workspace_id = @workspace_id
163
+
164
+ @tmp_home = mktemp_dir
165
+ @original_home = Dir.home
166
+ ENV['HOME'] = @tmp_home
167
+ end
168
+
169
+ after :all do
170
+ @toggl.delete_time_entry(@time_entry['id'])
171
+
172
+ FileUtils.rm_rf(@tmp_home)
173
+ ENV['HOME'] = @original_home
174
+ end
175
+
176
+ context 'JSON reports' do
177
+ it 'summary' do
178
+ summary = @reports.summary
179
+ expect(summary.length).to eq 1
180
+ expect(summary.first['time']).to eq 77000
181
+ expect(summary.first['items'].length).to eq 1
182
+ expect(summary.first['items'].first['time']).to eq 77000
183
+ end
184
+
185
+ it 'weekly' do
186
+ weekly = @reports.weekly
187
+ expect(weekly.length).to eq 1
188
+ expect(weekly.first['details'].first['title']['user']).to eq Testing::USERNAME
189
+ expect(weekly.first['totals'][7]).to eq 77000
190
+ end
191
+
192
+ it 'details' do
193
+ details = @reports.details
194
+ expect(details.length).to eq 1
195
+ expect(details.first['user']).to eq Testing::USERNAME
196
+ expect(details.first['dur']).to eq 77000
197
+ end
198
+ end
199
+
200
+ context 'CSV reports' do
201
+ it 'summary' do
202
+ filename = File.join(@tmp_home, 'summary.csv')
203
+ summary = @reports.write_summary(filename)
204
+ expect(file_contains(filename, /00:01:17/))
205
+ end
206
+
207
+ it 'weekly' do
208
+ filename = File.join(@tmp_home, 'weekly.csv')
209
+ weekly = @reports.write_weekly(filename)
210
+ expect(file_contains(filename, /00:01:17/))
211
+ end
212
+
213
+ it 'details' do
214
+ filename = File.join(@tmp_home, 'details.csv')
215
+ details = @reports.write_details(filename)
216
+ expect(file_contains(filename, /00:01:17/))
217
+ end
218
+ end
219
+
220
+ context 'PDF reports' do
221
+ it 'summary' do
222
+ filename = File.join(@tmp_home, 'summary.pdf')
223
+ summary = @reports.write_summary(filename)
224
+ expect(file_is_pdf(filename))
225
+ end
226
+
227
+ it 'weekly' do
228
+ filename = File.join(@tmp_home, 'weekly.pdf')
229
+ weekly = @reports.write_weekly(filename)
230
+ expect(file_is_pdf(filename))
231
+ end
232
+
233
+ it 'details' do
234
+ filename = File.join(@tmp_home, 'details.pdf')
235
+ details = @reports.write_details(filename)
236
+ expect(file_is_pdf(filename))
237
+ end
238
+ end
239
+
240
+ context 'XLS reports', :pro_account do
241
+ it 'summary' do
242
+ filename = File.join(@tmp_home, 'summary.xls')
243
+ summary = @reports.write_summary(filename)
244
+ expect(file_is_xls(filename))
245
+ end
246
+
247
+ it 'details' do
248
+ filename = File.join(@tmp_home, 'details.xls')
249
+ details = @reports.write_details(filename)
250
+ expect(file_is_xls(filename))
251
+ end
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,145 @@
1
+ describe 'Clients' do
2
+ before :all do
3
+ @toggl = TogglV8::API.new(Testing::API_TOKEN)
4
+ @workspaces = @toggl.workspaces
5
+ @workspace_id = @workspaces.first['id']
6
+ end
7
+
8
+ it 'gets {} if there are no clients' do
9
+ client = @toggl.clients
10
+ expect(client).to be_empty
11
+ end
12
+
13
+ it 'gets {} if there are no workspace clients' do
14
+ client = @toggl.clients(@workspace_id)
15
+ expect(client).to be_empty
16
+ end
17
+
18
+ context 'new client' do
19
+ before :all do
20
+ @client = @toggl.create_client({ 'name' => 'new client +1', 'wid' => @workspace_id })
21
+ client_ids = @toggl.my_clients.map { |c| c['id'] }
22
+ expect(client_ids).to eq [ @client['id'] ]
23
+ end
24
+
25
+ after :all do
26
+ TogglV8SpecHelper.delete_all_clients(@toggl)
27
+ clients = @toggl.my_clients
28
+ expect(clients).to be_empty
29
+ end
30
+
31
+ it 'gets a client' do
32
+ client_ids = @toggl.clients.map { |c| c['id'] }
33
+ expect(client_ids).to eq [ @client['id'] ]
34
+ end
35
+
36
+ it 'gets a workspace client' do
37
+ client_ids = @toggl.clients(@workspace_id).map { |c| c['id'] }
38
+ expect(client_ids).to eq [ @client['id'] ]
39
+ end
40
+
41
+ context 'multiple clients' do
42
+ before :all do
43
+ @client2 = @toggl.create_client({ 'name' => 'new client 2', 'wid' => @workspace_id })
44
+ end
45
+
46
+ after :all do
47
+ @toggl.delete_client(@client2['id'])
48
+ end
49
+
50
+ it 'gets clients' do
51
+ client_ids = @toggl.clients.map { |c| c['id'] }
52
+ expect(client_ids).to match_array [ @client['id'], @client2['id'] ]
53
+ end
54
+
55
+ it 'gets workspace clients' do
56
+ client_ids = @toggl.clients(@workspace_id).map { |c| c['id'] }
57
+ expect(client_ids).to match_array [ @client['id'], @client2['id'] ]
58
+ end
59
+ end
60
+
61
+ it 'creates a client' do
62
+ expect(@client).to_not be nil
63
+ expect(@client['name']).to eq 'new client +1'
64
+ expect(@client['notes']).to eq nil
65
+ expect(@client['wid']).to eq @workspace_id
66
+ end
67
+
68
+ it 'gets client data' do
69
+ client = @toggl.get_client(@client['id'])
70
+ expect(client).to_not be nil
71
+ expect(client['name']).to eq @client['name']
72
+ expect(client['wid']).to eq @client['wid']
73
+ expect(client['notes']).to eq @client['notes']
74
+ expect(client['at']).to_not be nil
75
+ end
76
+
77
+ context 'client projects' do
78
+ it 'gets {} if there are no client projects' do
79
+ projects = @toggl.get_client_projects(@client['id'])
80
+ expect(projects).to be_empty
81
+ end
82
+
83
+ context 'new client projects' do
84
+ before :all do
85
+ @project = @toggl.create_project({ 'name' => 'project', 'wid' => @workspace_id, 'cid' => @client['id'] })
86
+ end
87
+
88
+ after :all do
89
+ TogglV8SpecHelper.delete_all_projects(@toggl)
90
+ end
91
+
92
+ it 'gets a client project' do
93
+ projects = @toggl.get_client_projects(@client['id'])
94
+ project_ids = projects.map { |p| p['id'] }
95
+ expect(project_ids).to eq [ @project['id'] ]
96
+ end
97
+
98
+ it 'gets multiple client projects' do
99
+ project2 = @toggl.create_project({ 'name' => 'project2', 'wid' => @workspace_id, 'cid' => @client['id'] })
100
+
101
+ projects = @toggl.get_client_projects(@client['id'])
102
+ project_ids = projects.map { |p| p['id'] }
103
+ expect(project_ids).to match_array [ @project['id'], project2['id'] ]
104
+
105
+ @toggl.delete_project(project2['id'])
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ context 'updated client' do
112
+ before :each do
113
+ @client = @toggl.create_client({ 'name' => 'client to update', 'wid' => @workspace_id })
114
+ end
115
+
116
+ after :each do
117
+ @toggl.delete_client(@client['id'])
118
+ end
119
+
120
+ it 'updates client data' do
121
+ new_values = {
122
+ 'name' => 'CLIENT-NEW',
123
+ 'notes' => 'NOTES-NEW',
124
+ }
125
+
126
+ client = @toggl.update_client(@client['id'], new_values)
127
+ expect(client).to include(new_values)
128
+ end
129
+
130
+ # :nocov:
131
+ # It appears hourly rate is no longer tied to a client despite the docs:
132
+ # https://github.com/toggl/toggl_api_docs/blob/master/chapters/clients.md#clients
133
+ xit 'updates Pro client data', :pro_account do
134
+ new_values = {
135
+ 'hrate' => '7.77',
136
+ 'cur' => 'USD',
137
+ }
138
+ client = @toggl.update_client(@client['id'], new_values)
139
+
140
+ client = @toggl.get_client(@client['id'])
141
+ expect(client).to include(new_values)
142
+ end
143
+ # :nocov:
144
+ end
145
+ end