sfctl 0.0.4 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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