abt-cli 0.0.3 → 0.0.8
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 +2 -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 +16 -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 +10 -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 +24 -0
- data/lib/abt/providers/harvest/commands/clear_global.rb +24 -0
- data/lib/abt/providers/harvest/commands/current.rb +83 -0
- data/lib/abt/providers/harvest/commands/init.rb +83 -0
- data/lib/abt/providers/harvest/commands/pick.rb +51 -0
- data/lib/abt/providers/harvest/commands/projects.rb +40 -0
- data/lib/abt/providers/harvest/commands/share.rb +29 -0
- data/lib/abt/providers/harvest/commands/start.rb +58 -0
- data/lib/abt/providers/harvest/commands/stop.rb +58 -0
- data/lib/abt/providers/harvest/commands/tasks.rb +45 -0
- data/lib/abt/providers/harvest/commands/track.rb +70 -0
- data/lib/abt/providers/harvest/configuration.rb +91 -0
- data/lib/abt/version.rb +1 -1
- metadata +18 -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 Start < BaseCommand
|
8
|
+
def self.command
|
9
|
+
'start harvest[:<project-id>/<task-id>]'
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.description
|
13
|
+
'As track, but also lets the user override the current task and triggers `start` commands for other providers ' # rubocop:disable Layout/LineLength
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
start_output = call_start
|
18
|
+
puts start_output
|
19
|
+
|
20
|
+
use_arg_str(arg_str_from_start_output(start_output))
|
21
|
+
|
22
|
+
maybe_override_current_task
|
23
|
+
rescue Abt::HttpError::HttpError => e
|
24
|
+
cli.warn e
|
25
|
+
cli.abort 'Unable to start tracker'
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def arg_str_from_start_output(output)
|
31
|
+
output = output.split(' # ').first
|
32
|
+
output.split(':')[1]
|
33
|
+
end
|
34
|
+
|
35
|
+
def call_start
|
36
|
+
output = StringIO.new
|
37
|
+
Abt::Cli.new(argv: ['track', *cli.args], output: output).perform
|
38
|
+
|
39
|
+
output_str = output.string.strip
|
40
|
+
cli.abort 'No task provided' if output_str.empty?
|
41
|
+
output_str
|
42
|
+
end
|
43
|
+
|
44
|
+
def maybe_override_current_task
|
45
|
+
return if arg_str.nil?
|
46
|
+
return if same_args_as_config?
|
47
|
+
return unless config.local_available?
|
48
|
+
return unless cli.prompt_boolean 'Set selected task as current?'
|
49
|
+
|
50
|
+
output = StringIO.new
|
51
|
+
Abt::Cli.new(argv: ['current', "harvest:#{project_id}/#{task_id}"],
|
52
|
+
output: output).perform
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -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: config.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,45 @@
|
|
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
|
+
tasks.each do |task|
|
18
|
+
print_task(project, task)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def project
|
25
|
+
project_assignment['project']
|
26
|
+
end
|
27
|
+
|
28
|
+
def tasks
|
29
|
+
@tasks ||= project_assignment['task_assignments'].map { |ta| ta['task'] }
|
30
|
+
end
|
31
|
+
|
32
|
+
def project_assignment
|
33
|
+
@project_assignment ||= begin
|
34
|
+
project_assignments.find { |pa| pa['project']['id'].to_s == project_id }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def project_assignments
|
39
|
+
@project_assignments ||= api.get_paged('users/me/project_assignments')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Abt
|
4
|
+
module Providers
|
5
|
+
module Harvest
|
6
|
+
module Commands
|
7
|
+
class Track < BaseCommand
|
8
|
+
def self.command
|
9
|
+
'track harvest[:<project-id>/<task-id>]'
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.description
|
13
|
+
'Start tracker for current or specified task. Add a relevant provider to link the time entry: E.g. `abt start harvest asana`' # rubocop:disable Layout/LineLength
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
abort 'No current/provided task' if task_id.nil?
|
18
|
+
cli.abort('No task selected') if task_id.nil?
|
19
|
+
|
20
|
+
print_task(created_time_entry['project'], created_time_entry['task'])
|
21
|
+
|
22
|
+
cli.warn 'Tracker successfully started'
|
23
|
+
rescue Abt::HttpError::HttpError => e
|
24
|
+
cli.abort 'Invalid task'
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def created_time_entry
|
30
|
+
@created_time_entry ||= create_time_entry
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_time_entry
|
34
|
+
body = {
|
35
|
+
project_id: project_id,
|
36
|
+
task_id: task_id,
|
37
|
+
user_id: config.user_id,
|
38
|
+
spent_date: Date.today.iso8601
|
39
|
+
}
|
40
|
+
|
41
|
+
if external_link_data
|
42
|
+
body.merge! external_link_data
|
43
|
+
else
|
44
|
+
cli.warn 'No external link provided'
|
45
|
+
body[:notes] ||= cli.prompt('Fill in comment (optional)')
|
46
|
+
end
|
47
|
+
|
48
|
+
api.post('time_entries', Oj.dump(body, mode: :json))
|
49
|
+
end
|
50
|
+
|
51
|
+
def external_link_data
|
52
|
+
@external_link_data ||= begin
|
53
|
+
arg_strs = cli.args.join(' ')
|
54
|
+
lines = `#{$PROGRAM_NAME} harvest-time-entry-data #{arg_strs}`.split("\n")
|
55
|
+
|
56
|
+
return if lines.empty?
|
57
|
+
|
58
|
+
# TODO: Make user choose which reference to use by printing the urls
|
59
|
+
if lines.length > 1
|
60
|
+
cli.abort('Multiple providers had harvest reference data, only one is supported at a time') # rubocop:disable Layout/LineLength
|
61
|
+
end
|
62
|
+
|
63
|
+
Oj.load(lines.first)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,91 @@
|
|
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
|
+
value = value.to_s unless value.nil?
|
28
|
+
return if project_id == value
|
29
|
+
|
30
|
+
clear_local
|
31
|
+
git['projectId'] = value
|
32
|
+
end
|
33
|
+
|
34
|
+
def task_id=(value)
|
35
|
+
value = value.to_s unless value.nil?
|
36
|
+
git['taskId'] = value
|
37
|
+
end
|
38
|
+
|
39
|
+
def clear_local
|
40
|
+
cli.abort 'No local configuration was found' unless local_available?
|
41
|
+
|
42
|
+
git['projectId'] = nil
|
43
|
+
git['taskId'] = nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def clear_global
|
47
|
+
git.global['userId'] = nil
|
48
|
+
git.global['accountId'] = nil
|
49
|
+
git.global['accessToken'] = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def access_token
|
53
|
+
return git.global['accessToken'] unless git.global['accessToken'].nil?
|
54
|
+
|
55
|
+
git.global['accessToken'] = cli.prompt([
|
56
|
+
'Please provide your personal access token for Harvest.',
|
57
|
+
'If you don\'t have one, create one here: https://id.getharvest.com/developers',
|
58
|
+
'',
|
59
|
+
'Enter access token'
|
60
|
+
].join("\n"))
|
61
|
+
end
|
62
|
+
|
63
|
+
def account_id
|
64
|
+
return git.global['accountId'] unless git.global['accountId'].nil?
|
65
|
+
|
66
|
+
git.global['accountId'] = cli.prompt([
|
67
|
+
'Please provide harvest account id.',
|
68
|
+
'This information is shown next to your generated access token',
|
69
|
+
'',
|
70
|
+
'Enter account id'
|
71
|
+
].join("\n"))
|
72
|
+
end
|
73
|
+
|
74
|
+
def user_id
|
75
|
+
return git.global['userId'] unless git.global['userId'].nil?
|
76
|
+
|
77
|
+
git.global['userId'] = api.get('users/me')['id'].to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
attr_reader :git
|
83
|
+
|
84
|
+
def api
|
85
|
+
@api ||=
|
86
|
+
Abt::Providers::Harvest::Api.new(access_token: access_token, account_id: account_id)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
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.8
|
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-20 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,30 @@ 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/commands/track.rb"
|
117
|
+
- "./lib/abt/providers/harvest/configuration.rb"
|
114
118
|
- "./lib/abt/version.rb"
|
115
119
|
- bin/abt
|
116
120
|
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
|