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.
- checksums.yaml +4 -4
- data/bin/abt +1 -0
- data/lib/abt/cli.rb +16 -7
- data/lib/abt/cli/dialogs.rb +18 -2
- data/lib/abt/cli/io.rb +8 -6
- data/lib/abt/docs.rb +12 -5
- data/lib/abt/docs/cli.rb +1 -1
- data/lib/abt/docs/markdown.rb +1 -1
- data/lib/abt/git_config.rb +55 -49
- data/lib/abt/providers/asana/api.rb +1 -1
- data/lib/abt/providers/asana/base_command.rb +9 -4
- data/lib/abt/providers/asana/commands/current.rb +10 -4
- data/lib/abt/providers/asana/commands/finalize.rb +71 -0
- data/lib/abt/providers/asana/commands/harvest_time_entry_data.rb +2 -2
- data/lib/abt/providers/asana/commands/init.rb +8 -3
- data/lib/abt/providers/asana/commands/{pick_task.rb → pick.rb} +13 -6
- data/lib/abt/providers/asana/commands/projects.rb +9 -2
- data/lib/abt/providers/asana/commands/share.rb +29 -0
- data/lib/abt/providers/asana/commands/start.rb +51 -6
- data/lib/abt/providers/asana/commands/tasks.rb +4 -1
- data/lib/abt/providers/asana/configuration.rb +54 -34
- data/lib/abt/providers/harvest.rb +9 -51
- data/lib/abt/providers/harvest/api.rb +62 -0
- data/lib/abt/providers/harvest/base_command.rb +12 -16
- data/lib/abt/providers/harvest/commands/clear.rb +26 -0
- data/lib/abt/providers/harvest/commands/clear_global.rb +24 -0
- data/lib/abt/providers/harvest/commands/current.rb +81 -0
- data/lib/abt/providers/harvest/commands/init.rb +66 -0
- data/lib/abt/providers/harvest/commands/pick.rb +49 -0
- data/lib/abt/providers/harvest/commands/projects.rb +34 -0
- data/lib/abt/providers/harvest/commands/share.rb +29 -0
- data/lib/abt/providers/harvest/commands/start.rb +81 -0
- data/lib/abt/providers/harvest/commands/stop.rb +58 -0
- data/lib/abt/providers/harvest/commands/tasks.rb +38 -0
- data/lib/abt/providers/harvest/configuration.rb +90 -0
- data/lib/abt/version.rb +1 -1
- metadata +17 -14
- data/lib/abt/harvest_client.rb +0 -58
- data/lib/abt/providers/asana/commands/move.rb +0 -56
- data/lib/abt/providers/harvest/clear.rb +0 -24
- data/lib/abt/providers/harvest/clear_global.rb +0 -24
- data/lib/abt/providers/harvest/current.rb +0 -79
- data/lib/abt/providers/harvest/init.rb +0 -61
- data/lib/abt/providers/harvest/pick_task.rb +0 -45
- data/lib/abt/providers/harvest/projects.rb +0 -29
- data/lib/abt/providers/harvest/start.rb +0 -58
- data/lib/abt/providers/harvest/stop.rb +0 -51
- 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
|
data/lib/abt/version.rb
CHANGED
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.
|
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-
|
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/
|
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/
|
110
|
-
- "./lib/abt/providers/harvest/projects.rb"
|
111
|
-
- "./lib/abt/providers/harvest/
|
112
|
-
- "./lib/abt/providers/harvest/
|
113
|
-
- "./lib/abt/providers/harvest/
|
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
|
data/lib/abt/harvest_client.rb
DELETED
@@ -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
|