abt-cli 0.0.11 → 0.0.12

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: 0cc34f7579a887a49ae83db56c8b3808a0e00b09c55631f9b4224228477a0762
4
- data.tar.gz: 9c26a06c3af53ebe55ca5e53f93b5d6d73854962daebef31d131000657be1c63
3
+ metadata.gz: 4f1631b9069ca5094ba301eb3ccc5cf42da8e48a85b2678e8499cea1e9e4551c
4
+ data.tar.gz: cd9dc57e8a6276de5a2bca61e59fb537bc8887901615d7ec86f2c4a59145f76c
5
5
  SHA512:
6
- metadata.gz: 4fe10297a130b747d93dc1398bdf404996d5c07987680deac59a5365d6893ab3d0466ebd764ef087f5eb2409b99a7733dc55a869432d81286f3460ec510d247d
7
- data.tar.gz: 3684a1d3113f9df06637a6c7294f334b48e2c8cd5e54d4334c1ec1884571519f6e5aedeec327d8292f5364d1251453d6a7a8131074f91b2f94935b7a3586ddb9
6
+ metadata.gz: bdca57213f85db7a3604ac1a5ed50a16a2918fded5b3b3e090e52aeb184414345d7609f08ef5145f205cf3fb9f1576f9e0aaa6b25a2e41684cfd506bed19e851
7
+ data.tar.gz: d7eb95a22516aa0f894262e0a1b52ee659736b88775936040a87db03260711e12dbc6764674799b5d2f8bdb86cb14495f01440a0703d300759c3482db17a810a
@@ -24,18 +24,18 @@ module Abt
24
24
  end
25
25
  end
26
26
 
27
- def prompt_choice(text, options, allow_back_option = false)
27
+ def prompt_choice(text, options, nil_option = false)
28
28
  warn "#{text}:"
29
29
 
30
30
  if options.length.zero?
31
- abort 'No available options' unless allow_back_option
31
+ abort 'No available options' unless nil_option
32
32
 
33
33
  warn 'No available options'
34
34
  return nil
35
35
  end
36
36
 
37
37
  print_options(options)
38
- select_options(options, allow_back_option)
38
+ select_options(options, nil_option)
39
39
  end
40
40
 
41
41
  private
@@ -46,11 +46,11 @@ module Abt
46
46
  end
47
47
  end
48
48
 
49
- def select_options(options, allow_back_option)
49
+ def select_options(options, nil_option)
50
50
  loop do
51
- number = read_option_number(options.length, allow_back_option)
51
+ number = read_option_number(options.length, nil_option)
52
52
  if number.nil?
53
- return nil if allow_back_option
53
+ return nil if nil_option
54
54
 
55
55
  next
56
56
  end
@@ -62,12 +62,12 @@ module Abt
62
62
  end
63
63
  end
64
64
 
65
- def read_option_number(options_length, allow_back_option)
66
- err_output.print "(1-#{options_length}#{allow_back_option ? ', q: back' : ''}): "
65
+ def read_option_number(options_length, nil_option)
66
+ err_output.print "(1-#{options_length}#{nil_option_string(nil_option)}): "
67
67
 
68
68
  input = read_user_input
69
69
 
70
- return nil if allow_back_option && input == 'q'
70
+ return nil if nil_option && input == nil_option_character(nil_option)
71
71
 
72
72
  option_number = input.to_i
73
73
  if option_number <= 0 || option_number > options_length
@@ -78,6 +78,25 @@ module Abt
78
78
  option_number
79
79
  end
80
80
 
81
+ def nil_option_string(nil_option)
82
+ return '' unless nil_option
83
+
84
+ ", #{nil_option_character(nil_option)}: #{nil_option_description(nil_option)}"
85
+ end
86
+
87
+ def nil_option_character(nil_option)
88
+ return 'q' if nil_option == true
89
+
90
+ nil_option[0]
91
+ end
92
+
93
+ def nil_option_description(nil_option)
94
+ return 'back' if nil_option == true
95
+ return nil_option if nil_option.is_a?(String)
96
+
97
+ nil_option[1]
98
+ end
99
+
81
100
  def read_user_input
82
101
  open('/dev/tty', &:gets).strip
83
102
  end
@@ -20,6 +20,17 @@ module Abt
20
20
 
21
21
  private
22
22
 
23
+ def require_project!
24
+ cli.abort 'No current/specified project. Did you initialize Asana?' if project_gid.nil?
25
+ end
26
+
27
+ def require_task!
28
+ if project_gid.nil?
29
+ cli.abort 'No current/specified project. Did you initialize Asana and pick a task?'
30
+ end
31
+ cli.abort 'No current/specified task. Did you pick an Asana task?' if task_gid.nil?
32
+ end
33
+
23
34
  def same_args_as_config?
24
35
  project_gid == config.project_gid && task_gid == config.task_gid
25
36
  end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abt
4
+ module Providers
5
+ module Asana
6
+ module Commands
7
+ class Add < BaseCommand
8
+ def self.command
9
+ 'add asana[:<project-gid>]'
10
+ end
11
+
12
+ def self.description
13
+ 'Create a new task for the current/specified Asana project'
14
+ end
15
+
16
+ def call
17
+ require_project!
18
+
19
+ task
20
+ print_task(project, task)
21
+
22
+ move_task if section
23
+ end
24
+
25
+ private
26
+
27
+ def task
28
+ @task ||= begin
29
+ body = {
30
+ data: {
31
+ name: name,
32
+ notes: notes,
33
+ projects: [project_gid]
34
+ }
35
+ }
36
+ cli.warn 'Creating task'
37
+ api.post('tasks', Oj.dump(body, mode: :json))
38
+ end
39
+ end
40
+
41
+ def move_task
42
+ body = { data: { task: task['gid'] } }
43
+ body_json = Oj.dump(body, mode: :json)
44
+ api.post("sections/#{section['gid']}/addTask", body_json)
45
+ end
46
+
47
+ def name
48
+ @name ||= cli.prompt 'Enter task description'
49
+ end
50
+
51
+ def notes
52
+ @notes ||= cli.prompt 'Enter task notes'
53
+ end
54
+
55
+ def project
56
+ @project ||= api.get("projects/#{project_gid}")
57
+ end
58
+
59
+ def section
60
+ @section ||= cli.prompt_choice 'Add to section?', sections, ['q', 'Don\'t add to section']
61
+ end
62
+
63
+ def sections
64
+ @sections ||= begin
65
+ cli.warn 'Fetching sections...'
66
+ api.get_paged("projects/#{project_gid}/sections", opt_fields: 'name')
67
+ rescue Abt::HttpError::HttpError
68
+ []
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -14,6 +14,8 @@ module Abt
14
14
  end
15
15
 
16
16
  def call
17
+ require_project!
18
+
17
19
  if same_args_as_config? || !config.local_available?
18
20
  show_current_configuration
19
21
  else
@@ -25,9 +27,7 @@ module Abt
25
27
  private
26
28
 
27
29
  def show_current_configuration
28
- if project_gid.nil?
29
- cli.warn 'No project selected'
30
- elsif task_gid.nil?
30
+ if task_gid.nil?
31
31
  print_project(project)
32
32
  else
33
33
  print_task(project, task)
@@ -17,7 +17,7 @@ module Abt
17
17
  unless config.local_available?
18
18
  cli.abort 'This is a no-op for tasks outside the current project'
19
19
  end
20
- cli.abort 'No current or specified task' if task.nil?
20
+ require_task!
21
21
  print_task(project_gid, task)
22
22
 
23
23
  if task_already_in_finalized_section?
@@ -14,6 +14,7 @@ module Abt
14
14
  end
15
15
 
16
16
  def call
17
+ require_task!
17
18
  ensure_current_is_valid!
18
19
 
19
20
  body = {
@@ -15,6 +15,7 @@ module Abt
15
15
 
16
16
  def call
17
17
  cli.abort 'Must be run inside a git repository' unless config.local_available?
18
+ require_project!
18
19
 
19
20
  cli.warn project['name']
20
21
 
@@ -14,9 +14,9 @@ module Abt
14
14
  end
15
15
 
16
16
  def call
17
- if project_gid.nil?
18
- cli.warn 'No project selected'
19
- elsif task_gid.nil?
17
+ require_project!
18
+
19
+ if task_gid.nil?
20
20
  cli.print_provider_command('asana', project_gid)
21
21
  else
22
22
  cli.print_provider_command('asana', "#{project_gid}/#{task_gid}")
@@ -14,7 +14,7 @@ module Abt
14
14
  end
15
15
 
16
16
  def call
17
- abort 'No current/provided task' if task_gid.nil?
17
+ require_task!
18
18
 
19
19
  maybe_override_current_task
20
20
 
@@ -14,6 +14,8 @@ module Abt
14
14
  end
15
15
 
16
16
  def call
17
+ require_project!
18
+
17
19
  tasks.each do |task|
18
20
  print_task(project, task)
19
21
  end
@@ -28,6 +28,18 @@ module Abt
28
28
  # TODO: Loop if necessary
29
29
  end
30
30
 
31
+ def work_item_query(wiql)
32
+ response = post('wit/wiql', Oj.dump({ query: wiql }, mode: :json))
33
+ ids = response['workItems'].map { |work_item| work_item['id'] }
34
+
35
+ work_items = []
36
+ ids.each_slice(200) do |page_ids|
37
+ work_items += get_paged('wit/workitems', ids: page_ids.join(','))
38
+ end
39
+
40
+ work_items
41
+ end
42
+
31
43
  def request(*args)
32
44
  response = connection.public_send(*args)
33
45
 
@@ -21,6 +21,22 @@ module Abt
21
21
 
22
22
  private
23
23
 
24
+ def require_board!
25
+ return if organization_name && project_name && board_id
26
+
27
+ cli.abort 'No current/specified board. Did you initialize DevOps?'
28
+ end
29
+
30
+ def require_work_item!
31
+ unless organization_name && project_name && board_id
32
+ cli.abort 'No current/specified board. Did you initialize DevOps and pick a work item?'
33
+ end
34
+
35
+ return if work_item_id
36
+
37
+ cli.abort 'No current/specified work item. Did you pick a DevOps work item?'
38
+ end
39
+
24
40
  def sanitize_work_item(work_item)
25
41
  return nil if work_item.nil?
26
42
 
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abt
4
+ module Providers
5
+ module Devops
6
+ module Commands
7
+ class Boards < BaseCommand
8
+ def self.command
9
+ 'boards devops'
10
+ end
11
+
12
+ def self.description
13
+ 'List all boards - useful for piping into grep etc'
14
+ end
15
+
16
+ def call
17
+ cli.abort 'No organization selected. Did you initialize DevOps?' if organization_name.nil?
18
+ cli.abort 'No project selected. Did you initialize DevOps?' if project_name.nil?
19
+
20
+ boards.map do |board|
21
+ print_board(organization_name, project_name, board)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def boards
28
+ @boards ||= api.get_paged('work/boards')
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -14,6 +14,8 @@ module Abt
14
14
  end
15
15
 
16
16
  def call
17
+ require_board!
18
+
17
19
  if same_args_as_config? || !config.local_available?
18
20
  show_current_configuration
19
21
  else
@@ -25,13 +27,7 @@ module Abt
25
27
  private
26
28
 
27
29
  def show_current_configuration
28
- if organization_name.nil?
29
- cli.warn 'No organization selected'
30
- elsif project_name.nil?
31
- cli.warn 'No project selected'
32
- elsif board_id.nil?
33
- cli.warn 'No board selected'
34
- elsif work_item_id.nil?
30
+ if work_item_id.nil?
35
31
  print_board(organization_name, project_name, board)
36
32
  else
37
33
  print_work_item(organization_name, project_name, board, work_item)
@@ -14,6 +14,8 @@ module Abt
14
14
  end
15
15
 
16
16
  def call
17
+ require_work_item!
18
+
17
19
  body = {
18
20
  notes: notes,
19
21
  external_reference: {
@@ -15,22 +15,26 @@ module Abt
15
15
 
16
16
  def call
17
17
  cli.abort 'Must be run inside a git repository' unless config.local_available?
18
+ require_board!
18
19
 
19
20
  cli.warn "#{project_name} - #{board['name']}"
20
21
 
21
22
  work_item = select_work_item
22
23
 
23
- # We might have gotten org, project, board as arg str
24
- config.organization_name = organization_name
25
- config.project_name = project_name
26
- config.board_id = board_id
27
- config.work_item_id = work_item['id']
24
+ update_config!(work_item)
28
25
 
29
26
  print_work_item(organization_name, project_name, board, work_item)
30
27
  end
31
28
 
32
29
  private
33
30
 
31
+ def update_config!(work_item)
32
+ config.organization_name = organization_name
33
+ config.project_name = project_name
34
+ config.board_id = board_id
35
+ config.work_item_id = work_item['id']
36
+ end
37
+
34
38
  def select_work_item
35
39
  loop do
36
40
  column = cli.prompt_choice 'Which column?', columns
@@ -48,16 +52,14 @@ module Abt
48
52
  end
49
53
 
50
54
  def work_items_in_column(column)
51
- wiql = <<~WIQL
52
- SELECT [System.Id]
53
- FROM WorkItems
54
- WHERE [System.BoardColumn] = '#{column['name']}'
55
- ORDER BY [Microsoft.VSTS.Common.BacklogPriority] ASC
56
- WIQL
57
-
58
- response = api.post('wit/wiql', Oj.dump({ query: wiql }, mode: :json))
59
- ids = response['workItems'].map { |work_item| work_item['id'] }
60
- work_items = api.get_paged('wit/workitems', ids: ids.join(','))
55
+ work_items = api.work_item_query(
56
+ <<~WIQL
57
+ SELECT [System.Id]
58
+ FROM WorkItems
59
+ WHERE [System.BoardColumn] = '#{column['name']}'
60
+ ORDER BY [Microsoft.VSTS.Common.BacklogPriority] ASC
61
+ WIQL
62
+ )
61
63
 
62
64
  work_items.map { |work_item| sanitize_work_item(work_item) }
63
65
  end
@@ -14,16 +14,10 @@ module Abt
14
14
  end
15
15
 
16
16
  def call
17
- if organization_name.nil?
18
- cli.warn 'No organization selected'
19
- elsif project_name.nil?
20
- cli.warn 'No project selected'
21
- elsif board_id.nil?
22
- cli.warn 'No board selected'
23
- else
24
- args = [organization_name, project_name, board_id, work_item_id].compact
25
- cli.print_provider_command('devops', args.join('/'))
26
- end
17
+ require_work_item!
18
+
19
+ args = [organization_name, project_name, board_id, work_item_id].compact
20
+ cli.print_provider_command('devops', args.join('/'))
27
21
  end
28
22
  end
29
23
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abt
4
+ module Providers
5
+ module Devops
6
+ module Commands
7
+ class WorkItems < BaseCommand
8
+ def self.command
9
+ 'work-items devops'
10
+ end
11
+
12
+ def self.description
13
+ 'List all work items on board - useful for piping into grep etc.'
14
+ end
15
+
16
+ def call
17
+ require_board!
18
+
19
+ work_items.each do |work_item|
20
+ print_work_item(organization_name, project_name, board, work_item)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def work_items
27
+ @work_items ||= begin
28
+ cli.warn 'Fetching work items...'
29
+ api.work_item_query(
30
+ <<~WIQL
31
+ SELECT [System.Id]
32
+ FROM WorkItems
33
+ ORDER BY [System.Title] ASC
34
+ WIQL
35
+ ).map { |work_item| sanitize_work_item(work_item) }
36
+ end
37
+ end
38
+
39
+ def board
40
+ @board ||= api.get("work/boards/#{board_id}")
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -20,6 +20,17 @@ module Abt
20
20
 
21
21
  private
22
22
 
23
+ def require_project!
24
+ cli.abort 'No current/specified project. Did you initialize Harvest?' if project_id.nil?
25
+ end
26
+
27
+ def require_task!
28
+ if project_id.nil?
29
+ cli.abort 'No current/specified project. Did you initialize Harvest and pick a task?'
30
+ end
31
+ cli.abort 'No current/specified task. Did you pick a Harvest task?' if task_id.nil?
32
+ end
33
+
23
34
  def same_args_as_config?
24
35
  project_id == config.project_id && task_id == config.task_id
25
36
  end
@@ -14,6 +14,8 @@ module Abt
14
14
  end
15
15
 
16
16
  def call
17
+ require_project!
18
+
17
19
  if same_args_as_config? || !config.local_available?
18
20
  show_current_configuration
19
21
  else
@@ -25,9 +27,7 @@ module Abt
25
27
  private
26
28
 
27
29
  def show_current_configuration
28
- if project_id.nil?
29
- cli.warn 'No project selected'
30
- elsif task_id.nil?
30
+ if task_id.nil?
31
31
  print_project(project)
32
32
  else
33
33
  print_task(project, task)
@@ -15,6 +15,7 @@ module Abt
15
15
 
16
16
  def call
17
17
  cli.abort 'Must be run inside a git repository' unless config.local_available?
18
+ require_project!
18
19
 
19
20
  cli.warn project['name']
20
21
  task = cli.prompt_choice 'Select a task', tasks
@@ -37,9 +37,7 @@ module Abt
37
37
  output = StringIO.new
38
38
  Abt::Cli.new(argv: ['track'], output: output, input: input).perform
39
39
 
40
- output_str = output.string.strip
41
- cli.abort 'No task provided' if output_str.empty?
42
- output_str
40
+ output.string.strip
43
41
  end
44
42
 
45
43
  def maybe_override_current_task
@@ -14,6 +14,8 @@ module Abt
14
14
  end
15
15
 
16
16
  def call
17
+ require_project!
18
+
17
19
  tasks.each do |task|
18
20
  print_task(project, task)
19
21
  end
@@ -14,8 +14,7 @@ module Abt
14
14
  end
15
15
 
16
16
  def call
17
- abort 'No current/provided task' if task_id.nil?
18
- cli.abort('No task selected') if task_id.nil?
17
+ require_task!
19
18
 
20
19
  print_task(created_time_entry['project'], created_time_entry['task'])
21
20
 
data/lib/abt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Abt
4
- VERSION = '0.0.11'
4
+ VERSION = '0.0.12'
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.11
4
+ version: 0.0.12
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-29 00:00:00.000000000 Z
11
+ date: 2021-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-inflector
@@ -88,6 +88,7 @@ files:
88
88
  - "./lib/abt/providers/asana.rb"
89
89
  - "./lib/abt/providers/asana/api.rb"
90
90
  - "./lib/abt/providers/asana/base_command.rb"
91
+ - "./lib/abt/providers/asana/commands/add.rb"
91
92
  - "./lib/abt/providers/asana/commands/clear.rb"
92
93
  - "./lib/abt/providers/asana/commands/clear_global.rb"
93
94
  - "./lib/abt/providers/asana/commands/current.rb"
@@ -103,6 +104,7 @@ files:
103
104
  - "./lib/abt/providers/devops.rb"
104
105
  - "./lib/abt/providers/devops/api.rb"
105
106
  - "./lib/abt/providers/devops/base_command.rb"
107
+ - "./lib/abt/providers/devops/commands/boards.rb"
106
108
  - "./lib/abt/providers/devops/commands/clear.rb"
107
109
  - "./lib/abt/providers/devops/commands/clear_global.rb"
108
110
  - "./lib/abt/providers/devops/commands/current.rb"
@@ -110,6 +112,7 @@ files:
110
112
  - "./lib/abt/providers/devops/commands/init.rb"
111
113
  - "./lib/abt/providers/devops/commands/pick.rb"
112
114
  - "./lib/abt/providers/devops/commands/share.rb"
115
+ - "./lib/abt/providers/devops/commands/work-items.rb"
113
116
  - "./lib/abt/providers/devops/configuration.rb"
114
117
  - "./lib/abt/providers/harvest.rb"
115
118
  - "./lib/abt/providers/harvest/api.rb"