jura 0.0.1

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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +2 -0
  4. data/bin/jura +14 -0
  5. data/lib/jura.rb +10 -0
  6. data/lib/jura/api.rb +6 -0
  7. data/lib/jura/api/board.rb +25 -0
  8. data/lib/jura/api/client.rb +22 -0
  9. data/lib/jura/api/issue.rb +42 -0
  10. data/lib/jura/api/sprint.rb +44 -0
  11. data/lib/jura/api/token.rb +33 -0
  12. data/lib/jura/application.rb +74 -0
  13. data/lib/jura/command.rb +50 -0
  14. data/lib/jura/command/board.rb +23 -0
  15. data/lib/jura/command/board/select.rb +25 -0
  16. data/lib/jura/command/exit.rb +12 -0
  17. data/lib/jura/command/help.rb +11 -0
  18. data/lib/jura/command/invalid.rb +11 -0
  19. data/lib/jura/command/issue.rb +28 -0
  20. data/lib/jura/command/issue/base.rb +18 -0
  21. data/lib/jura/command/issue/list.rb +14 -0
  22. data/lib/jura/command/issue/mine.rb +14 -0
  23. data/lib/jura/command/issue/select.rb +22 -0
  24. data/lib/jura/command/issue/show.rb +18 -0
  25. data/lib/jura/command/sprint.rb +22 -0
  26. data/lib/jura/command/sprint/base.rb +18 -0
  27. data/lib/jura/command/sprint/list.rb +14 -0
  28. data/lib/jura/command/sprint/show.rb +22 -0
  29. data/lib/jura/component.rb +5 -0
  30. data/lib/jura/component/board.rb +21 -0
  31. data/lib/jura/component/board/help.rb +17 -0
  32. data/lib/jura/component/help.rb +24 -0
  33. data/lib/jura/component/issue.rb +43 -0
  34. data/lib/jura/component/issue/help.rb +18 -0
  35. data/lib/jura/component/issue/show.rb +30 -0
  36. data/lib/jura/component/logo.rb +22 -0
  37. data/lib/jura/component/sprint.rb +22 -0
  38. data/lib/jura/component/sprint/help.rb +17 -0
  39. data/lib/jura/component/sprint/show.rb +44 -0
  40. data/lib/jura/configuration.rb +36 -0
  41. data/lib/jura/utils.rb +32 -0
  42. data/lib/jura/version.rb +3 -0
  43. 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
@@ -0,0 +1,2 @@
1
+ # jura
2
+ A simple Jira CLI app by Ruby
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
@@ -0,0 +1,10 @@
1
+ require "readline"
2
+ require "json"
3
+
4
+ require "jura/version"
5
+ require "jura/utils"
6
+ require "jura/configuration"
7
+ require "jura/api"
8
+ require "jura/component"
9
+ require "jura/command"
10
+ require "jura/application"
data/lib/jura/api.rb ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+ require 'jura/api/client'
3
+ require 'jura/api/board'
4
+ require 'jura/api/issue'
5
+ require 'jura/api/sprint'
6
+ require 'jura/api/token'
@@ -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
@@ -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,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jura
4
+ module Command
5
+ class Exit
6
+ def self.execute
7
+ puts "Guar :("
8
+ exit
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jura
4
+ module Command
5
+ class Help
6
+ def self.execute
7
+ puts Component::Help.render
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jura
4
+ module Command
5
+ class Invalid
6
+ def self.execute(message)
7
+ puts message
8
+ end
9
+ end
10
+ end
11
+ 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 List < Base
7
+ def self.execute!
8
+ issues = Api::Issue.all(board_id)
9
+ puts Component::Issue.render(issues)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ 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,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jura
4
+ module Command
5
+ module Sprint
6
+ class List < Base
7
+ def self.execute!
8
+ sprints = Api::Sprint.all(board_id)
9
+ puts Component::Sprint.render(sprints)
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 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,5 @@
1
+ require "jura/component/help"
2
+ require "jura/component/board"
3
+ require "jura/component/issue"
4
+ require "jura/component/sprint"
5
+ require "jura/component/logo"
@@ -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
@@ -0,0 +1,3 @@
1
+ module Jura
2
+ VERSION = "0.0.1".freeze
3
+ end
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: []