jura 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +2 -0
- data/bin/jura +14 -0
- data/lib/jura.rb +10 -0
- data/lib/jura/api.rb +6 -0
- data/lib/jura/api/board.rb +25 -0
- data/lib/jura/api/client.rb +22 -0
- data/lib/jura/api/issue.rb +42 -0
- data/lib/jura/api/sprint.rb +44 -0
- data/lib/jura/api/token.rb +33 -0
- data/lib/jura/application.rb +74 -0
- data/lib/jura/command.rb +50 -0
- data/lib/jura/command/board.rb +23 -0
- data/lib/jura/command/board/select.rb +25 -0
- data/lib/jura/command/exit.rb +12 -0
- data/lib/jura/command/help.rb +11 -0
- data/lib/jura/command/invalid.rb +11 -0
- data/lib/jura/command/issue.rb +28 -0
- data/lib/jura/command/issue/base.rb +18 -0
- data/lib/jura/command/issue/list.rb +14 -0
- data/lib/jura/command/issue/mine.rb +14 -0
- data/lib/jura/command/issue/select.rb +22 -0
- data/lib/jura/command/issue/show.rb +18 -0
- data/lib/jura/command/sprint.rb +22 -0
- data/lib/jura/command/sprint/base.rb +18 -0
- data/lib/jura/command/sprint/list.rb +14 -0
- data/lib/jura/command/sprint/show.rb +22 -0
- data/lib/jura/component.rb +5 -0
- data/lib/jura/component/board.rb +21 -0
- data/lib/jura/component/board/help.rb +17 -0
- data/lib/jura/component/help.rb +24 -0
- data/lib/jura/component/issue.rb +43 -0
- data/lib/jura/component/issue/help.rb +18 -0
- data/lib/jura/component/issue/show.rb +30 -0
- data/lib/jura/component/logo.rb +22 -0
- data/lib/jura/component/sprint.rb +22 -0
- data/lib/jura/component/sprint/help.rb +17 -0
- data/lib/jura/component/sprint/show.rb +44 -0
- data/lib/jura/configuration.rb +36 -0
- data/lib/jura/utils.rb +32 -0
- data/lib/jura/version.rb +3 -0
- metadata +111 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: efabee656c658d42bf38e4a326b957167aecbbdb9c53bab0b0f972eef96781aa
|
4
|
+
data.tar.gz: d948e08cb3bc8670a89b4624ed5a6df812f170bbcd9d9e56984b87fff21d40db
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 57b2775f58a8f26a06a26a856c4181671f1bb43cbe903ac4e77602335bdce99517d5d6efe5e38b84f2a6f1f3e18a93deca923064509cf7a7dd4a4dd0b07ef4e6
|
7
|
+
data.tar.gz: 786a49e54f760545f478e776c569e93aa7c17df8b7d49cf4bd598a9bb70391c16b4b3fffc518835242fd2097891e66b98785ff2decd716ea98590607607ad406
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2020 Hoa Nguyen
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
data/bin/jura
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
4
|
+
require 'httparty'
|
5
|
+
require 'jura'
|
6
|
+
require 'tty-prompt'
|
7
|
+
require 'tty-table'
|
8
|
+
require 'tty-box'
|
9
|
+
require 'tty-markdown'
|
10
|
+
require 'tty-screen'
|
11
|
+
require 'pastel'
|
12
|
+
require 'optparse'
|
13
|
+
|
14
|
+
Jura::Application.start(ARGV)
|
data/lib/jura.rb
ADDED
data/lib/jura/api.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Api
|
5
|
+
module Board
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def all
|
9
|
+
parse_body(
|
10
|
+
Client.get('/board', {
|
11
|
+
headers: {
|
12
|
+
"Authorization" => "Basic #{Token.get_token}"
|
13
|
+
}
|
14
|
+
}).body
|
15
|
+
)['values']
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def parse_body(body)
|
21
|
+
JSON.parse(body)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'base64'
|
3
|
+
require 'singleton'
|
4
|
+
require 'httparty'
|
5
|
+
|
6
|
+
module Jura
|
7
|
+
module Api
|
8
|
+
class Client
|
9
|
+
include Singleton
|
10
|
+
include HTTParty
|
11
|
+
|
12
|
+
base_uri 'https://employmenthero.atlassian.net/rest/agile/1.0'
|
13
|
+
|
14
|
+
headers ({
|
15
|
+
"Accept" => "application/json",
|
16
|
+
"Content-Type" => "application/json",
|
17
|
+
})
|
18
|
+
|
19
|
+
raise_on [401, 404, 500]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Api
|
5
|
+
module Issue
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def all(board_id, query = nil)
|
9
|
+
options = {
|
10
|
+
headers: {
|
11
|
+
"Authorization" => "Basic #{Token.get_token}"
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
url = "/board/#{board_id}/issue?"
|
16
|
+
url = "#{url}jql=#{query}" unless query.nil?
|
17
|
+
|
18
|
+
parse_body(
|
19
|
+
Client.get(url, options).body
|
20
|
+
)['issues']
|
21
|
+
end
|
22
|
+
|
23
|
+
def show(issue_id)
|
24
|
+
options = {
|
25
|
+
headers: {
|
26
|
+
"Authorization" => "Basic #{Token.get_token}"
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
url = "/issue/#{issue_id}"
|
31
|
+
|
32
|
+
parse_body(Client.get(url, options).body)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def parse_body(body)
|
38
|
+
JSON.parse(body)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Api
|
5
|
+
module Sprint
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def all(board_id, state = nil)
|
9
|
+
options = {
|
10
|
+
headers: {
|
11
|
+
"Authorization" => "Basic #{Token.get_token}"
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
url = "/board/#{board_id}/sprint?"
|
16
|
+
url = "#{url}state=#{state}" unless state.nil?
|
17
|
+
|
18
|
+
parse_body(
|
19
|
+
Client.get(url, options).body
|
20
|
+
)['values']
|
21
|
+
end
|
22
|
+
|
23
|
+
def show(board_id, sprint_id)
|
24
|
+
options = {
|
25
|
+
headers: {
|
26
|
+
"Authorization" => "Basic #{Token.get_token}"
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
url = "/board/#{board_id}/sprint/#{sprint_id}/issue"
|
31
|
+
|
32
|
+
parse_body(
|
33
|
+
Client.get(url, options).body
|
34
|
+
)['issues']
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def parse_body(body)
|
40
|
+
JSON.parse(body)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Api
|
5
|
+
module Token
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def verify?(email, token)
|
9
|
+
values = parse_body(
|
10
|
+
Client.get('/board?total=1', {
|
11
|
+
headers: {
|
12
|
+
"Authorization" => "Basic #{Base64.urlsafe_encode64("#{email}:#{token}")}"
|
13
|
+
}
|
14
|
+
}).body
|
15
|
+
)['values']
|
16
|
+
|
17
|
+
!values.nil?
|
18
|
+
rescue HTTParty::ResponseError => e
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_token
|
23
|
+
Jura::Configuration.instance.config_obj['token']
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def parse_body(body)
|
29
|
+
JSON.parse(body)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Application
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def start(args)
|
8
|
+
Readline.completion_append_character = " "
|
9
|
+
|
10
|
+
Readline.completion_proc = lambda { |buffer|
|
11
|
+
Command.generate_suggestions(buffer, Readline.line_buffer)
|
12
|
+
}
|
13
|
+
|
14
|
+
prompt = TTY::Prompt.new
|
15
|
+
|
16
|
+
prompt.say(Jura::Component::Logo.render)
|
17
|
+
prompt.say(Jura::Component::Help.render)
|
18
|
+
|
19
|
+
config = Jura::Configuration.instance.load_config
|
20
|
+
|
21
|
+
if config.empty?
|
22
|
+
config = config_credentials(prompt)
|
23
|
+
else
|
24
|
+
prompt.say("Welcome back!, " + Utils.paint(config['email'], :green) + "\n\n")
|
25
|
+
prompt.say("Your selected board is " + Utils.paint(config['selected_board_name'], :green) + "\n\n") unless config['selected_board_id'].nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
Jura::Configuration.instance.set_config(config)
|
29
|
+
|
30
|
+
loop do
|
31
|
+
command_buffer = Readline.readline("\e[15;48;5;27m Jura Guarrr! \e[0m > ", true)
|
32
|
+
|
33
|
+
Jura::Command.execute!(command_buffer.strip())
|
34
|
+
rescue IndexError, NoMethodError => _e
|
35
|
+
Command::Invalid.execute("Something went wrong, please try with another command")
|
36
|
+
end
|
37
|
+
rescue Interrupt
|
38
|
+
Command::Exit.execute
|
39
|
+
end
|
40
|
+
|
41
|
+
def config_credentials(prompt)
|
42
|
+
email = prompt.ask("Jira account email: ") { |q| q.validate :email }
|
43
|
+
|
44
|
+
has_token = prompt.yes?("Already have your JIRA token?")
|
45
|
+
|
46
|
+
unless has_token
|
47
|
+
prompt.say("\nOpening Your default browser")
|
48
|
+
prompt.warn("> Warning: If browser does not open, visit")
|
49
|
+
prompt.warn("> https://id.atlassian.com/manage-profile/security/api-tokens to obtain your token\n")
|
50
|
+
|
51
|
+
system "open https://id.atlassian.com/manage-profile/security/api-tokens"
|
52
|
+
end
|
53
|
+
|
54
|
+
token = prompt.mask("Input your Jira token: ") do|q|
|
55
|
+
q.required true
|
56
|
+
end
|
57
|
+
|
58
|
+
config = {}
|
59
|
+
|
60
|
+
if Jura::Api::Token.verify?(email, token)
|
61
|
+
prompt.say("Logged in as #{email}\n", color: :green)
|
62
|
+
prompt.say("Configuration has been saved to " + Utils.paint(Configuration::CONFIG_FILE_PATH, :green) + "\n")
|
63
|
+
|
64
|
+
config = { 'email' => email, 'token' => Base64.urlsafe_encode64("#{email}:#{token}") }
|
65
|
+
|
66
|
+
Jura::Configuration.instance.save_config(config)
|
67
|
+
else
|
68
|
+
prompt.say("Your token or email is invalid", color: :red)
|
69
|
+
end
|
70
|
+
|
71
|
+
config
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/jura/command.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'jura/command/board'
|
3
|
+
require 'jura/command/issue'
|
4
|
+
require 'jura/command/sprint'
|
5
|
+
require 'jura/command/help'
|
6
|
+
require 'jura/command/exit'
|
7
|
+
require 'jura/command/invalid'
|
8
|
+
|
9
|
+
module Jura
|
10
|
+
module Command
|
11
|
+
COMMANDS = {
|
12
|
+
"board" => -> (sub_cmd, args) { Command::Board.execute!(sub_cmd, args) },
|
13
|
+
"issue" => -> (sub_cmd, args) { Command::Issue.execute!(sub_cmd, args) },
|
14
|
+
"sprint" => -> (sub_cmd, args) { Command::Sprint.execute!(sub_cmd, args) },
|
15
|
+
"help" => -> (*_args) { Command::Help.execute() },
|
16
|
+
"exit" => -> (*_args) { Command::Exit.execute() }
|
17
|
+
}
|
18
|
+
|
19
|
+
def self.execute!(cmd_buffer)
|
20
|
+
cmd_name, sub_cmd, *args = cmd_buffer.to_s.strip.split(" ")
|
21
|
+
|
22
|
+
if cmd_name.nil? || cmd_name.empty?
|
23
|
+
# TODO: return and execute empty command error
|
24
|
+
end
|
25
|
+
|
26
|
+
command = COMMANDS[cmd_name]
|
27
|
+
|
28
|
+
if command.nil?
|
29
|
+
return Command::Invalid.execute("Command not found: #{command.inspect}. Run #{"help".inspect} for more informations")
|
30
|
+
end
|
31
|
+
|
32
|
+
puts '' # Empty line
|
33
|
+
command.call(sub_cmd, args)
|
34
|
+
puts '' # Empty line
|
35
|
+
rescue Command::Board::RequiredBoardIdError => _
|
36
|
+
puts 'Please select a board first!'
|
37
|
+
Command::Board.execute!('select', [])
|
38
|
+
end
|
39
|
+
|
40
|
+
def generate_suggestions(buffer, command_buffer)
|
41
|
+
commands = {
|
42
|
+
"board" => %[list select],
|
43
|
+
"issue" => %[list],
|
44
|
+
"sprint" => %[list show]
|
45
|
+
}
|
46
|
+
|
47
|
+
commands.keys.grep(/^#{Regexp.escape(buffer)}/)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'jura/command/board/select'
|
4
|
+
|
5
|
+
module Jura
|
6
|
+
module Command
|
7
|
+
module Board
|
8
|
+
class RequiredBoardIdError < StandardError; end
|
9
|
+
|
10
|
+
def self.execute!(sub_cmd, args)
|
11
|
+
case sub_cmd
|
12
|
+
when "list"
|
13
|
+
boards = Api::Board.all
|
14
|
+
puts Component::Board.render(boards)
|
15
|
+
when "select"
|
16
|
+
Command::Board::Select.execute
|
17
|
+
else
|
18
|
+
Command::Invalid.execute("Command not found: #{sub_cmd.inspect}. Run #{"help".inspect} for more informations")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Command
|
5
|
+
module Board
|
6
|
+
class Select
|
7
|
+
def self.execute
|
8
|
+
prompt = TTY::Prompt.new
|
9
|
+
boards = Api::Board.all
|
10
|
+
board = prompt.select("Choose your board?", filter: true, per_page: 10) do |menu|
|
11
|
+
boards.each do |b|
|
12
|
+
menu.choice b['location']['displayName'], b
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
config = Jura::Configuration.instance.load_config
|
17
|
+
config['selected_board_id'] = board['id']
|
18
|
+
config['selected_board_name'] = board['location']['displayName']
|
19
|
+
|
20
|
+
Jura::Configuration.instance.save_config(config)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'jura/command/issue/base'
|
4
|
+
require 'jura/command/issue/list'
|
5
|
+
require 'jura/command/issue/show'
|
6
|
+
require 'jura/command/issue/mine'
|
7
|
+
require 'jura/command/issue/select'
|
8
|
+
|
9
|
+
module Jura
|
10
|
+
module Command
|
11
|
+
module Issue
|
12
|
+
def self.execute!(sub_cmd, args)
|
13
|
+
case sub_cmd
|
14
|
+
when "mine"
|
15
|
+
Command::Issue::Mine.execute!
|
16
|
+
when "select"
|
17
|
+
Command::Issue::Select.execute!
|
18
|
+
when "list"
|
19
|
+
Command::Issue::List.execute!
|
20
|
+
when "show"
|
21
|
+
Command::Issue::Show.execute!(args)
|
22
|
+
else
|
23
|
+
Command::Invalid.execute("Command not found: #{sub_cmd.inspect}. Run #{"help".inspect} for more informations")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Command
|
5
|
+
module Issue
|
6
|
+
class Base
|
7
|
+
def self.board_id
|
8
|
+
config = Jura::Configuration.instance.load_config
|
9
|
+
board_id = config['selected_board_id']
|
10
|
+
|
11
|
+
raise Command::Board::RequiredBoardIdError if board_id.nil?
|
12
|
+
|
13
|
+
board_id
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Command
|
5
|
+
module Issue
|
6
|
+
class Mine < Base
|
7
|
+
def self.execute!
|
8
|
+
issues = Api::Issue.all(board_id, "assignee = currentUser()")
|
9
|
+
puts Component::Issue.render(issues)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Command
|
5
|
+
module Issue
|
6
|
+
class Select < Base
|
7
|
+
def self.execute!
|
8
|
+
prompt = TTY::Prompt.new
|
9
|
+
issues = Api::Issue.all(board_id, "assignee = currentUser()")
|
10
|
+
issue = prompt.select("Choose issue", filter: true, per_page: 10) do |menu|
|
11
|
+
issues.each do |b|
|
12
|
+
menu.choice "#{b['key']} - #{b['fields']['summary']}", b
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
issue = Api::Issue.show(issue['key'])
|
17
|
+
puts Component::Issue::Show.render(issue)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Command
|
5
|
+
module Issue
|
6
|
+
class Show < Base
|
7
|
+
def self.execute!(args)
|
8
|
+
if (args.length != 1)
|
9
|
+
return
|
10
|
+
end
|
11
|
+
|
12
|
+
issue = Api::Issue.show(args[0])
|
13
|
+
puts Component::Issue::Show.render(issue)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'jura/command/sprint/base'
|
4
|
+
require 'jura/command/sprint/list'
|
5
|
+
require 'jura/command/sprint/show'
|
6
|
+
|
7
|
+
module Jura
|
8
|
+
module Command
|
9
|
+
module Sprint
|
10
|
+
def self.execute!(sub_cmd, args)
|
11
|
+
case sub_cmd
|
12
|
+
when "show"
|
13
|
+
Command::Sprint::Show.execute!(args)
|
14
|
+
when "list"
|
15
|
+
Command::Sprint::List.execute!
|
16
|
+
else
|
17
|
+
Command::Invalid.execute("Command not found: #{command.inspect}. Run #{"help".inspect} for more informations")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Command
|
5
|
+
module Sprint
|
6
|
+
class Base
|
7
|
+
def self.board_id
|
8
|
+
config = Jura::Configuration.instance.load_config
|
9
|
+
board_id = config['selected_board_id']
|
10
|
+
|
11
|
+
raise Command::Board::RequiredBoardIdError if board_id.nil?
|
12
|
+
|
13
|
+
board_id
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Command
|
5
|
+
module Sprint
|
6
|
+
class Show < Base
|
7
|
+
def self.execute!(args)
|
8
|
+
if args.length != 1
|
9
|
+
return
|
10
|
+
end
|
11
|
+
|
12
|
+
sprint_id = args[0]
|
13
|
+
|
14
|
+
issues = Api::Sprint.show(board_id, sprint_id)
|
15
|
+
puts Component::Sprint::Show.render(issues)
|
16
|
+
rescue HTTParty::ResponseError => _e
|
17
|
+
Command::Invalid.execute("Something went wrong, please try with another command")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jura/component/board/help"
|
4
|
+
|
5
|
+
module Jura
|
6
|
+
module Component
|
7
|
+
module Board
|
8
|
+
def self.render(boards)
|
9
|
+
<<~TEMPLATE
|
10
|
+
#{boards.map { |board| render_board(board) }.join("\n")}
|
11
|
+
TEMPLATE
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def self.render_board(board)
|
17
|
+
"#{board['id']} - #{board['location']['displayName']}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Component
|
5
|
+
module Board
|
6
|
+
class Help
|
7
|
+
def self.render
|
8
|
+
<<~TEMPLATE.strip
|
9
|
+
#{Utils.format_bold("# Available board commands:")}
|
10
|
+
board list - Show list of boards
|
11
|
+
board select - Select board
|
12
|
+
TEMPLATE
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Component
|
5
|
+
class Help
|
6
|
+
def self.render
|
7
|
+
<<~TEMPLATE
|
8
|
+
|
9
|
+
#{Component::Board::Help.render()}
|
10
|
+
|
11
|
+
#{Component::Issue::Help.render()}
|
12
|
+
|
13
|
+
#{Component::Sprint::Help.render()}
|
14
|
+
|
15
|
+
#{Utils.format_bold('# Miscellaneous commands:')}
|
16
|
+
|
17
|
+
help - Display this help message
|
18
|
+
exit - Escape from Jurassic park
|
19
|
+
|
20
|
+
TEMPLATE
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jura/component/issue/help"
|
4
|
+
require "jura/component/issue/show"
|
5
|
+
|
6
|
+
module Jura
|
7
|
+
module Component
|
8
|
+
module Issue
|
9
|
+
def self.render(issues)
|
10
|
+
<<~TEMPLATE
|
11
|
+
#{issues.map { |i| render_issue(i) }.join("\n")}
|
12
|
+
TEMPLATE
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def self.render_issue(issue)
|
18
|
+
"#{convert_key(issue)} - #{paint(issue)} - #{issue['fields']['summary']}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.paint(issue)
|
22
|
+
status = issue.dig('fields','status', 'name')
|
23
|
+
p = Pastel.new
|
24
|
+
|
25
|
+
case status.downcase
|
26
|
+
when 'in progress'
|
27
|
+
p.decorate(status,:blue)
|
28
|
+
when 'in review'
|
29
|
+
p.decorate(status,:magenta)
|
30
|
+
when 'done'
|
31
|
+
p.decorate(status,:green)
|
32
|
+
else
|
33
|
+
p.decorate(status,:white)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.convert_key(issue)
|
38
|
+
kws = issue['key'].split('-')
|
39
|
+
"#{kws[0]}-#{(kws[1].rjust(3, ' '))}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Component
|
5
|
+
module Issue
|
6
|
+
class Help
|
7
|
+
def self.render
|
8
|
+
<<~TEMPLATE.strip
|
9
|
+
#{Utils.format_bold("# Available issue commands:")}
|
10
|
+
issue list - Show list of issues in backlog
|
11
|
+
issue mine - Show list of issues which have assingee is mine
|
12
|
+
issue show <issue_id> - Show details of an issue
|
13
|
+
TEMPLATE
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Component
|
5
|
+
module Issue
|
6
|
+
class Show
|
7
|
+
def self.render(issue)
|
8
|
+
issue_id = issue['key']
|
9
|
+
project = issue['fields']['project']['name']
|
10
|
+
box = TTY::Box.frame(
|
11
|
+
border: :thick,
|
12
|
+
padding: [1,2],
|
13
|
+
title: {top_left: issue_id, bottom_right: project},
|
14
|
+
width: TTY::Screen.width
|
15
|
+
) do
|
16
|
+
<<~TEMPLATE
|
17
|
+
#{Jura::Utils.format_bold(issue['fields']['summary'])}
|
18
|
+
|
19
|
+
#{Jura::Utils.format_bold('URL')}: #{issue['self']}
|
20
|
+
|
21
|
+
#{Jura::Utils.format_bold('Description')}
|
22
|
+
|
23
|
+
#{TTY::Markdown.parse(issue['fields']['description'])}
|
24
|
+
TEMPLATE
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Component
|
5
|
+
class Logo
|
6
|
+
def self.render
|
7
|
+
<<~TEMPLATE
|
8
|
+
|
9
|
+
██╗██╗ ██╗██████╗ █████╗ ██╗██╗██████╗ █████╗ ██████╗ ██╗ ██╗██████╗ ██╗ ██╗
|
10
|
+
██║██║ ██║██╔══██╗██╔══██╗ ██║██║██╔══██╗██╔══██╗ ██╔══██╗██║ ██║██╔══██╗╚██╗ ██╔╝
|
11
|
+
██║██║ ██║██████╔╝███████║ █████╗ ██║██║██████╔╝███████║ ██████╔╝██║ ██║██████╔╝ ╚████╔╝
|
12
|
+
██ ██║██║ ██║██╔══██╗██╔══██║ ╚════╝ ██ ██║██║██╔══██╗██╔══██║ ██╔══██╗██║ ██║██╔══██╗ ╚██╔╝
|
13
|
+
╚█████╔╝╚██████╔╝██║ ██║██║ ██║ ╚█████╔╝██║██║ ██║██║ ██║ ██║ ██║╚██████╔╝██████╔╝ ██║
|
14
|
+
╚════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚════╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝
|
15
|
+
|
16
|
+
#{Utils.format_bold(Utils.paint("Jura v#{Jura::VERSION} - Interactive CLI application for Jira", :blue))}
|
17
|
+
|
18
|
+
TEMPLATE
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jura/component/sprint/help"
|
4
|
+
require "jura/component/sprint/show"
|
5
|
+
|
6
|
+
module Jura
|
7
|
+
module Component
|
8
|
+
module Sprint
|
9
|
+
def self.render(sprints)
|
10
|
+
<<~TEMPLATE
|
11
|
+
#{sprints.map { |s| render_sprint(s) }.join("\n")}
|
12
|
+
TEMPLATE
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def self.render_sprint(sprint)
|
18
|
+
"#{sprint['id']} - #{sprint['name']} - #{sprint['state']}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Component
|
5
|
+
module Sprint
|
6
|
+
class Help
|
7
|
+
def self.render
|
8
|
+
<<~TEMPLATE.strip
|
9
|
+
#{Utils.format_bold("# Available sprint commands:")}
|
10
|
+
sprint list - Show list of sprints
|
11
|
+
sprint show <sprint_id> - Show list of issues of 1 sprint
|
12
|
+
TEMPLATE
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
module Component
|
5
|
+
module Sprint
|
6
|
+
class Show
|
7
|
+
def self.render(issues)
|
8
|
+
table = TTY::Table.new(
|
9
|
+
header: [
|
10
|
+
"TO DO",
|
11
|
+
Jura::Utils.paint("IN PROGRESS", :blue),
|
12
|
+
Jura::Utils.paint("IN REVIEW", :purple),
|
13
|
+
Jura::Utils.paint("DONE", :green)
|
14
|
+
]
|
15
|
+
)
|
16
|
+
convertIssues(table, issues)
|
17
|
+
table.render :unicode, resize: true, column_widths: [25, 25, 25, 25] do |renderer|
|
18
|
+
renderer.border.separator = :each_row
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.convertIssues(table, issues)
|
23
|
+
issues.each do |issue|
|
24
|
+
state = issue['fields']['status']['name']
|
25
|
+
case state.downcase
|
26
|
+
when 'to do'
|
27
|
+
table << [render_issue(issue), '', '', '']
|
28
|
+
when 'in progress'
|
29
|
+
table << ['', render_issue(issue), '', '']
|
30
|
+
when 'in review'
|
31
|
+
table << ['', '', render_issue(issue), '']
|
32
|
+
when 'done'
|
33
|
+
table << ['', '', '', render_issue(issue)]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.render_issue(issue)
|
39
|
+
"#{issue['key']} - #{issue['fields']['summary']}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
module Jura
|
5
|
+
class Configuration
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
CONFIG_FILE_PATH = "~/.jura.config.json"
|
9
|
+
|
10
|
+
def set_config(data)
|
11
|
+
@config_obj = data
|
12
|
+
end
|
13
|
+
|
14
|
+
def freeze_config
|
15
|
+
config_obj.freeze
|
16
|
+
end
|
17
|
+
|
18
|
+
def config_obj
|
19
|
+
@config_obj ||= {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def load_config
|
23
|
+
config_path = File.expand_path(CONFIG_FILE_PATH)
|
24
|
+
|
25
|
+
if File.exist?(config_path)
|
26
|
+
JSON.parse(File.read(config_path))
|
27
|
+
else
|
28
|
+
{}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def save_config(config)
|
33
|
+
File.write(File.expand_path(CONFIG_FILE_PATH), config.to_json)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/jura/utils.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jura
|
4
|
+
class Utils
|
5
|
+
class << self
|
6
|
+
COLOR = {
|
7
|
+
:red => 31,
|
8
|
+
:pink => 31,
|
9
|
+
:blue => 34,
|
10
|
+
:green => 32,
|
11
|
+
:lime => 32,
|
12
|
+
:black => 37,
|
13
|
+
:white => 37,
|
14
|
+
:purple => 35,
|
15
|
+
:yellow => 33,
|
16
|
+
:orange => 33,
|
17
|
+
:cyan => 36,
|
18
|
+
:sky => 36
|
19
|
+
}
|
20
|
+
|
21
|
+
def paint(string, color)
|
22
|
+
code = COLOR.fetch(color)
|
23
|
+
|
24
|
+
"\e[#{code}m#{string}\e[0m"
|
25
|
+
end
|
26
|
+
|
27
|
+
def format_bold(string)
|
28
|
+
"\e[1m#{string}\e[0m"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/jura/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jura
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Hoa Nguyen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-09-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: tty-prompt
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.22'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.22'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: httparty
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.18.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.18.1
|
41
|
+
description: A simple Jira CLI by Ruby
|
42
|
+
email: nvh0412@gmail.com
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- LICENSE
|
48
|
+
- README.md
|
49
|
+
- bin/jura
|
50
|
+
- lib/jura.rb
|
51
|
+
- lib/jura/api.rb
|
52
|
+
- lib/jura/api/board.rb
|
53
|
+
- lib/jura/api/client.rb
|
54
|
+
- lib/jura/api/issue.rb
|
55
|
+
- lib/jura/api/sprint.rb
|
56
|
+
- lib/jura/api/token.rb
|
57
|
+
- lib/jura/application.rb
|
58
|
+
- lib/jura/command.rb
|
59
|
+
- lib/jura/command/board.rb
|
60
|
+
- lib/jura/command/board/select.rb
|
61
|
+
- lib/jura/command/exit.rb
|
62
|
+
- lib/jura/command/help.rb
|
63
|
+
- lib/jura/command/invalid.rb
|
64
|
+
- lib/jura/command/issue.rb
|
65
|
+
- lib/jura/command/issue/base.rb
|
66
|
+
- lib/jura/command/issue/list.rb
|
67
|
+
- lib/jura/command/issue/mine.rb
|
68
|
+
- lib/jura/command/issue/select.rb
|
69
|
+
- lib/jura/command/issue/show.rb
|
70
|
+
- lib/jura/command/sprint.rb
|
71
|
+
- lib/jura/command/sprint/base.rb
|
72
|
+
- lib/jura/command/sprint/list.rb
|
73
|
+
- lib/jura/command/sprint/show.rb
|
74
|
+
- lib/jura/component.rb
|
75
|
+
- lib/jura/component/board.rb
|
76
|
+
- lib/jura/component/board/help.rb
|
77
|
+
- lib/jura/component/help.rb
|
78
|
+
- lib/jura/component/issue.rb
|
79
|
+
- lib/jura/component/issue/help.rb
|
80
|
+
- lib/jura/component/issue/show.rb
|
81
|
+
- lib/jura/component/logo.rb
|
82
|
+
- lib/jura/component/sprint.rb
|
83
|
+
- lib/jura/component/sprint/help.rb
|
84
|
+
- lib/jura/component/sprint/show.rb
|
85
|
+
- lib/jura/configuration.rb
|
86
|
+
- lib/jura/utils.rb
|
87
|
+
- lib/jura/version.rb
|
88
|
+
homepage: https://rubygems.org/gems/jura
|
89
|
+
licenses:
|
90
|
+
- MIT
|
91
|
+
metadata: {}
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - "~>"
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '2.5'
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubygems_version: 3.1.2
|
108
|
+
signing_key:
|
109
|
+
specification_version: 4
|
110
|
+
summary: Jura Guarrr!
|
111
|
+
test_files: []
|