codeclimate-services 0.3.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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.ruby-version +1 -0
- data/.travis.yml +13 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +121 -0
- data/Rakefile +11 -0
- data/bin/bundler +16 -0
- data/bin/coderay +16 -0
- data/bin/nokogiri +16 -0
- data/bin/pry +16 -0
- data/bin/rake +16 -0
- data/codeclimate-services.gemspec +29 -0
- data/config/cacert.pem +4026 -0
- data/config/load.rb +4 -0
- data/lib/axiom/types/password.rb +7 -0
- data/lib/cc/formatters/linked_formatter.rb +60 -0
- data/lib/cc/formatters/plain_formatter.rb +36 -0
- data/lib/cc/formatters/snapshot_formatter.rb +101 -0
- data/lib/cc/formatters/ticket_formatter.rb +27 -0
- data/lib/cc/helpers/coverage_helper.rb +25 -0
- data/lib/cc/helpers/issue_helper.rb +9 -0
- data/lib/cc/helpers/quality_helper.rb +44 -0
- data/lib/cc/helpers/vulnerability_helper.rb +31 -0
- data/lib/cc/presenters/github_pull_requests_presenter.rb +54 -0
- data/lib/cc/service/config.rb +4 -0
- data/lib/cc/service/formatter.rb +34 -0
- data/lib/cc/service/helper.rb +54 -0
- data/lib/cc/service/http.rb +87 -0
- data/lib/cc/service/invocation/invocation_chain.rb +15 -0
- data/lib/cc/service/invocation/with_error_handling.rb +45 -0
- data/lib/cc/service/invocation/with_metrics.rb +37 -0
- data/lib/cc/service/invocation/with_retries.rb +17 -0
- data/lib/cc/service/invocation/with_return_values.rb +18 -0
- data/lib/cc/service/invocation.rb +57 -0
- data/lib/cc/service/response_check.rb +42 -0
- data/lib/cc/service.rb +127 -0
- data/lib/cc/services/asana.rb +90 -0
- data/lib/cc/services/campfire.rb +55 -0
- data/lib/cc/services/flowdock.rb +61 -0
- data/lib/cc/services/github_issues.rb +80 -0
- data/lib/cc/services/github_pull_requests.rb +210 -0
- data/lib/cc/services/hipchat.rb +57 -0
- data/lib/cc/services/jira.rb +93 -0
- data/lib/cc/services/lighthouse.rb +79 -0
- data/lib/cc/services/pivotal_tracker.rb +78 -0
- data/lib/cc/services/slack.rb +124 -0
- data/lib/cc/services/version.rb +5 -0
- data/lib/cc/services.rb +9 -0
- data/pull_request_test.rb +47 -0
- data/service_test.rb +86 -0
- data/test/asana_test.rb +85 -0
- data/test/axiom/types/password_test.rb +22 -0
- data/test/campfire_test.rb +144 -0
- data/test/fixtures.rb +68 -0
- data/test/flowdock_test.rb +148 -0
- data/test/formatters/snapshot_formatter_test.rb +47 -0
- data/test/github_issues_test.rb +96 -0
- data/test/github_pull_requests_test.rb +293 -0
- data/test/helper.rb +50 -0
- data/test/hipchat_test.rb +130 -0
- data/test/invocation_error_handling_test.rb +51 -0
- data/test/invocation_return_values_test.rb +21 -0
- data/test/invocation_test.rb +167 -0
- data/test/jira_test.rb +80 -0
- data/test/lighthouse_test.rb +74 -0
- data/test/pivotal_tracker_test.rb +73 -0
- data/test/presenters/github_pull_requests_presenter_test.rb +49 -0
- data/test/service_test.rb +63 -0
- data/test/slack_test.rb +222 -0
- data/test/support/fake_logger.rb +11 -0
- data/test/with_metrics_test.rb +19 -0
- metadata +263 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
class CC::Service::Invocation
|
2
|
+
class WithRetries
|
3
|
+
def initialize(invocation, retries)
|
4
|
+
@invocation = invocation
|
5
|
+
@retries = retries
|
6
|
+
end
|
7
|
+
|
8
|
+
def call
|
9
|
+
@invocation.call
|
10
|
+
rescue => ex
|
11
|
+
raise ex if @retries.zero?
|
12
|
+
|
13
|
+
@retries -= 1
|
14
|
+
retry
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CC::Service::Invocation
|
2
|
+
class WithReturnValues
|
3
|
+
def initialize(invocation, message = nil)
|
4
|
+
@invocation = invocation
|
5
|
+
@message = message || "An internal error happened"
|
6
|
+
end
|
7
|
+
|
8
|
+
def call
|
9
|
+
result = @invocation.call
|
10
|
+
if result.nil?
|
11
|
+
{ ok: false, message: @message }
|
12
|
+
else
|
13
|
+
result
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'cc/service/invocation/invocation_chain'
|
2
|
+
require 'cc/service/invocation/with_retries'
|
3
|
+
require 'cc/service/invocation/with_metrics'
|
4
|
+
require 'cc/service/invocation/with_error_handling'
|
5
|
+
require 'cc/service/invocation/with_return_values'
|
6
|
+
|
7
|
+
class CC::Service::Invocation
|
8
|
+
MIDDLEWARE = {
|
9
|
+
retries: WithRetries,
|
10
|
+
metrics: WithMetrics,
|
11
|
+
error_handling: WithErrorHandling,
|
12
|
+
return_values: WithReturnValues,
|
13
|
+
}
|
14
|
+
|
15
|
+
attr_reader :result
|
16
|
+
|
17
|
+
# Build a chain of invocation wrappers which eventually calls receive
|
18
|
+
# on the given service, then execute that chain.
|
19
|
+
#
|
20
|
+
# Order is important. Each call to #with, wraps the last.
|
21
|
+
#
|
22
|
+
# Usage:
|
23
|
+
#
|
24
|
+
# CC::Service::Invocation.invoke(service) do |i|
|
25
|
+
# i.with :retries, 3
|
26
|
+
# i.with :metrics, $statsd
|
27
|
+
# i.with :error_handling, Rails.logger
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# In the above example, service.receive could happen 4 times (once,
|
31
|
+
# then three retries) before an exception is re-raised up to the
|
32
|
+
# metrics collector, then up again to the error handling. If the order
|
33
|
+
# were reversed, the error handling middleware would prevent the other
|
34
|
+
# middleware from seeing any exceptions at all.
|
35
|
+
def self.invoke(service, &block)
|
36
|
+
instance = new(service, &block)
|
37
|
+
instance.result
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize(service)
|
41
|
+
@chain = InvocationChain.new { service.receive }
|
42
|
+
|
43
|
+
yield(self) if block_given?
|
44
|
+
|
45
|
+
@result = @chain.call
|
46
|
+
end
|
47
|
+
|
48
|
+
def with(middleware, *args)
|
49
|
+
if klass = MIDDLEWARE[middleware]
|
50
|
+
wrap(klass, *args)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def wrap(klass, *args)
|
55
|
+
@chain.wrap(klass, *args)
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class CC::Service
|
2
|
+
class HTTPError < StandardError
|
3
|
+
attr_reader :response_body, :status, :params, :endpoint_url
|
4
|
+
attr_accessor :user_message
|
5
|
+
|
6
|
+
def initialize(message, env)
|
7
|
+
@response_body = env[:body]
|
8
|
+
@status = env[:status]
|
9
|
+
@params = env[:params]
|
10
|
+
@endpoint_url = env[:url].to_s
|
11
|
+
|
12
|
+
super(message)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class ResponseCheck < Faraday::Response::Middleware
|
17
|
+
ErrorStatuses = 400...600
|
18
|
+
|
19
|
+
def on_complete(env)
|
20
|
+
if ErrorStatuses === env[:status]
|
21
|
+
message = error_message(env) ||
|
22
|
+
"API request unsuccessful (#{env[:status]})"
|
23
|
+
|
24
|
+
raise HTTPError.new(message, env)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def error_message(env)
|
31
|
+
# We only handle Jira (or responses which look like Jira's). We will add
|
32
|
+
# more logic here over time to account for other service's typical error
|
33
|
+
# responses as we see them.
|
34
|
+
if env[:response_headers]["content-type"] =~ /application\/json/
|
35
|
+
errors = JSON.parse(env[:body])["errors"]
|
36
|
+
errors.is_a?(Hash) && errors.values.map(&:capitalize).join(", ")
|
37
|
+
end
|
38
|
+
rescue JSON::ParserError
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
data/lib/cc/service.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
module CC
|
2
|
+
class Service
|
3
|
+
require "cc/service/config"
|
4
|
+
require "cc/service/http"
|
5
|
+
require "cc/service/helper"
|
6
|
+
require "cc/service/formatter"
|
7
|
+
require "cc/service/invocation"
|
8
|
+
require "axiom/types/password"
|
9
|
+
|
10
|
+
dir = File.expand_path '../helpers', __FILE__
|
11
|
+
Dir["#{dir}/*_helper.rb"].sort.each do |helper|
|
12
|
+
require helper
|
13
|
+
end
|
14
|
+
|
15
|
+
dir = File.expand_path '../formatters', __FILE__
|
16
|
+
Dir["#{dir}/*_formatter.rb"].sort.each do |formatter|
|
17
|
+
require formatter
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.load_services
|
21
|
+
path = File.expand_path("../services/**/*.rb", __FILE__)
|
22
|
+
Dir[path].sort.each { |lib| require(lib) }
|
23
|
+
end
|
24
|
+
|
25
|
+
Error = Class.new(StandardError)
|
26
|
+
ConfigurationError = Class.new(Error)
|
27
|
+
|
28
|
+
include HTTP
|
29
|
+
include Helper
|
30
|
+
|
31
|
+
attr_reader :event, :config, :payload
|
32
|
+
|
33
|
+
ALL_EVENTS = %w[test unit coverage quality vulnerability snapshot pull_request issue]
|
34
|
+
|
35
|
+
# Tracks the defined services.
|
36
|
+
def self.services
|
37
|
+
@services ||= []
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.inherited(svc)
|
41
|
+
Service.services << svc
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.by_slug(slug)
|
46
|
+
services.detect { |s| s.slug == slug }
|
47
|
+
end
|
48
|
+
|
49
|
+
class << self
|
50
|
+
attr_writer :title
|
51
|
+
attr_accessor :description
|
52
|
+
attr_accessor :issue_tracker
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.title
|
56
|
+
@title ||= begin
|
57
|
+
hook = name.dup
|
58
|
+
hook.sub! /.*:/, ''
|
59
|
+
hook
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.slug
|
64
|
+
@slug ||= begin
|
65
|
+
hook = name.dup
|
66
|
+
hook.downcase!
|
67
|
+
hook.sub! /.*:/, ''
|
68
|
+
hook
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def initialize(config, payload)
|
73
|
+
@payload = payload.stringify_keys
|
74
|
+
@config = create_config(config)
|
75
|
+
@event = @payload["name"].to_s
|
76
|
+
|
77
|
+
load_helper
|
78
|
+
validate_event
|
79
|
+
end
|
80
|
+
|
81
|
+
def receive
|
82
|
+
methods = [:receive_event, :"receive_#{event}"]
|
83
|
+
|
84
|
+
methods.each do |method|
|
85
|
+
if respond_to?(method)
|
86
|
+
return public_send(method)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
{ ok: false, ignored: true, message: "No service handler found" }
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def load_helper
|
96
|
+
helper_name = "#{event.classify}Helper"
|
97
|
+
|
98
|
+
if Service.const_defined?(helper_name)
|
99
|
+
@helper = Service.const_get(helper_name)
|
100
|
+
extend @helper
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def validate_event
|
105
|
+
unless ALL_EVENTS.include?(event)
|
106
|
+
raise ArgumentError.new("Invalid event: #{event}")
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def create_config(config)
|
111
|
+
config_class.new(config).tap do |c|
|
112
|
+
unless c.valid?
|
113
|
+
raise ConfigurationError, "Invalid config: #{config.inspect}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def config_class
|
119
|
+
if defined?("#{self.class.name}::Config")
|
120
|
+
"#{self.class.name}::Config".constantize
|
121
|
+
else
|
122
|
+
Config
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
class CC::Service::Asana < CC::Service
|
2
|
+
class Config < CC::Service::Config
|
3
|
+
attribute :api_key, String, label: "API key"
|
4
|
+
|
5
|
+
attribute :workspace_id, String, label: "Workspace ID"
|
6
|
+
|
7
|
+
attribute :project_id, String, label: "Project ID",
|
8
|
+
description: "(optional)"
|
9
|
+
|
10
|
+
attribute :assignee, String, label: "Assignee",
|
11
|
+
description: "Assignee email address (optional)"
|
12
|
+
|
13
|
+
validates :api_key, presence: true
|
14
|
+
validates :workspace_id, presence: true
|
15
|
+
end
|
16
|
+
|
17
|
+
ENDPOINT = "https://app.asana.com/api/1.0/tasks"
|
18
|
+
|
19
|
+
self.title = "Asana"
|
20
|
+
self.description = "Create tasks in Asana"
|
21
|
+
self.issue_tracker = true
|
22
|
+
|
23
|
+
def receive_test
|
24
|
+
result = create_task("Test task from Code Climate")
|
25
|
+
result.merge(
|
26
|
+
message: "Ticket <a href='#{result[:url]}'>#{result[:id]}</a> created."
|
27
|
+
)
|
28
|
+
rescue CC::Service::HTTPError => ex
|
29
|
+
body = JSON.parse(ex.response_body)
|
30
|
+
ex.user_message = body["errors"].map{|e| e["message"] }.join(" ")
|
31
|
+
raise ex
|
32
|
+
end
|
33
|
+
|
34
|
+
def receive_issue
|
35
|
+
title = %{Fix "#{issue["check_name"]}" issue in #{constant_name}}
|
36
|
+
|
37
|
+
body = [issue["description"], details_url].join("\n\n")
|
38
|
+
|
39
|
+
create_task(title, body)
|
40
|
+
end
|
41
|
+
|
42
|
+
def receive_quality
|
43
|
+
title = "Refactor #{constant_name} from #{rating} on Code Climate"
|
44
|
+
|
45
|
+
create_task("#{title} - #{details_url}")
|
46
|
+
end
|
47
|
+
|
48
|
+
def receive_vulnerability
|
49
|
+
formatter = CC::Formatters::TicketFormatter.new(self)
|
50
|
+
title = formatter.format_vulnerability_title
|
51
|
+
|
52
|
+
create_task("#{title} - #{details_url}")
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def create_task(name, notes = nil)
|
58
|
+
params = generate_params(name, notes)
|
59
|
+
authenticate_http
|
60
|
+
http.headers["Content-Type"] = "application/json"
|
61
|
+
service_post(ENDPOINT, params.to_json) do |response|
|
62
|
+
body = JSON.parse(response.body)
|
63
|
+
id = body['data']['id']
|
64
|
+
url = "https://app.asana.com/0/#{config.workspace_id}/#{id}"
|
65
|
+
{ id: id, url: url }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def generate_params(name, notes = nil)
|
70
|
+
params = {
|
71
|
+
data: { workspace: config.workspace_id, name: name, notes: notes }
|
72
|
+
}
|
73
|
+
|
74
|
+
if config.project_id.present?
|
75
|
+
# Note this is undocumented, found via trial & error
|
76
|
+
params[:data][:projects] = [config.project_id]
|
77
|
+
end
|
78
|
+
|
79
|
+
if config.assignee.present?
|
80
|
+
params[:data][:assignee] = config.assignee
|
81
|
+
end
|
82
|
+
|
83
|
+
params
|
84
|
+
end
|
85
|
+
|
86
|
+
def authenticate_http
|
87
|
+
http.basic_auth(config.api_key, "")
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class CC::Service::Campfire < CC::Service
|
2
|
+
class Config < CC::Service::Config
|
3
|
+
attribute :subdomain, String,
|
4
|
+
description: "The Campfire subdomain for the account"
|
5
|
+
attribute :token, String,
|
6
|
+
description: "Your Campfire API auth token"
|
7
|
+
attribute :room_id, String,
|
8
|
+
description: "Check your campfire URL for a room ID. Usually 6 digits."
|
9
|
+
|
10
|
+
validates :subdomain, presence: true
|
11
|
+
validates :room_id, presence: true
|
12
|
+
validates :token, presence: true
|
13
|
+
end
|
14
|
+
|
15
|
+
self.description = "Send messages to a Campfire chat room"
|
16
|
+
|
17
|
+
def receive_test
|
18
|
+
speak(formatter.format_test).merge(
|
19
|
+
message: "Test message sent"
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def receive_coverage
|
24
|
+
speak(formatter.format_coverage)
|
25
|
+
end
|
26
|
+
|
27
|
+
def receive_quality
|
28
|
+
speak(formatter.format_quality)
|
29
|
+
end
|
30
|
+
|
31
|
+
def receive_vulnerability
|
32
|
+
speak(formatter.format_vulnerability)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def formatter
|
38
|
+
CC::Formatters::PlainFormatter.new(self)
|
39
|
+
end
|
40
|
+
|
41
|
+
def speak(line)
|
42
|
+
http.headers['Content-Type'] = 'application/json'
|
43
|
+
params = { message: { body: line } }
|
44
|
+
|
45
|
+
http.basic_auth(config.token, "X")
|
46
|
+
service_post(speak_uri, params.to_json)
|
47
|
+
end
|
48
|
+
|
49
|
+
def speak_uri
|
50
|
+
subdomain = config.subdomain
|
51
|
+
room_id = config.room_id
|
52
|
+
"https://#{subdomain}.campfirenow.com/room/#{room_id}/speak.json"
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class CC::Service::Flowdock < CC::Service
|
2
|
+
class Config < CC::Service::Config
|
3
|
+
attribute :api_token, String,
|
4
|
+
label: "API Token",
|
5
|
+
description: "The API token of the Flow to send notifications to",
|
6
|
+
link: "https://www.flowdock.com/account/tokens"
|
7
|
+
validates :api_token, presence: true
|
8
|
+
end
|
9
|
+
|
10
|
+
BASE_URL = "https://api.flowdock.com/v1"
|
11
|
+
INVALID_PROJECT_CHARACTERS = /[^0-9a-z\-_ ]+/i
|
12
|
+
|
13
|
+
self.description = "Send messages to a Flowdock inbox"
|
14
|
+
|
15
|
+
def receive_test
|
16
|
+
notify("Test", repo_name, formatter.format_test).merge(
|
17
|
+
message: "Test message sent"
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def receive_coverage
|
22
|
+
notify("Coverage", repo_name, formatter.format_coverage)
|
23
|
+
end
|
24
|
+
|
25
|
+
def receive_quality
|
26
|
+
notify("Quality", repo_name, formatter.format_quality)
|
27
|
+
end
|
28
|
+
|
29
|
+
def receive_vulnerability
|
30
|
+
notify("Vulnerability", repo_name, formatter.format_vulnerability)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def formatter
|
36
|
+
CC::Formatters::LinkedFormatter.new(
|
37
|
+
self,
|
38
|
+
prefix: "",
|
39
|
+
prefix_with_repo: false,
|
40
|
+
link_style: :html
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def notify(subject, project, content)
|
45
|
+
params = {
|
46
|
+
source: "Code Climate",
|
47
|
+
from_address: "hello@codeclimate.com",
|
48
|
+
from_name: "Code Climate",
|
49
|
+
format: "html",
|
50
|
+
subject: subject,
|
51
|
+
project: project.gsub(INVALID_PROJECT_CHARACTERS, ''),
|
52
|
+
content: content,
|
53
|
+
link: "https://codeclimate.com"
|
54
|
+
}
|
55
|
+
|
56
|
+
url = "#{BASE_URL}/messages/team_inbox/#{config.api_token}"
|
57
|
+
http.headers["User-Agent"] = "Code Climate"
|
58
|
+
|
59
|
+
service_post(url, params)
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
class CC::Service::GitHubIssues < CC::Service
|
2
|
+
class Config < CC::Service::Config
|
3
|
+
attribute :oauth_token, String,
|
4
|
+
label: "OAuth Token",
|
5
|
+
description: "A personal OAuth token with permissions for the repo"
|
6
|
+
attribute :project, String,
|
7
|
+
label: "Project",
|
8
|
+
description: "Project name on GitHub (e.g 'thoughtbot/paperclip')"
|
9
|
+
attribute :labels, String,
|
10
|
+
label: "Labels (comma separated)",
|
11
|
+
description: "Comma separated list of labels to apply to the issue"
|
12
|
+
|
13
|
+
validates :oauth_token, presence: true
|
14
|
+
end
|
15
|
+
|
16
|
+
self.title = "GitHub Issues"
|
17
|
+
self.description = "Open issues on GitHub"
|
18
|
+
self.issue_tracker = true
|
19
|
+
|
20
|
+
BASE_URL = "https://api.github.com"
|
21
|
+
|
22
|
+
def receive_test
|
23
|
+
result = create_issue("Test ticket from Code Climate", "")
|
24
|
+
result.merge(
|
25
|
+
message: "Issue <a href='#{result[:url]}'>##{result[:number]}</a> created."
|
26
|
+
)
|
27
|
+
rescue CC::Service::HTTPError => e
|
28
|
+
body = JSON.parse(e.response_body)
|
29
|
+
e.user_message = body["message"]
|
30
|
+
raise e
|
31
|
+
end
|
32
|
+
|
33
|
+
def receive_quality
|
34
|
+
title = "Refactor #{constant_name} from #{rating} on Code Climate"
|
35
|
+
|
36
|
+
create_issue(title, details_url)
|
37
|
+
end
|
38
|
+
|
39
|
+
def receive_vulnerability
|
40
|
+
formatter = CC::Formatters::TicketFormatter.new(self)
|
41
|
+
|
42
|
+
create_issue(
|
43
|
+
formatter.format_vulnerability_title,
|
44
|
+
formatter.format_vulnerability_body
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def receive_issue
|
49
|
+
title = %{Fix "#{issue["check_name"]}" issue in #{constant_name}}
|
50
|
+
|
51
|
+
body = [issue["description"], details_url].join("\n\n")
|
52
|
+
|
53
|
+
create_issue(title, body)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def create_issue(title, issue_body)
|
59
|
+
params = { title: title, body: issue_body }
|
60
|
+
|
61
|
+
if config.labels.present?
|
62
|
+
params[:labels] = config.labels.split(",").map(&:strip).reject(&:blank?).compact
|
63
|
+
end
|
64
|
+
|
65
|
+
http.headers["Content-Type"] = "application/json"
|
66
|
+
http.headers["Authorization"] = "token #{config.oauth_token}"
|
67
|
+
http.headers["User-Agent"] = "Code Climate"
|
68
|
+
|
69
|
+
url = "#{BASE_URL}/repos/#{config.project}/issues"
|
70
|
+
service_post(url, params.to_json) do |response|
|
71
|
+
body = JSON.parse(response.body)
|
72
|
+
{
|
73
|
+
id: body["id"],
|
74
|
+
number: body["number"],
|
75
|
+
url: body["html_url"]
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|