abt-cli 0.0.3 → 0.0.4

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/bin/abt +1 -0
  3. data/lib/abt/cli.rb +16 -7
  4. data/lib/abt/cli/dialogs.rb +18 -2
  5. data/lib/abt/cli/io.rb +8 -6
  6. data/lib/abt/docs.rb +12 -5
  7. data/lib/abt/docs/cli.rb +1 -1
  8. data/lib/abt/docs/markdown.rb +1 -1
  9. data/lib/abt/git_config.rb +55 -49
  10. data/lib/abt/providers/asana/api.rb +1 -1
  11. data/lib/abt/providers/asana/base_command.rb +9 -4
  12. data/lib/abt/providers/asana/commands/current.rb +10 -4
  13. data/lib/abt/providers/asana/commands/finalize.rb +71 -0
  14. data/lib/abt/providers/asana/commands/harvest_time_entry_data.rb +2 -2
  15. data/lib/abt/providers/asana/commands/init.rb +8 -3
  16. data/lib/abt/providers/asana/commands/{pick_task.rb → pick.rb} +13 -6
  17. data/lib/abt/providers/asana/commands/projects.rb +9 -2
  18. data/lib/abt/providers/asana/commands/share.rb +29 -0
  19. data/lib/abt/providers/asana/commands/start.rb +51 -6
  20. data/lib/abt/providers/asana/commands/tasks.rb +4 -1
  21. data/lib/abt/providers/asana/configuration.rb +54 -34
  22. data/lib/abt/providers/harvest.rb +9 -51
  23. data/lib/abt/providers/harvest/api.rb +62 -0
  24. data/lib/abt/providers/harvest/base_command.rb +12 -16
  25. data/lib/abt/providers/harvest/commands/clear.rb +26 -0
  26. data/lib/abt/providers/harvest/commands/clear_global.rb +24 -0
  27. data/lib/abt/providers/harvest/commands/current.rb +81 -0
  28. data/lib/abt/providers/harvest/commands/init.rb +66 -0
  29. data/lib/abt/providers/harvest/commands/pick.rb +49 -0
  30. data/lib/abt/providers/harvest/commands/projects.rb +34 -0
  31. data/lib/abt/providers/harvest/commands/share.rb +29 -0
  32. data/lib/abt/providers/harvest/commands/start.rb +81 -0
  33. data/lib/abt/providers/harvest/commands/stop.rb +58 -0
  34. data/lib/abt/providers/harvest/commands/tasks.rb +38 -0
  35. data/lib/abt/providers/harvest/configuration.rb +90 -0
  36. data/lib/abt/version.rb +1 -1
  37. metadata +17 -14
  38. data/lib/abt/harvest_client.rb +0 -58
  39. data/lib/abt/providers/asana/commands/move.rb +0 -56
  40. data/lib/abt/providers/harvest/clear.rb +0 -24
  41. data/lib/abt/providers/harvest/clear_global.rb +0 -24
  42. data/lib/abt/providers/harvest/current.rb +0 -79
  43. data/lib/abt/providers/harvest/init.rb +0 -61
  44. data/lib/abt/providers/harvest/pick_task.rb +0 -45
  45. data/lib/abt/providers/harvest/projects.rb +0 -29
  46. data/lib/abt/providers/harvest/start.rb +0 -58
  47. data/lib/abt/providers/harvest/stop.rb +0 -51
  48. data/lib/abt/providers/harvest/tasks.rb +0 -36
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abt
4
+ module Providers
5
+ module Harvest
6
+ module Commands
7
+ class Stop < BaseCommand
8
+ def self.command
9
+ 'stop harvest'
10
+ end
11
+
12
+ def self.description
13
+ 'Stop running harvest tracker'
14
+ end
15
+
16
+ def call
17
+ cli.abort 'No running time entry' if time_entry.nil?
18
+
19
+ stop_time_entry
20
+
21
+ cli.warn 'Harvest time entry stopped'
22
+ print_task(project, task)
23
+ rescue Abt::HttpError::HttpError => e
24
+ cli.warn e
25
+ cli.abort 'Unable to stop time entry'
26
+ end
27
+
28
+ private
29
+
30
+ def stop_time_entry
31
+ api.patch("time_entries/#{time_entry['id']}/stop")
32
+ end
33
+
34
+ def project
35
+ time_entry['project']
36
+ end
37
+
38
+ def task
39
+ time_entry['task']
40
+ end
41
+
42
+ def time_entry
43
+ @time_entry ||= begin
44
+ api.get_paged(
45
+ 'time_entries',
46
+ is_running: true,
47
+ user_id: Harvest.user_id
48
+ ).first
49
+ rescue Abt::HttpError::HttpError => e # rubocop:disable Layout/RescueEnsureAlignment
50
+ cli.warn e
51
+ cli.abort 'Unable to fetch running time entry'
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abt
4
+ module Providers
5
+ module Harvest
6
+ module Commands
7
+ class Tasks < BaseCommand
8
+ def self.command
9
+ 'tasks harvest'
10
+ end
11
+
12
+ def self.description
13
+ 'List available tasks on project - useful for piping into grep etc.'
14
+ end
15
+
16
+ def call
17
+ project_task_assignments.each do |a|
18
+ project = a['project']
19
+ task = a['task']
20
+
21
+ print_task(project, task)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def project_task_assignments
28
+ @project_task_assignments ||= begin
29
+ api.get_paged("projects/#{project_id}/task_assignments", is_active: true)
30
+ rescue Abt::HttpError::HttpError # rubocop:disable Layout/RescueEnsureAlignment
31
+ []
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abt
4
+ module Providers
5
+ module Harvest
6
+ class Configuration
7
+ attr_accessor :cli
8
+
9
+ def initialize(cli:)
10
+ @cli = cli
11
+ @git = GitConfig.new(namespace: 'abt.harvest')
12
+ end
13
+
14
+ def local_available?
15
+ GitConfig.local_available?
16
+ end
17
+
18
+ def project_id
19
+ local_available? ? git['projectId'] : nil
20
+ end
21
+
22
+ def task_id
23
+ local_available? ? git['taskId'] : nil
24
+ end
25
+
26
+ def project_id=(value)
27
+ return if project_id == value
28
+
29
+ clear_local
30
+ git['projectId'] = value unless value.nil?
31
+ end
32
+
33
+ def task_id=(value)
34
+ git['taskId'] = value
35
+ end
36
+
37
+ def clear_local
38
+ cli.abort 'No local configuration was found' unless local_available?
39
+
40
+ git['projectId'] = nil
41
+ git['taskId'] = nil
42
+ end
43
+
44
+ def clear_global
45
+ git.global['userId'] = nil
46
+ git.global['accountId'] = nil
47
+ git.global['accessToken'] = nil
48
+ end
49
+
50
+ def access_token
51
+ return git.global['accessToken'] unless git.global['accessToken'].nil?
52
+
53
+ git.global['accessToken'] = cli.prompt([
54
+ 'Please provide your personal access token for Harvest.',
55
+ 'If you don\'t have one, create one here: https://id.getharvest.com/developers',
56
+ '',
57
+ 'Enter access token'
58
+ ].join("\n"))
59
+ end
60
+
61
+ def account_id
62
+ return git.global['accountId'] unless git.global['accountId'].nil?
63
+
64
+ git.global['accountId'] = cli.prompt([
65
+ 'Please provide harvest account id.',
66
+ 'This information is shown next to your generated access token',
67
+ '',
68
+ 'Enter account id'
69
+ ].join("\n"))
70
+ end
71
+
72
+ def user_id
73
+ return git.global['userId'] unless git.global['userId'].nil?
74
+
75
+ git.global['userId'] = cli.prompt([
76
+ 'Please provide your harvest User ID.',
77
+ 'To find it open "My profile" inside the harvest web UI.',
78
+ 'The ID is the number part of the URL for that page.',
79
+ '',
80
+ 'Enter user id'
81
+ ].join("\n"))
82
+ end
83
+
84
+ private
85
+
86
+ attr_reader :git
87
+ end
88
+ end
89
+ end
90
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Abt
4
- VERSION = '0.0.3'
4
+ VERSION = '0.0.4'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: abt-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jesper Sørensen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-14 00:00:00.000000000 Z
11
+ date: 2021-01-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-inflector
@@ -82,7 +82,6 @@ files:
82
82
  - "./lib/abt/docs/cli.rb"
83
83
  - "./lib/abt/docs/markdown.rb"
84
84
  - "./lib/abt/git_config.rb"
85
- - "./lib/abt/harvest_client.rb"
86
85
  - "./lib/abt/helpers.rb"
87
86
  - "./lib/abt/http_error.rb"
88
87
  - "./lib/abt/providers.rb"
@@ -92,25 +91,29 @@ files:
92
91
  - "./lib/abt/providers/asana/commands/clear.rb"
93
92
  - "./lib/abt/providers/asana/commands/clear_global.rb"
94
93
  - "./lib/abt/providers/asana/commands/current.rb"
94
+ - "./lib/abt/providers/asana/commands/finalize.rb"
95
95
  - "./lib/abt/providers/asana/commands/harvest_time_entry_data.rb"
96
96
  - "./lib/abt/providers/asana/commands/init.rb"
97
- - "./lib/abt/providers/asana/commands/move.rb"
98
- - "./lib/abt/providers/asana/commands/pick_task.rb"
97
+ - "./lib/abt/providers/asana/commands/pick.rb"
99
98
  - "./lib/abt/providers/asana/commands/projects.rb"
99
+ - "./lib/abt/providers/asana/commands/share.rb"
100
100
  - "./lib/abt/providers/asana/commands/start.rb"
101
101
  - "./lib/abt/providers/asana/commands/tasks.rb"
102
102
  - "./lib/abt/providers/asana/configuration.rb"
103
103
  - "./lib/abt/providers/harvest.rb"
104
+ - "./lib/abt/providers/harvest/api.rb"
104
105
  - "./lib/abt/providers/harvest/base_command.rb"
105
- - "./lib/abt/providers/harvest/clear.rb"
106
- - "./lib/abt/providers/harvest/clear_global.rb"
107
- - "./lib/abt/providers/harvest/current.rb"
108
- - "./lib/abt/providers/harvest/init.rb"
109
- - "./lib/abt/providers/harvest/pick_task.rb"
110
- - "./lib/abt/providers/harvest/projects.rb"
111
- - "./lib/abt/providers/harvest/start.rb"
112
- - "./lib/abt/providers/harvest/stop.rb"
113
- - "./lib/abt/providers/harvest/tasks.rb"
106
+ - "./lib/abt/providers/harvest/commands/clear.rb"
107
+ - "./lib/abt/providers/harvest/commands/clear_global.rb"
108
+ - "./lib/abt/providers/harvest/commands/current.rb"
109
+ - "./lib/abt/providers/harvest/commands/init.rb"
110
+ - "./lib/abt/providers/harvest/commands/pick.rb"
111
+ - "./lib/abt/providers/harvest/commands/projects.rb"
112
+ - "./lib/abt/providers/harvest/commands/share.rb"
113
+ - "./lib/abt/providers/harvest/commands/start.rb"
114
+ - "./lib/abt/providers/harvest/commands/stop.rb"
115
+ - "./lib/abt/providers/harvest/commands/tasks.rb"
116
+ - "./lib/abt/providers/harvest/configuration.rb"
114
117
  - "./lib/abt/version.rb"
115
118
  - bin/abt
116
119
  homepage: https://github.com/abtion/abt
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Abt
4
- class HarvestClient
5
- API_ENDPOINT = 'https://api.harvestapp.com/v2'
6
- VERBS = %i[get post patch].freeze
7
-
8
- attr_reader :access_token, :account_id
9
-
10
- def initialize(access_token:, account_id:)
11
- @access_token = access_token
12
- @account_id = account_id
13
- end
14
-
15
- VERBS.each do |verb|
16
- define_method(verb) do |*args|
17
- request(verb, *args)
18
- end
19
- end
20
-
21
- def get_paged(path, query = {})
22
- result_key = path.split('?').first.split('/').last
23
-
24
- page = 1
25
- records = []
26
-
27
- loop do
28
- result = get(path, query.merge(page: page))
29
- records += result[result_key]
30
- break if result['total_pages'] == page
31
-
32
- page += 1
33
- end
34
-
35
- records
36
- end
37
-
38
- def request(*args)
39
- response = connection.public_send(*args)
40
-
41
- if response.success?
42
- Oj.load(response.body)
43
- else
44
- error_class = Abt::HttpError.error_class_for_status(response.status)
45
- encoded_response_body = response.body.force_encoding('utf-8')
46
- raise error_class, "Code: #{response.status}, body: #{encoded_response_body}"
47
- end
48
- end
49
-
50
- def connection
51
- @connection ||= Faraday.new(API_ENDPOINT) do |connection|
52
- connection.headers['Authorization'] = "Bearer #{access_token}"
53
- connection.headers['Harvest-Account-Id'] = account_id
54
- connection.headers['Content-Type'] = 'application/json'
55
- end
56
- end
57
- end
58
- end
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Abt
4
- module Providers
5
- module Asana
6
- module Commands
7
- class Move < BaseCommand
8
- def self.command
9
- 'move asana[:<project-gid>/<task-gid>]'
10
- end
11
-
12
- def self.description
13
- 'Move current or specified task to another section (column)'
14
- end
15
-
16
- def call
17
- print_task(project, task)
18
-
19
- move_task
20
-
21
- cli.warn "Asana task moved to #{section['name']}"
22
- rescue Abt::HttpError::HttpError => e
23
- cli.warn e
24
- cli.abort 'Unable to move asana task'
25
- end
26
-
27
- private
28
-
29
- def task
30
- @task ||= api.get("tasks/#{task_gid}")
31
- end
32
-
33
- def move_task
34
- body = { data: { task: task_gid } }
35
- body_json = Oj.dump(body, mode: :json)
36
- api.post("sections/#{section['gid']}/addTask", body_json)
37
- end
38
-
39
- def section
40
- @section ||= cli.prompt_choice 'Move asana task to?', sections
41
- end
42
-
43
- def project
44
- @project ||= api.get("projects/#{project_gid}")
45
- end
46
-
47
- def sections
48
- api.get_paged("projects/#{project_gid}/sections")
49
- rescue Abt::HttpError::HttpError
50
- []
51
- end
52
- end
53
- end
54
- end
55
- end
56
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Abt
4
- module Providers
5
- class Harvest
6
- class Clear
7
- def self.command
8
- 'clear harvest'
9
- end
10
-
11
- def self.description
12
- 'Clear project/task for current git repository'
13
- end
14
-
15
- def initialize(**); end
16
-
17
- def call
18
- warn 'Clearing Harvest project configuration'
19
- Harvest.clear
20
- end
21
- end
22
- end
23
- end
24
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Abt
4
- module Providers
5
- class Harvest
6
- class ClearGlobal
7
- def self.command
8
- 'clear-global harvest'
9
- end
10
-
11
- def self.description
12
- 'Clear all global configuration'
13
- end
14
-
15
- def initialize(**); end
16
-
17
- def call
18
- warn 'Clearing Harvest project configuration'
19
- Harvest.clear_global
20
- end
21
- end
22
- end
23
- end
24
- end
@@ -1,79 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Abt
4
- module Providers
5
- class Harvest
6
- class Current < BaseCommand
7
- def self.command
8
- 'current harvest[:<project-id>[/<task-id>]]'
9
- end
10
-
11
- def self.description
12
- 'Get or set project and or task for current git repository'
13
- end
14
-
15
- def call
16
- if arg_str.nil?
17
- show_current_configuration
18
- else
19
- warn 'Updating configuration'
20
- update_configuration
21
- end
22
- end
23
-
24
- private
25
-
26
- def show_current_configuration
27
- if project_id.nil?
28
- warn 'No project selected'
29
- elsif task_id.nil?
30
- print_project(project)
31
- else
32
- print_task(project, task)
33
- end
34
- end
35
-
36
- def update_configuration
37
- ensure_project_is_valid!
38
- remember_project_id(project_id)
39
-
40
- if task_id.nil?
41
- print_project(project)
42
- remember_task_id(nil)
43
- else
44
- ensure_task_is_valid!
45
- remember_task_id(task_id)
46
-
47
- print_task(project, task)
48
- end
49
- end
50
-
51
- def ensure_project_is_valid!
52
- abort "Invalid project: #{project_id}" if project.nil?
53
- end
54
-
55
- def ensure_task_is_valid!
56
- abort "Invalid task: #{task_id}" if task.nil?
57
- end
58
-
59
- def project
60
- @project ||= Harvest.client.get("projects/#{project_id}")
61
- end
62
-
63
- def task
64
- project_task_assignments
65
- .map { |assignment| assignment['task'] }
66
- .find { |task| task['id'].to_s == task_id }
67
- end
68
-
69
- def project_task_assignments
70
- @project_task_assignments ||= begin
71
- Harvest.client.get_paged("projects/#{project_id}/task_assignments", is_active: true)
72
- rescue Abt::HttpError::HttpError # rubocop:disable Layout/RescueEnsureAlignment
73
- []
74
- end
75
- end
76
- end
77
- end
78
- end
79
- end