speedflow-plugin-jira 0.1.0

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.
@@ -0,0 +1,119 @@
1
+ require 'tty-prompt'
2
+ require 'json'
3
+
4
+ module Speedflow
5
+ module Plugin
6
+ module Jira
7
+ # Plugin core
8
+ class PluginCore
9
+ # @return [Prompt] Plugin prompt.
10
+ attr_writer :prompt
11
+
12
+ # @return [Client] Plugin client.
13
+ attr_writer :client
14
+
15
+ # @return [Config] Plugin config.
16
+ attr_writer :config
17
+
18
+ # Initialize.
19
+ #
20
+ # arguments - Hash of arguments.
21
+ #
22
+ # Examples
23
+ #
24
+ # Plugin.new({})
25
+ # # => <Speedflow::Plugin::Jira::PluginCore>
26
+ #
27
+ # Returns nothing.
28
+ def initialize(arguments)
29
+ @arguments = arguments
30
+ end
31
+
32
+ # Public: Create issue.
33
+ #
34
+ # Returns Hash of issue.
35
+ def action_create_issue
36
+ title = config.by_required_input('title')
37
+
38
+ project_key = config.by_input('project')
39
+ project_key = prompt.project(client.projects) if project_key.empty?
40
+
41
+ issue_type_id = config.by_input('type')
42
+ if issue_type_id.empty?
43
+ issue_type_id = prompt.issue_type(client.issue_types)
44
+ end
45
+
46
+ client.create_issue(project_key, title, issue_type_id)
47
+ end
48
+
49
+ # Public: Search issue.
50
+ #
51
+ # Returns Hash of issue.
52
+ def action_search_issue
53
+ limit = config.by_input('limit', 20)
54
+ project = config.by_input('project')
55
+ project = prompt.project(client.projects) if project.empty?
56
+
57
+ issue_id = prompt.issue do |title|
58
+ client.search_issue(project, title).take(limit.to_i)
59
+ end
60
+
61
+ issue_id
62
+ end
63
+
64
+ # Public: Change issue assignee.
65
+ #
66
+ # Returns Hash of assignee.
67
+ def action_change_issue_assignee
68
+ issue_key = config.by_input('issue')
69
+ assignee_name = config.by_input('assignee')
70
+
71
+ issue_key = action_search_issue if issue_key.empty?
72
+
73
+ client.update_issue_assignee(issue_key, assignee_name)
74
+
75
+ prompt.ok "[JIRA] Issue assignee changed to: #{assignee_name}"
76
+ end
77
+
78
+ # Public: Change issue transition.
79
+ #
80
+ # Returns Hash of assignee.
81
+ def action_change_issue_transition
82
+ issue_key = config.by_input('issue')
83
+ transition_id = config.by_input('transition')
84
+
85
+ issue_key = action_search_issue if issue_key.empty?
86
+
87
+ if transition_id.empty?
88
+ transition_id = prompt.transition(client.issue_trans(issue_key))
89
+ end
90
+
91
+ client.update_issue_transition(issue_key, transition_id)
92
+
93
+ prompt.ok "[JIRA] Issue transition changed to: #{transition_id}"
94
+ end
95
+
96
+ # Public: Plugin configuration.
97
+ #
98
+ # Returns Configuration instance.
99
+ def config
100
+ @config ||= Configuration.new(@arguments)
101
+ end
102
+
103
+ # Public: Plugin client.
104
+ #
105
+ # Returns Client instance.
106
+ def client
107
+ @client ||= Client.new(@config, @prompt)
108
+ end
109
+
110
+ # Public: Plugin prompt.
111
+ #
112
+ # Returns Prompt instance.
113
+ def prompt
114
+ @prompt ||= Prompt.new
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,111 @@
1
+ require 'tty-prompt'
2
+ require 'json'
3
+
4
+ module Speedflow
5
+ module Plugin
6
+ module Jira
7
+ # Plugin prompt
8
+ class Prompt # < TTY::Prompt
9
+ # @return [TTY::Prompt] TTY::Prompt.
10
+ attr_writer :prompt
11
+
12
+ # Public: Prompt title.
13
+ #
14
+ # Returns String of title.
15
+ def title
16
+ ask('What is the title of issue?', required: true)
17
+ end
18
+
19
+ # Public: Prompt project.
20
+ #
21
+ # projects - List of projects.
22
+ #
23
+ # Returns String of project key.
24
+ def project(projects)
25
+ prompt.select('Choose the project:') do |menu|
26
+ projects.each do |project|
27
+ menu.choice "#{project.name} (#{project.key})", project.key
28
+ end
29
+ end
30
+ end
31
+
32
+ # Public: Prompt issue type.
33
+ #
34
+ # issue_types - List of issue types.
35
+ #
36
+ # Returns ID of issue type.
37
+ def issue_type(issue_types)
38
+ prompt.select('Choose the issue type:') do |menu|
39
+ issue_types.each do |issue_type|
40
+ menu.choice "#{issue_type.name} (#{issue_type.id})", issue_type.id
41
+ end
42
+ end
43
+ end
44
+
45
+ # Public: Prompt transition.
46
+ #
47
+ # projects - List of transitions.
48
+ #
49
+ # Returns String of status key.
50
+ def transition(transitions)
51
+ prompt.select('Choose the transition:') do |menu|
52
+ transitions.each do |transition|
53
+ menu.choice(
54
+ "#{transition.name} (#{transition.id})", transition.id)
55
+ end
56
+ end
57
+ end
58
+
59
+ # Public: Prompt issue.
60
+ #
61
+ # issues - Hash of issues from block.
62
+ #
63
+ # Returns String key of issue.
64
+ def issue(&issues)
65
+ sel_issue = prompt.select('Choose the issue:') do |menu|
66
+ menu.choice 'Retry search!', :retry
67
+ yield(title).each do |v|
68
+ menu.choice "#{v.summary} (#{v.key})", v.key
69
+ end
70
+ end
71
+
72
+ issue(&issues) if sel_issue == :retry
73
+ ok "[JIRA] Issue found: #{sel_issue}" unless sel_issue == :retry
74
+
75
+ sel_issue
76
+ end
77
+
78
+ # Public: Errors from JIRA exception.
79
+ #
80
+ # exception - JIRA::HTTPError.
81
+ #
82
+ # Returns nothing.
83
+ def errors(exception)
84
+ if exception.response.respond_to?('body')
85
+ response = ::JSON.parse(exception.response.body)
86
+ response['errors'].each { |k, v| prompt.warn "- #{k}: #{v}" }
87
+ response['errorMessages'].each { |v| prompt.warn "- #{v}" }
88
+ end
89
+ end
90
+
91
+ # Delegate
92
+ #
93
+ # method - Method.
94
+ # args - Arguments.
95
+ # block - Block.
96
+ #
97
+ # Returns wathever.
98
+ def method_missing(method, *args, &block)
99
+ prompt.send(method, *args, &block)
100
+ end
101
+
102
+ # Public: TTY prompt.
103
+ #
104
+ # Returns ::TTY::Prompt instance.
105
+ def prompt
106
+ @prompt ||= ::TTY::Prompt.new
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,7 @@
1
+ module Speedflow
2
+ module Plugin
3
+ module Jira
4
+ VERSION = '0.1.0'.freeze
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+
3
+ require 'simplecov'
4
+ require 'codeclimate-test-reporter'
5
+
6
+ CodeClimate::TestReporter.start if ENV['CODECLIMATE_REPO_TOKEN']
7
+
8
+ SimpleCov.start do
9
+ formatter SimpleCov::Formatter::MultiFormatter.new(
10
+ [SimpleCov::Formatter::HTMLFormatter, CodeClimate::TestReporter::Formatter])
11
+ end
12
+
13
+ require 'speedflow/plugin/jira'
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+
3
+ describe Speedflow::Plugin::Jira::Client do
4
+ let(:args) do
5
+ title = { 'value' => 'test' }
6
+ assignee = { 'default' => 'julien.breux' }
7
+ { 'title' => title, 'assignee' => assignee }
8
+ end
9
+ let(:config) do
10
+ ::Speedflow::Plugin::Jira::Configuration.new(args)
11
+ end
12
+ let(:prompt) do
13
+ prompt = ::Speedflow::Plugin::Jira::Prompt.new
14
+ prompt.prompt = double
15
+ prompt
16
+ end
17
+ let(:jira_client) { double }
18
+ let(:client) do
19
+ client = ::Speedflow::Plugin::Jira::Client.new(config, prompt)
20
+ client.jira_client = jira_client
21
+ client
22
+ end
23
+ let(:exception_response) do
24
+ double(body: '{"errors": {"k": "v"}, "errorMessages": ["v"]}')
25
+ end
26
+
27
+ it '.search_issue' do
28
+ allow(jira_client).to receive_message_chain('Issue.jql')
29
+
30
+ client.search_issue('test', 'test')
31
+ end
32
+
33
+ it '.create_issue' do
34
+ receive = receive_message_chain('Issue.build') do
35
+ double(save!: nil, fetch: nil)
36
+ end
37
+ allow(jira_client)
38
+ .to receive
39
+
40
+ client.create_issue('1', 'test', '1')
41
+ end
42
+
43
+ it '.update_issue_assignee' do
44
+ receive = receive_message_chain('Issue.find') do
45
+ double(save!: nil)
46
+ end
47
+ allow(jira_client)
48
+ .to receive
49
+
50
+ client.update_issue_assignee('1', 'julien.breux')
51
+ end
52
+
53
+ it '.update_issue_transition' do
54
+ receive = receive_message_chain('Issue.find') do
55
+ double(transitions: double(build: double(save!: nil)))
56
+ end
57
+ allow(jira_client)
58
+ .to receive
59
+
60
+ client.update_issue_transition('T-1', 1)
61
+ end
62
+
63
+ it '.project' do
64
+ allow(jira_client).to receive_message_chain('Project.find')
65
+
66
+ client.project('T')
67
+ end
68
+
69
+ it '.projects' do
70
+ allow(jira_client).to receive_message_chain('Project.all')
71
+
72
+ client.projects
73
+ end
74
+
75
+ it '.issue_types' do
76
+ allow(jira_client).to receive_message_chain('Issuetype.all')
77
+
78
+ client.issue_types
79
+ end
80
+
81
+ it '.issue_trans' do
82
+ receive = receive_message_chain('Issue.find') do
83
+ double(transitions: nil)
84
+ end
85
+ allow(jira_client).to receive
86
+
87
+ client.issue_trans('T-1')
88
+ end
89
+
90
+ it '.safe' do
91
+ allow(client.prompt)
92
+ .to receive(:error).with('Invalid URL')
93
+ e = expect do
94
+ client.safe do
95
+ raise ::URI::InvalidURIError.new(''), '...'
96
+ end
97
+ end
98
+ e.to raise_error(SystemExit)
99
+
100
+ allow(client.prompt)
101
+ .to receive(:error).with('Jira errors')
102
+ allow(client.prompt)
103
+ .to receive(:errors)
104
+ e = expect do
105
+ client.safe do
106
+ raise ::JIRA::HTTPError.new(exception_response), '...'
107
+ end
108
+ end
109
+ e.to raise_error(SystemExit)
110
+ end
111
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ describe Speedflow::Plugin::Jira::Configuration do
4
+ let(:config) do
5
+ title = { 'value' => 'hello' }
6
+ num = { 'value' => 3 }
7
+ jira_config = { 'username' => 'foo', 'password' => 'bar', 'site' => 'baz' }
8
+ config = { 'jira' => jira_config }
9
+ args = { 'num' => num, 'title' => title, '_config' => config }
10
+ Speedflow::Plugin::Jira::Configuration.new(args)
11
+ end
12
+
13
+ it '.by_config' do
14
+ expect(config.by_config('username', 'default'))
15
+ .to eq 'foo'
16
+ end
17
+
18
+ it '.by_input' do
19
+ expect(config.by_input('title', 'default'))
20
+ .to eq 'hello'
21
+
22
+ expect(config.by_input('num'))
23
+ .to eq '3'
24
+
25
+ expect(config.by_input('notitle', 'default'))
26
+ .to eq 'default'
27
+ end
28
+
29
+ it '.by_required_input' do
30
+ expect(config.by_required_input('title', 'default'))
31
+ .to eq 'hello'
32
+
33
+ expect { config.by_required_input('notitle') }
34
+ .to raise_error(Exception)
35
+
36
+ expect { config.by_required_input('nonotitle', 'default') }
37
+ .to raise_error(Exception)
38
+ end
39
+
40
+ it '.auth' do
41
+ returned_hash = {
42
+ auth_type: :basic,
43
+ context_path: '/',
44
+ password: 'bar',
45
+ read_timeout: 120,
46
+ site: 'baz',
47
+ username: 'foo'
48
+ }
49
+
50
+ expect(config.auth)
51
+ .to eq returned_hash
52
+ end
53
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Speedflow::Plugin::Jira::Formatter::IssueFormatter do
4
+ let(:mod) { Speedflow::Plugin::Jira::Formatter::IssueFormatter }
5
+
6
+ it '#to_create' do
7
+ returned_hash = {
8
+ 'fields' => {
9
+ 'project' => { 'key' => 'foo' },
10
+ 'summary' => 'bar',
11
+ 'issuetype' => { 'id' => 'baz' }
12
+ }
13
+ }
14
+ expect(mod.to_create('foo', 'bar', 'baz'))
15
+ .to eq returned_hash
16
+ end
17
+
18
+ it '#to_assignee' do
19
+ returned_hash = {
20
+ 'fields' => {
21
+ 'assignee' => {
22
+ 'name' => 'julien.breux'
23
+ }
24
+ }
25
+ }
26
+ expect(mod.to_assignee('julien.breux'))
27
+ .to eq returned_hash
28
+ end
29
+ end