sfctl 0.0.4 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43943ce5d78b2cf30c6f6e171bd5ba8568adbce169ee8a9f66a64f1162b80ff8
4
- data.tar.gz: 8960aea77d4cd2d90170005d275625435497a6685a835729728523d62d81ee4a
3
+ metadata.gz: 36e137543a9eed8b4f2fd6c472fd5899d5dc94a3b98fd50d8ae527b79dd671b4
4
+ data.tar.gz: 2cb0c12ea7a79b7fefae5cbbffba604815a7a9629b002049530c165461d4a701
5
5
  SHA512:
6
- metadata.gz: b48ecdfda4052022ad25b1e8008de8148c29e3a88e5b36b33261a597d58fcb49e6ee98b7d41c63443d01ed652d7c304e68a4622a119e707b1a8afe427f4271f2
7
- data.tar.gz: 332ce44447b8f1d2b020eda9b3f47c982a63a8bc4c119432446e93d101b19516fe34934e970dbfd5123cd3365fd4cb98d784b55e30f67011599095688d8e983f
6
+ metadata.gz: f0e23db4406b9d253250398269a216a819e1a55afd11570f9515ab909e7b230b316cdbd8eedf28c910c78772e0aece324dc0adad1a4a0210b253b700a64e1df1
7
+ data.tar.gz: 292842c5de401507c927181a9c346e0a324a9944e5653fe4be752d8951dfc1bcac8e902d6e137eb0426ed0c6fa5636568fe2377a14361eb6a2d93d0a4c7a0d0a
data/.gitignore CHANGED
@@ -12,3 +12,4 @@
12
12
  .DS_Store
13
13
  sfctl-*.gem
14
14
  /.sflink
15
+ .byebug_history
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sfctl (0.0.4)
4
+ sfctl (1.0.1)
5
5
  faraday (~> 1.0)
6
6
  pastel (~> 0.7)
7
7
  rake (~> 12.0)
@@ -23,7 +23,7 @@ PATH
23
23
  tty-progressbar (~> 0.17.0)
24
24
  tty-prompt (~> 0.21.0)
25
25
  tty-reader (~> 0.7.0)
26
- tty-screen (~> 0.7.1)
26
+ tty-screen (~> 0.8.1)
27
27
  tty-spinner (~> 0.9.3)
28
28
  tty-table (~> 0.11.0)
29
29
  tty-tree (~> 0.4.0)
@@ -34,12 +34,12 @@ GEM
34
34
  specs:
35
35
  addressable (2.7.0)
36
36
  public_suffix (>= 2.0.2, < 5.0)
37
- ast (2.4.0)
38
- byebug (11.1.1)
39
- coderay (1.1.2)
37
+ ast (2.4.1)
38
+ byebug (11.1.3)
39
+ coderay (1.1.3)
40
40
  crack (0.4.3)
41
41
  safe_yaml (~> 1.0.0)
42
- diff-lcs (1.3)
42
+ diff-lcs (1.4.4)
43
43
  docile (1.3.2)
44
44
  equatable (0.6.1)
45
45
  faraday (1.0.1)
@@ -48,15 +48,14 @@ GEM
48
48
  rspec-core (~> 3.0)
49
49
  ruby-progressbar (~> 1.4)
50
50
  hashdiff (1.0.1)
51
- jaro_winkler (1.5.4)
52
51
  kramdown (1.16.2)
53
52
  method_source (1.0.0)
54
53
  multipart-post (2.1.1)
55
54
  necromancer (0.5.1)
56
- parallel (1.19.1)
57
- parser (2.7.1.1)
58
- ast (~> 2.4.0)
59
- pastel (0.7.3)
55
+ parallel (1.19.2)
56
+ parser (2.7.1.4)
57
+ ast (~> 2.4.1)
58
+ pastel (0.7.4)
60
59
  equatable (~> 0.6)
61
60
  tty-color (~> 0.5)
62
61
  pry (0.13.1)
@@ -65,32 +64,36 @@ GEM
65
64
  pry-byebug (3.9.0)
66
65
  byebug (~> 11.0)
67
66
  pry (~> 0.13.0)
68
- public_suffix (4.0.4)
67
+ public_suffix (4.0.5)
69
68
  rainbow (3.0.0)
70
69
  rake (12.3.3)
70
+ regexp_parser (1.7.1)
71
71
  rexml (3.2.4)
72
- rouge (3.18.0)
72
+ rouge (3.21.0)
73
73
  rspec (3.9.0)
74
74
  rspec-core (~> 3.9.0)
75
75
  rspec-expectations (~> 3.9.0)
76
76
  rspec-mocks (~> 3.9.0)
77
- rspec-core (3.9.1)
78
- rspec-support (~> 3.9.1)
79
- rspec-expectations (3.9.1)
77
+ rspec-core (3.9.2)
78
+ rspec-support (~> 3.9.3)
79
+ rspec-expectations (3.9.2)
80
80
  diff-lcs (>= 1.2.0, < 2.0)
81
81
  rspec-support (~> 3.9.0)
82
82
  rspec-mocks (3.9.1)
83
83
  diff-lcs (>= 1.2.0, < 2.0)
84
84
  rspec-support (~> 3.9.0)
85
- rspec-support (3.9.2)
86
- rubocop (0.81.0)
87
- jaro_winkler (~> 1.5.1)
85
+ rspec-support (3.9.3)
86
+ rubocop (0.88.0)
88
87
  parallel (~> 1.10)
89
- parser (>= 2.7.0.1)
88
+ parser (>= 2.7.1.1)
90
89
  rainbow (>= 2.2.2, < 4.0)
90
+ regexp_parser (>= 1.7)
91
91
  rexml
92
+ rubocop-ast (>= 0.1.0, < 1.0)
92
93
  ruby-progressbar (~> 1.7)
93
94
  unicode-display_width (>= 1.4.0, < 2.0)
95
+ rubocop-ast (0.1.0)
96
+ parser (>= 2.7.0.1)
94
97
  ruby-progressbar (1.10.1)
95
98
  safe_yaml (1.0.5)
96
99
  simplecov (0.18.5)
@@ -151,7 +154,7 @@ GEM
151
154
  tty-cursor (~> 0.7)
152
155
  tty-screen (~> 0.7)
153
156
  wisper (~> 2.0.0)
154
- tty-screen (0.7.1)
157
+ tty-screen (0.8.1)
155
158
  tty-spinner (0.9.3)
156
159
  tty-cursor (~> 0.7)
157
160
  tty-table (0.11.0)
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # sfctl [![Build status](https://badge.buildkite.com/22ecc67f358163f4714383ff0fde8e847d1e3ae488fc10312f.svg)](https://buildkite.com/starfish/sf-control)
2
+ [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Falphatier-works%2Fsfctl.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Falphatier-works%2Fsfctl?ref=badge_shield)
2
3
 
3
4
  ```
4
5
  sfctl is a command line interface for the Starfish API.
@@ -270,3 +271,7 @@ Uploading to starfish.team: [IN PROGRESS|DONE]
270
271
 
271
272
  [NEXT CONNECTION]
272
273
  ```
274
+
275
+
276
+ ## License
277
+ [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Falphatier-works%2Fsfctl.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Falphatier-works%2Fsfctl?ref=badge_large)
@@ -21,7 +21,7 @@ module Sfctl
21
21
  end
22
22
 
23
23
  class_option :"no-color", type: :boolean, default: false, desc: 'Disable colorization in output'
24
- class_option :"starfish-host", type: :string, default: 'https://starfish.team',
24
+ class_option :"starfish-host", type: :string, default: 'https://app.starfish.team',
25
25
  desc: 'The starfish API endpoint',
26
26
  banner: 'HOST'
27
27
 
@@ -13,8 +13,10 @@ module Sfctl
13
13
  LINK_CONFIG_PATH = "#{Dir.pwd}/#{LINK_CONFIG_FILENAME}"
14
14
 
15
15
  TOGGL_PROVIDER = 'toggl'
16
+ HARVEST_PROVIDER = 'harvest'
16
17
  PROVIDERS_LIST = [
17
- TOGGL_PROVIDER
18
+ TOGGL_PROVIDER,
19
+ HARVEST_PROVIDER
18
20
  ].freeze
19
21
 
20
22
  def_delegators :command, :run
@@ -1,7 +1,7 @@
1
1
  require 'pastel'
2
2
  require 'tty-table'
3
3
  require_relative '../../command'
4
- require_relative '../../starfish'
4
+ require_relative '../../starfish/client'
5
5
 
6
6
  module Sfctl
7
7
  module Commands
@@ -15,7 +15,7 @@ module Sfctl
15
15
  def execute(output: $stdout)
16
16
  return unless config_present?(output)
17
17
 
18
- success, data = Starfish.account_assignments(@options['starfish-host'], @options['all'], access_token)
18
+ success, data = Starfish::Client.account_assignments(@options['starfish-host'], @options['all'], access_token)
19
19
 
20
20
  unless success
21
21
  output.puts @pastel.red('Something went wrong. Unable to fetch assignments')
@@ -1,7 +1,7 @@
1
1
  require 'pastel'
2
2
  require 'tty-table'
3
3
  require_relative '../../command'
4
- require_relative '../../starfish'
4
+ require_relative '../../starfish/client'
5
5
 
6
6
  module Sfctl
7
7
  module Commands
@@ -15,7 +15,7 @@ module Sfctl
15
15
  def execute(output: $stdout)
16
16
  return unless config_present?(output)
17
17
 
18
- success, info = Starfish.account_info(@options['starfish-host'], access_token)
18
+ success, info = Starfish::Client.account_info(@options['starfish-host'], access_token)
19
19
 
20
20
  unless success
21
21
  output.puts @pastel.red('Something went wrong. Unable to fetch account info')
@@ -1,5 +1,5 @@
1
1
  require_relative '../../command'
2
- require_relative '../../starfish'
2
+ require_relative '../../starfish/client'
3
3
  require 'pastel'
4
4
  require 'tty-prompt'
5
5
  require 'tty-spinner'
@@ -23,7 +23,7 @@ module Sfctl
23
23
  private
24
24
 
25
25
  def token_valid?(access_token)
26
- Starfish.check_authorization(@options['starfish-host'], access_token)
26
+ Starfish::Client.check_authorization(@options['starfish-host'], access_token)
27
27
  end
28
28
 
29
29
  def token_accepted_message
@@ -3,8 +3,9 @@ require 'pastel'
3
3
  require 'tty-spinner'
4
4
  require 'tty-prompt'
5
5
  require_relative '../../../command'
6
- require_relative '../../../starfish'
7
- require_relative '../../../toggl'
6
+ require_relative '../../../starfish/client'
7
+ require_relative '../../../toggl/client'
8
+ require_relative '../../../harvest/client'
8
9
 
9
10
  module Sfctl
10
11
  module Commands
@@ -22,7 +23,7 @@ module Sfctl
22
23
 
23
24
  ltoken = access_token
24
25
  config.delete(:access_token)
25
- success, data = Starfish.account_assignments(@options['starfish-host'], @options['all'], ltoken)
26
+ success, data = Starfish::Client.account_assignments(@options['starfish-host'], @options['all'], ltoken)
26
27
  unless success
27
28
  output.puts @pastel.red('Something went wrong. Unable to fetch assignments')
28
29
  return
@@ -38,14 +39,20 @@ module Sfctl
38
39
 
39
40
  assignment_obj = select_assignment(assignments)
40
41
 
42
+ setup_connection!(provider, output, assignment_obj)
43
+ end
44
+
45
+ private
46
+
47
+ def setup_connection!(provider, output, assignment_obj)
41
48
  case provider
42
49
  when TOGGL_PROVIDER
43
50
  setup_toggl_connection!(output, assignment_obj)
51
+ when HARVEST_PROVIDER
52
+ setup_harvest_connection!(output, assignment_obj)
44
53
  end
45
54
  end
46
55
 
47
- private
48
-
49
56
  def clear_conf_and_print_success!(output)
50
57
  delete_providers_from_link_config!
51
58
  save_link_config!
@@ -75,14 +82,22 @@ module Sfctl
75
82
  list
76
83
  end
77
84
 
78
- def setup_toggl_connection!(output, assignment_obj) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
85
+ def ask_for_billable
86
+ @prompt.select('Billable?', %w[yes no both])
87
+ end
88
+
89
+ def ask_for_rounding
90
+ @prompt.select('Rounding?', %w[on off])
91
+ end
92
+
93
+ def setup_toggl_connection!(output, assignment_obj) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
79
94
  spinner = ::TTY::Spinner.new('[:spinner] Loading ...')
80
95
 
81
96
  assignment_id = assignment_obj['id']
82
97
  toggl_token = read_link_config['providers'][TOGGL_PROVIDER]['access_token']
83
98
 
84
99
  spinner.auto_spin
85
- _success, workspaces = Toggl.workspaces(toggl_token)
100
+ _success, workspaces = Toggl::Client.workspaces(toggl_token)
86
101
  spinner.pause
87
102
  output.puts
88
103
  workspace = @prompt.select('Please select Workspace:') do |menu|
@@ -93,7 +108,7 @@ module Sfctl
93
108
  workspace_id = workspace['id']
94
109
 
95
110
  spinner.resume
96
- _success, projects = Toggl.workspace_projects(toggl_token, workspace_id)
111
+ _success, projects = Toggl::Client.workspace_projects(toggl_token, workspace_id)
97
112
 
98
113
  if projects.nil? || projects.empty?
99
114
  spinner.stop
@@ -113,7 +128,7 @@ module Sfctl
113
128
  spinner.resume
114
129
  tasks_objs = []
115
130
  project_ids.each do |pj_id|
116
- _success, tasks = Toggl.project_tasks(toggl_token, pj_id)
131
+ _success, tasks = Toggl::Client.project_tasks(toggl_token, pj_id)
117
132
  tasks_objs << tasks
118
133
  end
119
134
  tasks_objs.flatten!
@@ -131,9 +146,9 @@ module Sfctl
131
146
  output.puts @pastel.yellow("You don't have tasks. Continue...")
132
147
  end
133
148
 
134
- billable = @prompt.select('Billable?', %w[yes no both])
149
+ billable = ask_for_billable
135
150
 
136
- rounding = @prompt.select('Rounding?', %w[on off])
151
+ rounding = ask_for_rounding
137
152
 
138
153
  config.set("connections.#{assignment_id}.name", value: assignment_obj['name'])
139
154
  config.set("connections.#{assignment_id}.service", value: assignment_obj['service'])
@@ -146,6 +161,66 @@ module Sfctl
146
161
 
147
162
  clear_conf_and_print_success!(output)
148
163
  end
164
+
165
+ def setup_harvest_connection!(output, assignment_obj) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
166
+ spinner = ::TTY::Spinner.new('[:spinner] Loading ...')
167
+
168
+ assignment_id = assignment_obj['id']
169
+ harvest_account_id = read_link_config['providers'][HARVEST_PROVIDER]['account_id']
170
+ harvest_token = read_link_config['providers'][HARVEST_PROVIDER]['access_token']
171
+
172
+ spinner.auto_spin
173
+ _success, projects = Harvest::Client.projects(harvest_account_id, harvest_token)
174
+
175
+ if projects.nil? || projects.empty?
176
+ spinner.stop
177
+ error_message = "There is no projects. Please visit #{HARVEST_PROVIDER} and create them before continue."
178
+ output.puts @pastel.red(error_message)
179
+ return
180
+ end
181
+
182
+ spinner.pause
183
+ output.puts
184
+ project = @prompt.select('Please select Project:') do |menu|
185
+ projects.each do |pj|
186
+ menu.choice name: pj['name'], value: pj
187
+ end
188
+ end
189
+ project_id = project['id']
190
+
191
+ spinner.resume
192
+ _success, tasks = Harvest::Client.tasks(harvest_account_id, harvest_token)
193
+
194
+ if tasks.nil? || tasks.empty?
195
+ spinner.stop
196
+ error_message = "There is no tasks. Please visit #{HARVEST_PROVIDER} and create them before continue."
197
+ output.puts @pastel.red(error_message)
198
+ return
199
+ end
200
+
201
+ spinner.success
202
+ output.puts
203
+ task = @prompt.select('Please select Task:') do |menu|
204
+ tasks.each do |t|
205
+ menu.choice name: t['name'], value: t
206
+ end
207
+ end
208
+ task_id = task['id']
209
+
210
+ billable = ask_for_billable
211
+
212
+ rounding = ask_for_rounding
213
+
214
+ config.set("connections.#{assignment_id}.name", value: assignment_obj['name'])
215
+ config.set("connections.#{assignment_id}.service", value: assignment_obj['service'])
216
+ config.set("connections.#{assignment_id}.provider", value: HARVEST_PROVIDER)
217
+ config.set("connections.#{assignment_id}.project_id", value: project_id.to_s)
218
+ config.set("connections.#{assignment_id}.task_id", value: task_id.to_s)
219
+ config.set("connections.#{assignment_id}.billable", value: billable)
220
+ config.set("connections.#{assignment_id}.rounding", value: rounding)
221
+
222
+ clear_conf_and_print_success!(output)
223
+ end
149
224
  end
150
225
  end
151
226
  end
@@ -29,23 +29,41 @@ module Sfctl
29
29
 
30
30
  def print_connections(output)
31
31
  config.fetch(:connections).each_key do |assignment_id|
32
+ print_header!(output, assignment_id)
33
+
32
34
  case config.fetch(:connections, assignment_id, :provider)
33
35
  when TOGGL_PROVIDER
34
36
  print_toggl_connection!(output, assignment_id)
37
+ when HARVEST_PROVIDER
38
+ print_harvest_connection!(output, assignment_id)
35
39
  end
40
+
41
+ print_footer!(output, assignment_id)
36
42
  end
37
43
  end
38
44
 
39
- def print_toggl_connection!(output, assignment_id) # rubocop:disable Metrics/AbcSize
45
+ def print_header!(output, assignment_id)
40
46
  output.puts "Connection: #{config.fetch(:connections, assignment_id, :name)}"
41
47
  output.puts " service: #{config.fetch(:connections, assignment_id, :service)}"
48
+ end
49
+
50
+ def print_footer!(output, assignment_id)
51
+ output.puts " billable: #{config.fetch(:connections, assignment_id, :billable)}"
52
+ output.puts " rounding: #{config.fetch(:connections, assignment_id, :rounding)}"
53
+ output.puts
54
+ end
55
+
56
+ def print_toggl_connection!(output, assignment_id)
42
57
  output.puts " provider: #{TOGGL_PROVIDER}"
43
58
  output.puts " workspace_id: #{config.fetch(:connections, assignment_id, :workspace_id)}"
44
59
  output.puts " project_ids: #{config.fetch(:connections, assignment_id, :project_ids)}"
45
60
  output.puts " task_ids: #{config.fetch(:connections, assignment_id, :task_ids)}"
46
- output.puts " billable: #{config.fetch(:connections, assignment_id, :billable)}"
47
- output.puts " rounding: #{config.fetch(:connections, assignment_id, :rounding)}"
48
- output.puts
61
+ end
62
+
63
+ def print_harvest_connection!(output, assignment_id)
64
+ output.puts " provider: #{HARVEST_PROVIDER}"
65
+ output.puts " project_id: #{config.fetch(:connections, assignment_id, :project_id)}"
66
+ output.puts " task_id: #{config.fetch(:connections, assignment_id, :task_id)}"
49
67
  end
50
68
  end
51
69
  end
@@ -28,7 +28,7 @@ module Sfctl
28
28
  else
29
29
  output.puts "Provider: #{@pastel.cyan(provider)}"
30
30
  info.each_key do |k|
31
- output.puts " #{k.upcase}: #{info[k]}"
31
+ output.puts " #{k.upcase}: #{@pastel.magenta(info[k])}"
32
32
  end
33
33
  end
34
34
  end
@@ -18,11 +18,13 @@ module Sfctl
18
18
  prompt = ::TTY::Prompt.new
19
19
  provider = prompt.select('Setting up:', PROVIDERS_LIST)
20
20
 
21
- !ask_for_replace(output, prompt) && return unless config.fetch("providers.#{TOGGL_PROVIDER}").nil?
21
+ !ask_for_replace(output, prompt) && return unless config.fetch("providers.#{provider}").nil?
22
22
 
23
23
  case provider
24
24
  when TOGGL_PROVIDER
25
25
  setup_toggl_provider!(output, prompt)
26
+ when HARVEST_PROVIDER
27
+ setup_harvest_provider!(output, prompt)
26
28
  end
27
29
  end
28
30
 
@@ -33,22 +35,46 @@ module Sfctl
33
35
  prompt.yes?('Do you want to replace it?')
34
36
  end
35
37
 
36
- def save_toggl_config!(output, access_token)
37
- config.set("providers.#{TOGGL_PROVIDER}.access_token", value: access_token)
38
+ def correct?(prompt)
39
+ prompt.yes?('Is that information correct?')
40
+ end
41
+
42
+ def save_config_and_print_message!(output)
38
43
  save_config!
39
44
  output.puts @pastel.green('Everything saved.')
40
45
  end
41
46
 
47
+ def save_toggl_config!(output, access_token)
48
+ config.set("providers.#{TOGGL_PROVIDER}.access_token", value: access_token)
49
+ save_config_and_print_message!(output)
50
+ end
51
+
42
52
  def setup_toggl_provider!(output, prompt)
43
53
  output.puts
44
54
  access_token = prompt.ask("Your access token at [#{@pastel.green(TOGGL_PROVIDER)}]:", required: true)
45
- is_correct = prompt.yes?('Is that information correct?')
46
- if is_correct
55
+ if correct?(prompt)
47
56
  save_toggl_config!(output, access_token)
48
57
  else
49
58
  setup_toggl_provider!(output, prompt)
50
59
  end
51
60
  end
61
+
62
+ def save_harvest_config!(output, account_id, access_token)
63
+ config.set("providers.#{HARVEST_PROVIDER}.account_id", value: account_id)
64
+ config.set("providers.#{HARVEST_PROVIDER}.access_token", value: access_token)
65
+ save_config_and_print_message!(output)
66
+ end
67
+
68
+ def setup_harvest_provider!(output, prompt)
69
+ output.puts
70
+ account_id = prompt.ask("Your Account ID at [#{@pastel.green(HARVEST_PROVIDER)}]:", required: true)
71
+ access_token = prompt.ask("Your Token at [#{@pastel.green(HARVEST_PROVIDER)}]:", required: true)
72
+ if correct?(prompt)
73
+ save_harvest_config!(output, account_id, access_token)
74
+ else
75
+ setup_harvest_provider!(output, prompt)
76
+ end
77
+ end
52
78
  end
53
79
  end
54
80
  end
@@ -4,8 +4,9 @@ require 'tty-prompt'
4
4
  require 'tty-spinner'
5
5
  require 'tty-table'
6
6
  require_relative '../../command'
7
- require_relative '../../starfish'
8
- require_relative '../../toggl'
7
+ require_relative '../../starfish/client'
8
+ require_relative '../../toggl/sync'
9
+ require_relative '../../harvest/sync'
9
10
 
10
11
  module Sfctl
11
12
  module Commands
@@ -20,7 +21,7 @@ module Sfctl
20
21
  def execute(output: $stdout)
21
22
  return if !config_present?(output) || !link_config_present?(output)
22
23
 
23
- if read_link_config['connections'].length.zero?
24
+ if connections.length.zero?
24
25
  output.puts @pastel.red('Please add a connection before continue.')
25
26
  return
26
27
  end
@@ -33,8 +34,12 @@ module Sfctl
33
34
 
34
35
  private
35
36
 
37
+ def connections
38
+ @connections ||= read_link_config.fetch('connections', [])
39
+ end
40
+
36
41
  def assignments_from_connections
37
- read_link_config['connections'].map do |con|
42
+ connections.map do |con|
38
43
  id = con[0]
39
44
  asmnt = con[1]
40
45
  {
@@ -69,38 +74,47 @@ module Sfctl
69
74
  def sync_assignments(output, list)
70
75
  list.each do |assignment|
71
76
  assignment_id = assignment['id'].to_s
72
- connection = read_link_config['connections'].select { |c| c == assignment_id }
73
-
74
- if connection.empty?
75
- output.puts @pastel.red("Unable to find a connection for assignment \"#{assignment['name']}\"")
76
- next
77
- end
78
-
77
+ connection = connections.select { |c| c == assignment_id }
79
78
  sync(output, assignment, connection[assignment_id])
80
79
  end
81
80
  end
82
81
 
83
- def sync(output, assignment, connection)
84
- case connection['provider']
85
- when TOGGL_PROVIDER
86
- sync_with_toggl!(output, assignment, connection)
87
- end
88
- end
89
-
90
- def sync_with_toggl!(output, assignment, connection) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
82
+ def sync(output, assignment, connection) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
91
83
  output.puts "Synchronizing: #{@pastel.cyan("[#{assignment['name']} / #{assignment['service']}]")}"
92
84
 
93
- success, next_report = Starfish.next_report(@options['starfish-host'], access_token, assignment['id'])
85
+ success, next_report = Starfish::Client.next_report(@options['starfish-host'], access_token, assignment['id'])
94
86
 
95
87
  print_no_next_reporting_segment(output) && return if !success || next_report.empty?
96
88
 
97
- time_entries = load_data_from_toggl(output, next_report, connection)
89
+ time_entries = load_time_entries(output, next_report, connection)
98
90
 
99
91
  print_dry_run_enabled(output) && return if @options['dry_run']
100
92
 
101
93
  print_report_contains_data(output, next_report) && return if touchy?(next_report)
102
94
 
103
- uploading_to_starfish(output, assignment, time_entries)
95
+ uploading_to_starfish(output, assignment, time_entries, connection) if time_entries.any?
96
+ end
97
+
98
+ def report_interval(record)
99
+ start_date = Date.parse("#{record['year']}-#{record['month']}-01")
100
+ end_date = start_date.next_month.prev_day
101
+ [start_date, end_date]
102
+ end
103
+
104
+ def load_time_entries(output, next_report, connection)
105
+ output.puts "Next Report: #{@pastel.cyan(report_name(next_report))}"
106
+ next_report_interval = report_interval(next_report)
107
+
108
+ case connection['provider']
109
+ when TOGGL_PROVIDER
110
+ Toggl::Sync.load_data(
111
+ output, connection, read_link_config['providers'][TOGGL_PROVIDER], @pastel, next_report_interval
112
+ )
113
+ when HARVEST_PROVIDER
114
+ Harvest::Sync.load_data(
115
+ output, connection, read_link_config['providers'][HARVEST_PROVIDER], @pastel, next_report_interval
116
+ )
117
+ end
104
118
  end
105
119
 
106
120
  def touchy?(next_report)
@@ -133,93 +147,21 @@ module Sfctl
133
147
  true
134
148
  end
135
149
 
136
- def load_data_from_toggl(output, next_report, connection)
137
- output.puts "Next Report: #{@pastel.cyan(report_name(next_report))}"
138
-
139
- spinner = TTY::Spinner.new("Loaded data from #{TOGGL_PROVIDER}: [:spinner]", format: :dots)
140
- spinner.auto_spin
141
-
142
- time_entries = get_toggle_time_entries(next_report, connection)
143
-
144
- spinner.success(@pastel.green('Done'))
145
-
146
- table = TTY::Table.new %w[Date Comment Time], time_entries_table_rows(time_entries)
147
- output.puts
148
- output.print table.render(:unicode, padding: [0, 1], alignments: %i[left left right])
149
- output.puts
150
- output.puts
151
-
152
- time_entries['data']
153
- end
154
-
155
- def time_entries_table_rows(time_entries)
156
- rows = time_entries['data'].sort_by { |te| te['start'] }.map do |te|
157
- [
158
- Date.parse(te['start']).to_s,
159
- te['description'],
160
- "#{humanize_duration(te['dur'])}h"
161
- ]
162
- end
163
- rows.push(['Total:', '', "#{humanize_duration(time_entries['total_grand'])}h"])
164
- rows
165
- end
166
-
167
- def get_toggle_time_entries(next_report, connection)
168
- _success, data = Toggl.time_entries(
169
- read_link_config['providers'][TOGGL_PROVIDER]['access_token'], time_entries_params(next_report, connection)
170
- )
171
-
172
- data
173
- end
174
-
175
- def time_entries_params(next_report, connection)
176
- start_date = Date.parse("#{next_report['year']}-#{next_report['month']}-01")
177
- end_date = start_date.next_month.prev_day
178
- params = {
179
- workspace_id: connection['workspace_id'],
180
- project_ids: connection['project_ids'],
181
- billable: connection['billable'],
182
- rounding: connection['rounding'],
183
- since: start_date.to_s,
184
- until: end_date.to_s
185
- }
186
- params[:task_ids] = connection['task_ids'] if connection['task_ids'].length.positive?
187
- params
188
- end
189
-
190
- def humanize_duration(milliseconds)
191
- return '0' if milliseconds.nil?
192
-
193
- seconds = milliseconds / 1000
194
- minutes = seconds / 60
195
- int = (minutes / 60).ceil
196
- dec = minutes % 60
197
- amount = (dec * 100) / 60
198
- amount = if dec.zero?
199
- ''
200
- elsif amount.to_s.length == 1
201
- ".0#{amount}"
202
- else
203
- ".#{amount}"
204
- end
205
- "#{int}#{amount}"
206
- end
207
-
208
- def assignment_items(time_entries)
209
- time_entries.map do |te|
210
- {
211
- time: humanize_duration(te['dur']).to_f,
212
- date: Date.parse(te['start']).to_s,
213
- comment: te['description']
214
- }
150
+ def assignment_items(time_entries, connection)
151
+ case connection['provider']
152
+ when TOGGL_PROVIDER
153
+ Toggl::Sync.assignment_items(time_entries)
154
+ when HARVEST_PROVIDER
155
+ Harvest::Sync.assignment_items(time_entries, connection)
215
156
  end
216
157
  end
217
158
 
218
- def uploading_to_starfish(output, assignment, time_entries)
159
+ def uploading_to_starfish(output, assignment, time_entries, connection)
219
160
  spinner = TTY::Spinner.new('Uploading to starfish.team: [:spinner]', format: :dots)
220
161
  spinner.auto_spin
221
- success = Starfish.update_next_report(
222
- @options['starfish-host'], access_token, assignment['id'], assignment_items(time_entries)
162
+
163
+ success = Starfish::Client.update_next_report(
164
+ @options['starfish-host'], access_token, assignment['id'], assignment_items(time_entries, connection)
223
165
  )
224
166
  print_upload_results(output, success, spinner)
225
167
  end
@@ -0,0 +1,44 @@
1
+ require 'faraday'
2
+ require 'json'
3
+
4
+ module Sfctl
5
+ module Harvest
6
+ module Client
7
+ API_V2_PATH = 'api/v2/'.freeze
8
+
9
+ def self.conn(account_id, token)
10
+ raise 'Please set Harvest provider before continue.' if account_id.nil? || token.nil?
11
+
12
+ headers = {
13
+ 'Content-Type' => 'application/json',
14
+ 'Harvest-Account-ID' => account_id,
15
+ 'Authorization' => "Bearer #{token}"
16
+ }
17
+
18
+ Faraday.new(url: "https://api.harvestapp.com/#{API_V2_PATH}", headers: headers) do |builder|
19
+ builder.request :retry
20
+ builder.adapter :net_http
21
+ end
22
+ end
23
+
24
+ def self.parsed_response(response, key)
25
+ [response.status == 200, JSON.parse(response.body)[key]]
26
+ end
27
+
28
+ def self.projects(account_id, token)
29
+ response = conn(account_id, token).get('projects')
30
+ parsed_response(response, 'projects')
31
+ end
32
+
33
+ def self.tasks(account_id, token)
34
+ response = conn(account_id, token).get('tasks')
35
+ parsed_response(response, 'tasks')
36
+ end
37
+
38
+ def self.time_entries(account_id, token, params)
39
+ response = conn(account_id, token).get('time_entries', params)
40
+ parsed_response(response, 'time_entries')
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,83 @@
1
+ require 'tty-spinner'
2
+ require 'tty-table'
3
+ require_relative '../command'
4
+ require_relative './client'
5
+
6
+ module Sfctl
7
+ module Harvest
8
+ module Sync
9
+ def self.load_data(output, connection, harvest_config, pastel, report_interval)
10
+ spinner = TTY::Spinner.new("Loaded data from #{Sfctl::Command::HARVEST_PROVIDER}: [:spinner]", format: :dots)
11
+ spinner.auto_spin
12
+
13
+ time_entries = get_time_entries(connection, harvest_config, report_interval)
14
+
15
+ spinner.success(pastel.green('Done'))
16
+
17
+ table = TTY::Table.new %w[Date Comment Time], time_entries_table_rows(time_entries, connection)
18
+ output.puts
19
+ output.print table.render(:unicode, padding: [0, 1], alignments: %i[left left right])
20
+ output.puts
21
+ output.puts
22
+
23
+ time_entries
24
+ end
25
+
26
+ def self.get_time_entries(connection, harvest_config, report_interval)
27
+ _success, data = Harvest::Client.time_entries(
28
+ harvest_config['account_id'],
29
+ harvest_config['access_token'],
30
+ time_entries_params(connection, report_interval)
31
+ )
32
+
33
+ data
34
+ end
35
+
36
+ def self.hours_field(rounding)
37
+ return 'rounded_hours' if rounding == 'on'
38
+
39
+ 'hours'
40
+ end
41
+
42
+ def self.time_entries_table_rows(time_entries, connection)
43
+ hours_field = hours_field(connection['rounding'])
44
+ rows = time_entries.sort_by { |te| te['spent_date'] }.map do |te|
45
+ [
46
+ te['spent_date'],
47
+ te['notes'],
48
+ "#{te[hours_field]}h"
49
+ ]
50
+ end
51
+ total_grand = time_entries.map { |te| te[hours_field] }.sum
52
+ rows.push(['Total:', '', "#{total_grand}h"])
53
+ rows
54
+ end
55
+
56
+ def self.time_entries_params(connection, report_interval)
57
+ start_date, end_date = report_interval
58
+ params = {
59
+ project_id: connection['project_id'],
60
+ task_id: connection['task_id'],
61
+ from: start_date.to_s,
62
+ to: end_date.to_s
63
+ }
64
+ params[:is_billed] = connection['billable'] == 'yes' unless connection['billable'] == 'both'
65
+ params
66
+ end
67
+
68
+ def self.assignment_items(time_entries, connection)
69
+ hours_field = hours_field(connection['rounding'])
70
+ time_entries.map do |te|
71
+ hours = te[hours_field]
72
+ time_seconds = hours * 60 * 60
73
+ {
74
+ time_seconds: time_seconds.round,
75
+ date: te['spent_date'].to_s,
76
+ comment: te['notes'],
77
+ external_id: te['id'].to_s
78
+ }
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,53 @@
1
+ require 'faraday'
2
+ require 'json'
3
+
4
+ module Sfctl
5
+ module Starfish
6
+ module Client
7
+ def self.conn(endpoint, token)
8
+ raise 'Before continue please pass endpoint and token.' if endpoint.nil? || token.nil?
9
+
10
+ headers = {
11
+ 'Content-Type' => 'application/json',
12
+ 'X-Starfish-Auth' => token
13
+ }
14
+ Faraday.new(url: "#{endpoint}/api/v1", headers: headers) do |builder|
15
+ builder.request :retry
16
+ builder.adapter :net_http
17
+ end
18
+ end
19
+
20
+ def self.parsed_response(response)
21
+ [response.status == 200, JSON.parse(response.body)]
22
+ end
23
+
24
+ def self.check_authorization(endpoint, token)
25
+ response = conn(endpoint, token).get('profile')
26
+ response.status == 200
27
+ end
28
+
29
+ def self.account_info(endpoint, token)
30
+ response = conn(endpoint, token).get('profile')
31
+ parsed_response(response)
32
+ end
33
+
34
+ def self.account_assignments(endpoint, all, token)
35
+ api_conn = conn(endpoint, token)
36
+ response = all ? api_conn.get('assignments?all=1') : api_conn.get('assignments')
37
+ parsed_response(response)
38
+ end
39
+
40
+ def self.next_report(endpoint, token, assignment_id)
41
+ api_conn = conn(endpoint, token)
42
+ response = api_conn.get("assignments/#{assignment_id}/next_report")
43
+ parsed_response(response)
44
+ end
45
+
46
+ def self.update_next_report(endpoint, token, assignment_id, items)
47
+ api_conn = conn(endpoint, token)
48
+ response = api_conn.put("assignments/#{assignment_id}/next_report", JSON.generate(items: items))
49
+ response.status == 204
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,51 @@
1
+ require 'faraday'
2
+ require 'json'
3
+
4
+ module Sfctl
5
+ module Toggl
6
+ module Client
7
+ DEFAULT_API_PATH = 'api/v8/'.freeze
8
+ REPORTS_API_PATH = 'reports/api/v2/'.freeze
9
+
10
+ def self.conn(token, api = 'default')
11
+ raise 'Please set toggl provider before continue.' if token.nil?
12
+
13
+ api_path = api == 'reports' ? REPORTS_API_PATH : DEFAULT_API_PATH
14
+
15
+ headers = { 'Content-Type' => 'application/json' }
16
+ Faraday.new(url: "https://#{token}:api_token@www.toggl.com/#{api_path}", headers: headers) do |builder|
17
+ builder.request :retry
18
+ builder.adapter :net_http
19
+ end
20
+ end
21
+
22
+ def self.parsed_response(response)
23
+ [response.status == 200, JSON.parse(response.body)]
24
+ end
25
+
26
+ def self.workspaces(token)
27
+ response = conn(token).get('workspaces')
28
+ parsed_response(response)
29
+ end
30
+
31
+ def self.workspace_projects(token, workspace_id)
32
+ response = conn(token).get("workspaces/#{workspace_id}/projects")
33
+ parsed_response(response)
34
+ end
35
+
36
+ def self.project_tasks(token, project_id)
37
+ response = conn(token).get("workspaces/#{project_id}/tasks")
38
+
39
+ return [] if response.body.length.zero?
40
+
41
+ parsed_response(response)
42
+ end
43
+
44
+ def self.time_entries(token, params)
45
+ params[:user_agent] = 'api_test'
46
+ response = conn(token, 'reports').get('details', params)
47
+ parsed_response(response)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,118 @@
1
+ require 'tty-spinner'
2
+ require 'tty-table'
3
+ require_relative '../command'
4
+ require_relative './client'
5
+
6
+ module Sfctl
7
+ module Toggl
8
+ module Sync
9
+ def self.load_data(output, connection, toggl_config, pastel, report_interval)
10
+ spinner = TTY::Spinner.new("Loaded data from #{Sfctl::Command::TOGGL_PROVIDER}: [:spinner]", format: :dots)
11
+ spinner.auto_spin
12
+
13
+ time_entries, error = get_time_entries(connection, toggl_config, report_interval)
14
+
15
+ if error
16
+ spinner.error
17
+ output.puts pastel.red(error)
18
+ else
19
+ spinner.success(pastel.green('Done'))
20
+ end
21
+
22
+ table = TTY::Table.new %w[Date Comment Time], time_entries_table_rows(time_entries)
23
+ output.puts
24
+ output.print table.render(:unicode, padding: [0, 1], alignments: %i[left left right])
25
+ output.puts
26
+ output.puts
27
+
28
+ time_entries
29
+ end
30
+
31
+ def self.time_entries_table_rows(time_entries)
32
+ rows = time_entries.sort_by { |te| te['start'] }.map do |te|
33
+ [
34
+ Date.parse(te['start']).to_s,
35
+ te['description'],
36
+ "#{humanize_duration(te['dur'])}h"
37
+ ]
38
+ end
39
+ total_grand = time_entries.sum { |te| te['dur'] }
40
+ rows.push(['Total:', '', "#{humanize_duration(total_grand)}h"])
41
+ rows
42
+ end
43
+
44
+ def self.get_time_entries(connection, toggl_config, report_interval)
45
+ entries_list = []
46
+ error = nil
47
+
48
+ page = 1
49
+ loop do
50
+ success, body = Toggl::Client.time_entries(
51
+ toggl_config['access_token'],
52
+ time_entries_params(connection, report_interval, page)
53
+ )
54
+
55
+ unless success
56
+ error = body.fetch('message', body)
57
+ break
58
+ end
59
+
60
+ entries_list << body['data']
61
+ entries_list.flatten!
62
+ entries_list.compact!
63
+
64
+ break if entries_list.length >= body['total_count']
65
+
66
+ page += 1
67
+ end
68
+
69
+ [entries_list, error]
70
+ end
71
+
72
+ def self.time_entries_params(connection, report_interval, page = 1)
73
+ start_date, end_date = report_interval
74
+ params = {
75
+ workspace_id: connection['workspace_id'],
76
+ project_ids: connection['project_ids'],
77
+ billable: connection['billable'],
78
+ rounding: connection['rounding'],
79
+ since: start_date.to_s,
80
+ until: end_date.to_s,
81
+ page: page
82
+ }
83
+ params[:task_ids] = connection['task_ids'] if connection['task_ids'].length.positive?
84
+ params
85
+ end
86
+
87
+ def self.humanize_duration(milliseconds)
88
+ return '0' if milliseconds.nil?
89
+
90
+ seconds = milliseconds / 1000
91
+ minutes = seconds / 60
92
+ int = (minutes / 60).ceil
93
+ dec = minutes % 60
94
+ amount = (dec * 100) / 60
95
+ amount = if dec.zero?
96
+ ''
97
+ elsif amount.to_s.length == 1
98
+ ".0#{amount}"
99
+ else
100
+ ".#{amount}"
101
+ end
102
+ "#{int}#{amount}"
103
+ end
104
+
105
+ def self.assignment_items(time_entries)
106
+ time_entries.map do |te|
107
+ milliseconds = te['dur'] || 0
108
+ {
109
+ time_seconds: milliseconds.div(1000),
110
+ date: Date.parse(te['start']).to_s,
111
+ comment: te['description'],
112
+ external_id: te['id'].to_s
113
+ }
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -1,3 +1,3 @@
1
1
  module Sfctl
2
- VERSION = '0.0.4'.freeze
2
+ VERSION = '1.0.3'.freeze
3
3
  end
@@ -38,7 +38,7 @@ Gem::Specification.new do |spec|
38
38
  spec.add_runtime_dependency 'tty-progressbar', '~> 0.17.0'
39
39
  spec.add_runtime_dependency 'tty-prompt', '~> 0.21.0'
40
40
  spec.add_runtime_dependency 'tty-reader', '~> 0.7.0'
41
- spec.add_runtime_dependency 'tty-screen', '~> 0.7.1'
41
+ spec.add_runtime_dependency 'tty-screen', '~> 0.8.1'
42
42
  spec.add_runtime_dependency 'tty-spinner', '~> 0.9.3'
43
43
  spec.add_runtime_dependency 'tty-table', '~> 0.11.0'
44
44
  spec.add_runtime_dependency 'tty-tree', '~> 0.4.0'
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sfctl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Serhii Rudik
8
8
  - Markus Kuhnt
9
- autorequire:
9
+ autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2020-04-23 00:00:00.000000000 Z
12
+ date: 2020-09-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: faraday
@@ -311,14 +311,14 @@ dependencies:
311
311
  requirements:
312
312
  - - "~>"
313
313
  - !ruby/object:Gem::Version
314
- version: 0.7.1
314
+ version: 0.8.1
315
315
  type: :runtime
316
316
  prerelease: false
317
317
  version_requirements: !ruby/object:Gem::Requirement
318
318
  requirements:
319
319
  - - "~>"
320
320
  - !ruby/object:Gem::Version
321
- version: 0.7.1
321
+ version: 0.8.1
322
322
  - !ruby/object:Gem::Dependency
323
323
  name: tty-spinner
324
324
  requirement: !ruby/object:Gem::Requirement
@@ -375,7 +375,7 @@ dependencies:
375
375
  - - "~>"
376
376
  - !ruby/object:Gem::Version
377
377
  version: 0.4.2
378
- description:
378
+ description:
379
379
  email:
380
380
  - serhii@starfish.team
381
381
  executables:
@@ -417,9 +417,12 @@ files:
417
417
  - lib/sfctl/commands/time/providers/set.rb
418
418
  - lib/sfctl/commands/time/providers/unset.rb
419
419
  - lib/sfctl/commands/time/sync.rb
420
- - lib/sfctl/starfish.rb
420
+ - lib/sfctl/harvest/client.rb
421
+ - lib/sfctl/harvest/sync.rb
422
+ - lib/sfctl/starfish/client.rb
421
423
  - lib/sfctl/templates/.gitkeep
422
- - lib/sfctl/toggl.rb
424
+ - lib/sfctl/toggl/client.rb
425
+ - lib/sfctl/toggl/sync.rb
423
426
  - lib/sfctl/version.rb
424
427
  - scripts/test.sh
425
428
  - sfctl.gemspec
@@ -429,7 +432,7 @@ licenses:
429
432
  metadata:
430
433
  bug_tracker_uri: https://github.com/alphatier-works/sfctl/issues
431
434
  source_code_uri: https://github.com/alphatier-works/sfctl
432
- post_install_message:
435
+ post_install_message:
433
436
  rdoc_options: []
434
437
  require_paths:
435
438
  - lib
@@ -444,8 +447,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
444
447
  - !ruby/object:Gem::Version
445
448
  version: '0'
446
449
  requirements: []
447
- rubygems_version: 3.1.2
448
- signing_key:
450
+ rubygems_version: 3.1.4
451
+ signing_key:
449
452
  specification_version: 4
450
453
  summary: sfctl is a command line interface for the Starfish API.
451
454
  test_files: []
@@ -1,51 +0,0 @@
1
- require 'faraday'
2
- require 'json'
3
-
4
- module Sfctl
5
- module Starfish
6
- def self.conn(endpoint, token)
7
- raise 'Before continue please pass endpoint and token.' if endpoint.nil? || token.nil?
8
-
9
- headers = {
10
- 'Content-Type' => 'application/json',
11
- 'X-Starfish-Auth' => token
12
- }
13
- Faraday.new(url: "#{endpoint}/api/v1", headers: headers) do |builder|
14
- builder.request :retry
15
- builder.adapter :net_http
16
- end
17
- end
18
-
19
- def self.parsed_response(response)
20
- [response.status == 200, JSON.parse(response.body)]
21
- end
22
-
23
- def self.check_authorization(endpoint, token)
24
- response = conn(endpoint, token).get('profile')
25
- response.status == 200
26
- end
27
-
28
- def self.account_info(endpoint, token)
29
- response = conn(endpoint, token).get('profile')
30
- parsed_response(response)
31
- end
32
-
33
- def self.account_assignments(endpoint, all, token)
34
- api_conn = conn(endpoint, token)
35
- response = all ? api_conn.get('assignments?all=1') : api_conn.get('assignments')
36
- parsed_response(response)
37
- end
38
-
39
- def self.next_report(endpoint, token, assignment_id)
40
- api_conn = conn(endpoint, token)
41
- response = api_conn.get("assignments/#{assignment_id}/next_report")
42
- parsed_response(response)
43
- end
44
-
45
- def self.update_next_report(endpoint, token, assignment_id, items)
46
- api_conn = conn(endpoint, token)
47
- response = api_conn.put("assignments/#{assignment_id}/next_report", JSON.generate(items: items))
48
- response.status == 204
49
- end
50
- end
51
- end
@@ -1,49 +0,0 @@
1
- require 'faraday'
2
- require 'json'
3
-
4
- module Sfctl
5
- module Toggl
6
- DEFAULT_API_PATH = 'api/v8/'.freeze
7
- REPORTS_API_PATH = 'reports/api/v2/'.freeze
8
-
9
- def self.conn(token, api = 'default')
10
- raise 'Please set toggl provider before continue.' if token.nil?
11
-
12
- api_path = api == 'reports' ? REPORTS_API_PATH : DEFAULT_API_PATH
13
-
14
- headers = { 'Content-Type' => 'application/json' }
15
- Faraday.new(url: "https://#{token}:api_token@www.toggl.com/#{api_path}", headers: headers) do |builder|
16
- builder.request :retry
17
- builder.adapter :net_http
18
- end
19
- end
20
-
21
- def self.parsed_response(response)
22
- [response.status == 200, JSON.parse(response.body)]
23
- end
24
-
25
- def self.workspaces(token)
26
- response = conn(token).get('workspaces')
27
- parsed_response(response)
28
- end
29
-
30
- def self.workspace_projects(token, workspace_id)
31
- response = conn(token).get("workspaces/#{workspace_id}/projects")
32
- parsed_response(response)
33
- end
34
-
35
- def self.project_tasks(token, project_id)
36
- response = conn(token).get("workspaces/#{project_id}/tasks")
37
-
38
- return [] if response.body.length.zero?
39
-
40
- parsed_response(response)
41
- end
42
-
43
- def self.time_entries(token, params)
44
- params[:user_agent] = 'api_test'
45
- response = conn(token, 'reports').get('details', params)
46
- parsed_response(response)
47
- end
48
- end
49
- end