abt-cli 0.0.21 → 0.0.22
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/abt +3 -3
- data/lib/abt.rb +6 -6
- data/lib/abt/ari.rb +1 -1
- data/lib/abt/ari_list.rb +1 -1
- data/lib/abt/base_command.rb +7 -7
- data/lib/abt/cli.rb +27 -40
- data/lib/abt/cli/arguments_parser.rb +5 -9
- data/lib/abt/cli/global_commands.rb +23 -0
- data/lib/abt/cli/global_commands/commands.rb +2 -2
- data/lib/abt/cli/global_commands/examples.rb +2 -2
- data/lib/abt/cli/global_commands/help.rb +2 -2
- data/lib/abt/cli/global_commands/readme.rb +2 -2
- data/lib/abt/cli/global_commands/share.rb +6 -6
- data/lib/abt/cli/global_commands/version.rb +2 -2
- data/lib/abt/cli/prompt.rb +51 -20
- data/lib/abt/docs.rb +39 -33
- data/lib/abt/docs/cli.rb +3 -3
- data/lib/abt/docs/markdown.rb +5 -5
- data/lib/abt/git_config.rb +4 -6
- data/lib/abt/providers/asana/api.rb +9 -9
- data/lib/abt/providers/asana/base_command.rb +8 -10
- data/lib/abt/providers/asana/commands/add.rb +13 -12
- data/lib/abt/providers/asana/commands/branch_name.rb +8 -8
- data/lib/abt/providers/asana/commands/clear.rb +7 -8
- data/lib/abt/providers/asana/commands/current.rb +14 -14
- data/lib/abt/providers/asana/commands/finalize.rb +11 -12
- data/lib/abt/providers/asana/commands/harvest_time_entry_data.rb +11 -11
- data/lib/abt/providers/asana/commands/init.rb +8 -41
- data/lib/abt/providers/asana/commands/pick.rb +17 -17
- data/lib/abt/providers/asana/commands/projects.rb +5 -5
- data/lib/abt/providers/asana/commands/share.rb +5 -5
- data/lib/abt/providers/asana/commands/start.rb +21 -20
- data/lib/abt/providers/asana/commands/tasks.rb +6 -6
- data/lib/abt/providers/asana/configuration.rb +25 -25
- data/lib/abt/providers/asana/path.rb +5 -5
- data/lib/abt/providers/devops/api.rb +12 -12
- data/lib/abt/providers/devops/base_command.rb +10 -10
- data/lib/abt/providers/devops/commands/boards.rb +5 -7
- data/lib/abt/providers/devops/commands/branch_name.rb +9 -9
- data/lib/abt/providers/devops/commands/clear.rb +7 -8
- data/lib/abt/providers/devops/commands/current.rb +17 -17
- data/lib/abt/providers/devops/commands/harvest_time_entry_data.rb +13 -13
- data/lib/abt/providers/devops/commands/init.rb +17 -13
- data/lib/abt/providers/devops/commands/pick.rb +11 -11
- data/lib/abt/providers/devops/commands/share.rb +5 -5
- data/lib/abt/providers/devops/commands/{work-items.rb → work_items.rb} +3 -3
- data/lib/abt/providers/devops/configuration.rb +19 -15
- data/lib/abt/providers/devops/path.rb +5 -4
- data/lib/abt/providers/git/commands/branch.rb +17 -19
- data/lib/abt/providers/harvest/api.rb +8 -8
- data/lib/abt/providers/harvest/base_command.rb +6 -8
- data/lib/abt/providers/harvest/commands/clear.rb +7 -8
- data/lib/abt/providers/harvest/commands/current.rb +13 -13
- data/lib/abt/providers/harvest/commands/init.rb +10 -38
- data/lib/abt/providers/harvest/commands/pick.rb +11 -11
- data/lib/abt/providers/harvest/commands/projects.rb +5 -5
- data/lib/abt/providers/harvest/commands/share.rb +5 -5
- data/lib/abt/providers/harvest/commands/start.rb +5 -3
- data/lib/abt/providers/harvest/commands/stop.rb +12 -12
- data/lib/abt/providers/harvest/commands/tasks.rb +7 -7
- data/lib/abt/providers/harvest/commands/track.rb +21 -20
- data/lib/abt/providers/harvest/configuration.rb +18 -18
- data/lib/abt/providers/harvest/path.rb +5 -5
- data/lib/abt/version.rb +1 -1
- metadata +6 -5
data/lib/abt/docs.rb
CHANGED
@@ -9,37 +9,39 @@ module Abt
|
|
9
9
|
class << self
|
10
10
|
def basic_examples
|
11
11
|
{
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
"Getting started:" => {
|
13
|
+
"abt init asana harvest" => "Setup asana and harvest project for local git repo",
|
14
|
+
"abt pick harvest" => "Pick harvest task. This will likely stay the same throughout the project",
|
15
|
+
"abt pick asana | abt start harvest" => "Pick asana task and start tracking time",
|
16
|
+
"abt stop harvest" => "Stop time tracker",
|
17
|
+
"abt start asana harvest" => "Continue working, e.g., after a break",
|
18
|
+
"abt finalize asana" => "Finalize the selected asana task"
|
19
19
|
}
|
20
20
|
}
|
21
21
|
end
|
22
22
|
|
23
|
-
def extended_examples
|
23
|
+
def extended_examples # rubocop:disable Metrics/MethodLength
|
24
24
|
{
|
25
|
-
|
26
|
-
|
27
|
-
'abt pick harvest -d | abt track harvest -c "Name of meeting"' =>
|
25
|
+
"Tracking meetings (without switching current task setting):" => {
|
26
|
+
"abt pick asana -d | abt track harvest" => "Track on asana meeting task",
|
27
|
+
'abt pick harvest -d | abt track harvest -c "Name of meeting"' => "Track on separate harvest-task"
|
28
28
|
},
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
"Many commands output ARIs that can be piped into other commands:" => {
|
30
|
+
"abt tasks asana | grep -i <name of task>" => nil,
|
31
|
+
"abt tasks asana | grep -i <name of task> | abt start" => nil
|
32
32
|
},
|
33
|
-
|
34
|
-
'abt share asana harvest | tr "\n" " "' =>
|
35
|
-
'abt share asana harvest | tr "\n" " " | pbcopy' =>
|
36
|
-
|
37
|
-
|
33
|
+
"Sharing ARIs:" => {
|
34
|
+
'abt share asana harvest | tr "\n" " "' => "Print current asana and harvest ARIs on a single line",
|
35
|
+
'abt share asana harvest | tr "\n" " " | pbcopy' => "Copy ARIs to clipboard (mac only)",
|
36
|
+
"abt start <ARIs from coworker>" => "Work on a task your coworker shared with you",
|
37
|
+
"abt current <ARIs from coworker> | abt start" => "Set task as current, then start it"
|
38
38
|
},
|
39
|
-
|
40
|
-
'abt start harvest -c "comment"' =>
|
41
|
-
'abt start harvest -c "comment" -- asana' =>
|
42
|
-
|
39
|
+
"Flags:" => {
|
40
|
+
'abt start harvest -c "comment"' => "Add command flags after ARIs",
|
41
|
+
'abt start harvest -c "comment" -- asana' =>
|
42
|
+
"Use -- to end a list of flags, so that it can be followed by another ARI",
|
43
|
+
'abt pick harvest | abt start -c "comment"' =>
|
44
|
+
"Flags placed directly after a command applies to the piped in ARI"
|
43
45
|
}
|
44
46
|
}
|
45
47
|
end
|
@@ -48,15 +50,7 @@ module Abt
|
|
48
50
|
@providers ||= begin
|
49
51
|
providers = {}
|
50
52
|
|
51
|
-
|
52
|
-
providers['Global'] = global_command_names.each_with_object({}) do |name, definition|
|
53
|
-
command_class = Abt::Cli.global_command_class(name)
|
54
|
-
full_name = "abt #{name}"
|
55
|
-
|
56
|
-
if command_class.respond_to?(:usage) && command_class.respond_to?(:description)
|
57
|
-
definition[full_name] = [command_class.usage, command_class.description]
|
58
|
-
end
|
59
|
-
end
|
53
|
+
providers["Global"] = global_command_definitions
|
60
54
|
|
61
55
|
Abt.schemes.sort.each_with_object(providers) do |scheme, definition|
|
62
56
|
definition[scheme] = command_definitions(scheme)
|
@@ -68,6 +62,18 @@ module Abt
|
|
68
62
|
|
69
63
|
private
|
70
64
|
|
65
|
+
def global_command_definitions
|
66
|
+
global_command_names = Abt::Cli::GlobalCommands.command_names
|
67
|
+
global_command_names.each_with_object({}) do |name, definition|
|
68
|
+
command_class = Abt::Cli::GlobalCommands.command_class(name)
|
69
|
+
full_name = "abt #{name}"
|
70
|
+
|
71
|
+
if command_class.respond_to?(:usage) && command_class.respond_to?(:description)
|
72
|
+
definition[full_name] = [command_class.usage.strip, command_class.description.strip]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
71
77
|
def command_definitions(scheme)
|
72
78
|
provider = Abt.scheme_provider(scheme)
|
73
79
|
provider.command_names.each_with_object({}) do |name, definition|
|
@@ -75,7 +81,7 @@ module Abt
|
|
75
81
|
full_name = "abt #{name} #{scheme}"
|
76
82
|
|
77
83
|
if command_class.respond_to?(:usage) && command_class.respond_to?(:description)
|
78
|
-
definition[full_name] = [command_class.usage, command_class.description]
|
84
|
+
definition[full_name] = [command_class.usage.strip, command_class.description.strip]
|
79
85
|
end
|
80
86
|
end
|
81
87
|
end
|
data/lib/abt/docs/cli.rb
CHANGED
@@ -45,14 +45,14 @@ module Abt
|
|
45
45
|
private
|
46
46
|
|
47
47
|
def usage_line
|
48
|
-
|
48
|
+
"abt <command> [<ARI>] [<options> --] [<ARI>] ..."
|
49
49
|
end
|
50
50
|
|
51
51
|
def formatted_examples(example_groups)
|
52
52
|
lines = []
|
53
53
|
|
54
54
|
example_groups.each_with_index do |(title, examples), index|
|
55
|
-
lines <<
|
55
|
+
lines << "" unless index.zero?
|
56
56
|
lines << title
|
57
57
|
|
58
58
|
max_length = examples.keys.map(&:length).max
|
@@ -68,7 +68,7 @@ module Abt
|
|
68
68
|
lines = []
|
69
69
|
|
70
70
|
Docs.providers.each_with_index do |(scheme, commands_definition), index|
|
71
|
-
lines <<
|
71
|
+
lines << "" unless index.zero?
|
72
72
|
lines << "#{inflector.humanize(scheme)}:"
|
73
73
|
|
74
74
|
max_length = commands_definition.keys.map(&:length).max
|
data/lib/abt/docs/markdown.rb
CHANGED
@@ -52,11 +52,11 @@ module Abt
|
|
52
52
|
|
53
53
|
examples = Docs.basic_examples.merge(Docs.extended_examples)
|
54
54
|
examples.each_with_index do |(title, commands), index|
|
55
|
-
lines <<
|
55
|
+
lines << "" unless index.zero?
|
56
56
|
lines << title
|
57
57
|
|
58
58
|
commands.each do |(command, description)|
|
59
|
-
formatted_description = description.nil? ?
|
59
|
+
formatted_description = description.nil? ? "" : ": #{description}"
|
60
60
|
lines << "- `#{command}`#{formatted_description}"
|
61
61
|
end
|
62
62
|
end
|
@@ -68,10 +68,10 @@ module Abt
|
|
68
68
|
lines = []
|
69
69
|
|
70
70
|
Docs.providers.each_with_index do |(scheme, commands), index|
|
71
|
-
lines <<
|
71
|
+
lines << "" unless index.zero?
|
72
72
|
lines << "### #{inflector.humanize(scheme)}"
|
73
|
-
lines <<
|
74
|
-
lines <<
|
73
|
+
lines << "| Command | Description |"
|
74
|
+
lines << "| :------ | :---------- |"
|
75
75
|
|
76
76
|
max_length = commands.values.map(&:first).map(&:length).max
|
77
77
|
|
data/lib/abt/git_config.rb
CHANGED
@@ -6,12 +6,10 @@ module Abt
|
|
6
6
|
|
7
7
|
class UnsafeNamespaceError < StandardError; end
|
8
8
|
|
9
|
-
def initialize(scope =
|
9
|
+
def initialize(scope = "local", namespace = "")
|
10
10
|
@namespace = namespace
|
11
11
|
|
12
|
-
unless %w[local global].include?
|
13
|
-
raise ArgumentError, 'scope must be "local" or "global"'
|
14
|
-
end
|
12
|
+
raise ArgumentError, 'scope must be "local" or "global"' unless %w[local global].include?(scope)
|
15
13
|
|
16
14
|
@scope = scope
|
17
15
|
end
|
@@ -50,7 +48,7 @@ module Abt
|
|
50
48
|
end
|
51
49
|
|
52
50
|
def clear(output: nil)
|
53
|
-
raise UnsafeNamespaceError,
|
51
|
+
raise UnsafeNamespaceError, "Keys can only be cleared within a namespace" if namespace.empty?
|
54
52
|
|
55
53
|
keys.each do |key|
|
56
54
|
output&.puts "Clearing #{scope}: #{key_with_namespace(key)}"
|
@@ -67,7 +65,7 @@ module Abt
|
|
67
65
|
def ensure_scope_available!
|
68
66
|
return if available?
|
69
67
|
|
70
|
-
raise StandardError,
|
68
|
+
raise StandardError, "Local configuration is not available outside a git repository"
|
71
69
|
end
|
72
70
|
|
73
71
|
def key_with_namespace(key)
|
@@ -4,8 +4,8 @@ module Abt
|
|
4
4
|
module Providers
|
5
5
|
module Asana
|
6
6
|
class Api
|
7
|
-
API_ENDPOINT =
|
8
|
-
VERBS =
|
7
|
+
API_ENDPOINT = "https://app.asana.com/api/1.0"
|
8
|
+
VERBS = [:get, :post, :put].freeze
|
9
9
|
|
10
10
|
attr_reader :access_token
|
11
11
|
|
@@ -15,7 +15,7 @@ module Abt
|
|
15
15
|
|
16
16
|
VERBS.each do |verb|
|
17
17
|
define_method(verb) do |*args|
|
18
|
-
request(verb, *args)[
|
18
|
+
request(verb, *args)["data"]
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -24,10 +24,10 @@ module Abt
|
|
24
24
|
|
25
25
|
loop do
|
26
26
|
result = request(:get, path, query.merge(limit: 100))
|
27
|
-
records += result[
|
28
|
-
break if result[
|
27
|
+
records += result["data"]
|
28
|
+
break if result["next_page"].nil?
|
29
29
|
|
30
|
-
path = result[
|
30
|
+
path = result["next_page"]["path"][1..-1]
|
31
31
|
end
|
32
32
|
|
33
33
|
records
|
@@ -40,15 +40,15 @@ module Abt
|
|
40
40
|
Oj.load(response.body)
|
41
41
|
else
|
42
42
|
error_class = Abt::HttpError.error_class_for_status(response.status)
|
43
|
-
encoded_response_body = response.body.force_encoding(
|
43
|
+
encoded_response_body = response.body.force_encoding("utf-8")
|
44
44
|
raise error_class, "Code: #{response.status}, body: #{encoded_response_body}"
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
48
|
def connection
|
49
49
|
@connection ||= Faraday.new(API_ENDPOINT) do |connection|
|
50
|
-
connection.headers[
|
51
|
-
connection.headers[
|
50
|
+
connection.headers["Authorization"] = "Bearer #{access_token}"
|
51
|
+
connection.headers["Content-Type"] = "application/json"
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
@@ -21,25 +21,23 @@ module Abt
|
|
21
21
|
private
|
22
22
|
|
23
23
|
def require_project!
|
24
|
-
abort
|
24
|
+
abort("No current/specified project. Did you initialize Asana?") if project_gid.nil?
|
25
25
|
end
|
26
26
|
|
27
27
|
def require_task!
|
28
|
-
if project_gid.nil?
|
29
|
-
|
30
|
-
end
|
31
|
-
abort 'No current/specified task. Did you pick an Asana task?' if task_gid.nil?
|
28
|
+
abort("No current/specified project. Did you initialize Asana and pick a task?") if project_gid.nil?
|
29
|
+
abort("No current/specified task. Did you pick an Asana task?") if task_gid.nil?
|
32
30
|
end
|
33
31
|
|
34
32
|
def print_project(project)
|
35
|
-
cli.print_ari(
|
36
|
-
warn
|
33
|
+
cli.print_ari("asana", project["gid"], project["name"])
|
34
|
+
warn(project["permalink_url"]) if project.key?("permalink_url") && cli.output.isatty
|
37
35
|
end
|
38
36
|
|
39
37
|
def print_task(project, task)
|
40
|
-
project = {
|
41
|
-
cli.print_ari(
|
42
|
-
warn
|
38
|
+
project = { "gid" => project } if project.is_a?(String)
|
39
|
+
cli.print_ari("asana", "#{project['gid']}/#{task['gid']}", task["name"])
|
40
|
+
warn(task["permalink_url"]) if task.key?("permalink_url") && cli.output.isatty
|
43
41
|
end
|
44
42
|
|
45
43
|
def api
|
@@ -6,22 +6,22 @@ module Abt
|
|
6
6
|
module Commands
|
7
7
|
class Add < BaseCommand
|
8
8
|
def self.usage
|
9
|
-
|
9
|
+
"abt add asana[:<project-gid>]"
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
13
|
-
|
13
|
+
"Create a new task for the current/specified Asana project"
|
14
14
|
end
|
15
15
|
|
16
16
|
def perform
|
17
17
|
require_project!
|
18
18
|
|
19
19
|
task
|
20
|
-
warn
|
20
|
+
warn("Task created")
|
21
21
|
|
22
22
|
if section
|
23
23
|
move_task
|
24
|
-
warn
|
24
|
+
warn("Moved to section: #{section['name']}")
|
25
25
|
end
|
26
26
|
|
27
27
|
print_task(project, task)
|
@@ -38,36 +38,37 @@ module Abt
|
|
38
38
|
projects: [project_gid]
|
39
39
|
}
|
40
40
|
}
|
41
|
-
api.post(
|
41
|
+
api.post("tasks", Oj.dump(body, mode: :json))
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
45
|
def move_task
|
46
|
-
body = { data: { task: task[
|
46
|
+
body = { data: { task: task["gid"] } }
|
47
47
|
body_json = Oj.dump(body, mode: :json)
|
48
48
|
api.post("sections/#{section['gid']}/addTask", body_json)
|
49
49
|
end
|
50
50
|
|
51
51
|
def name
|
52
|
-
@name ||= cli.prompt.text
|
52
|
+
@name ||= cli.prompt.text("Enter task description")
|
53
53
|
end
|
54
54
|
|
55
55
|
def notes
|
56
|
-
@notes ||= cli.prompt.text
|
56
|
+
@notes ||= cli.prompt.text("Enter task notes")
|
57
57
|
end
|
58
58
|
|
59
59
|
def project
|
60
|
-
@project ||= api.get("projects/#{project_gid}", opt_fields:
|
60
|
+
@project ||= api.get("projects/#{project_gid}", opt_fields: "name")
|
61
61
|
end
|
62
62
|
|
63
63
|
def section
|
64
|
-
@section ||= cli.prompt.choice
|
64
|
+
@section ||= cli.prompt.choice("Add to section?", sections,
|
65
|
+
nil_option: ["q", "Don't add to section"])
|
65
66
|
end
|
66
67
|
|
67
68
|
def sections
|
68
69
|
@sections ||= begin
|
69
|
-
warn
|
70
|
-
api.get_paged("projects/#{project_gid}/sections", opt_fields:
|
70
|
+
warn("Fetching sections...")
|
71
|
+
api.get_paged("projects/#{project_gid}/sections", opt_fields: "name")
|
71
72
|
end
|
72
73
|
end
|
73
74
|
end
|
@@ -6,11 +6,11 @@ module Abt
|
|
6
6
|
module Commands
|
7
7
|
class BranchName < BaseCommand
|
8
8
|
def self.usage
|
9
|
-
|
9
|
+
"abt branch-name asana[:<project-gid>/<task-gid>]"
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
13
|
-
|
13
|
+
"Suggest a git branch name for the current/specified task."
|
14
14
|
end
|
15
15
|
|
16
16
|
def perform
|
@@ -23,21 +23,21 @@ module Abt
|
|
23
23
|
private
|
24
24
|
|
25
25
|
def name
|
26
|
-
task[
|
26
|
+
task["name"].downcase.gsub(/[^\w]+/, "-").gsub(/(^-|-$)/, "")
|
27
27
|
end
|
28
28
|
|
29
29
|
def ensure_current_is_valid!
|
30
|
-
abort
|
30
|
+
abort("Invalid task gid: #{task_gid}") if task.nil?
|
31
31
|
|
32
|
-
return if task[
|
32
|
+
return if task["memberships"].any? { |m| m.dig("project", "gid") == project_gid }
|
33
33
|
|
34
|
-
abort
|
34
|
+
abort("Invalid or unmatching project gid: #{project_gid}")
|
35
35
|
end
|
36
36
|
|
37
37
|
def task
|
38
38
|
@task ||= begin
|
39
|
-
warn
|
40
|
-
api.get("tasks/#{task_gid}", opt_fields:
|
39
|
+
warn("Fetching task...")
|
40
|
+
api.get("tasks/#{task_gid}", opt_fields: "name,memberships.project")
|
41
41
|
rescue Abt::HttpError::NotFoundError
|
42
42
|
nil
|
43
43
|
end
|
@@ -6,29 +6,28 @@ module Abt
|
|
6
6
|
module Commands
|
7
7
|
class Clear < BaseCommand
|
8
8
|
def self.usage
|
9
|
-
|
9
|
+
"abt clear asana"
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
13
|
-
|
13
|
+
"Clear asana configuration"
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.flags
|
17
17
|
[
|
18
|
-
[
|
19
|
-
|
18
|
+
["-g", "--global",
|
19
|
+
"Clear global instead of local asana configuration (credentials etc.)"],
|
20
|
+
["-a", "--all", "Clear all asana configuration"]
|
20
21
|
]
|
21
22
|
end
|
22
23
|
|
23
24
|
def perform
|
24
|
-
if flags[:global] && flags[:all]
|
25
|
-
abort('Flags --global and --all cannot be used at the same time')
|
26
|
-
end
|
25
|
+
abort("Flags --global and --all cannot be used at the same time") if flags[:global] && flags[:all]
|
27
26
|
|
28
27
|
config.clear_local unless flags[:global]
|
29
28
|
config.clear_global if flags[:global] || flags[:all]
|
30
29
|
|
31
|
-
warn
|
30
|
+
warn("Configuration cleared")
|
32
31
|
end
|
33
32
|
end
|
34
33
|
end
|
@@ -6,22 +6,22 @@ module Abt
|
|
6
6
|
module Commands
|
7
7
|
class Current < BaseCommand
|
8
8
|
def self.usage
|
9
|
-
|
9
|
+
"abt current asana[:<project-gid>[/<task-gid>]]"
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.description
|
13
|
-
|
13
|
+
"Get or set project and or task for current git repository"
|
14
14
|
end
|
15
15
|
|
16
16
|
def perform
|
17
|
-
abort
|
17
|
+
abort("Must be run inside a git repository") unless config.local_available?
|
18
18
|
|
19
19
|
require_project!
|
20
20
|
ensure_valid_configuration!
|
21
21
|
|
22
22
|
if path != config.path
|
23
23
|
config.path = path
|
24
|
-
warn
|
24
|
+
warn("Configuration updated")
|
25
25
|
end
|
26
26
|
|
27
27
|
print_configuration
|
@@ -34,25 +34,25 @@ module Abt
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def ensure_valid_configuration!
|
37
|
-
abort
|
38
|
-
abort
|
37
|
+
abort("Invalid project: #{project_gid}") if project.nil?
|
38
|
+
abort("Invalid task: #{task_gid}") if task_gid && task.nil?
|
39
39
|
end
|
40
40
|
|
41
41
|
def project
|
42
42
|
@project ||= begin
|
43
|
-
warn
|
44
|
-
api.get("projects/#{project_gid}", opt_fields:
|
45
|
-
|
46
|
-
|
43
|
+
warn("Fetching project...")
|
44
|
+
api.get("projects/#{project_gid}", opt_fields: "name,permalink_url")
|
45
|
+
rescue Abt::HttpError::NotFoundError
|
46
|
+
nil
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
50
|
def task
|
51
51
|
@task ||= begin
|
52
|
-
warn
|
53
|
-
api.get("tasks/#{task_gid}", opt_fields:
|
54
|
-
|
55
|
-
|
52
|
+
warn("Fetching task...")
|
53
|
+
api.get("tasks/#{task_gid}", opt_fields: "name,permalink_url")
|
54
|
+
rescue Abt::HttpError::NotFoundError
|
55
|
+
nil
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|