Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions app/models/redmine/api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
class Redmine::API < Tracker::RemoteAPI

def self.generate_base_url(tracker_url)
tracker_url.gsub(/\/issues\/\d+/, '/')
end

def self.generate_issue_list_path(params = {})
"issues?" + params.to_param
end

def self.needs_html_to_extract?
true
end

def self.extract_info_from_url(url, html)
return nil unless html.match(/redmine/)

issue_match = url.match('/issues/(\d+)')
if issue_match
issue_number = issue_match[1]

doc = Nokogiri::HTML(html)
issue_title = doc.css('subject h3').text.strip

tracker_url = url.gsub(issue_match[0], '')

tracker_name = doc.css('head > title').text.gsub(/(.*? - )/, '')

{
issue_url: url,
issue_number: issue_number,
issue_title: issue_title,
issue_class: Redmine::Issue,
tracker_url: tracker_url,
tracker_name: tracker_name,
tracker_class: Redmine::Tracker
}
end
end

def self.parse_single_issue(response = "")
doc = Nokogiri::HTML(response)

number = doc.css('#content > h2').text.strip.gsub(/[^\d]/, '')
title = doc.css('head > title').text.strip
body = doc.css('.description > .wiki').text.strip
state = doc.css('.status.attribute .value').text.strip.downcase
author_name = doc.css('.issue .author .user.active').text.strip

owner = doc.css('.assigned-to .value').text.strip
owner = (owner.present? and owner != '-' ? owner : nil)
#binding.pry
remote_created_at = doc.css('.issue .author a[2]').first['title'].strip
remote_updated_at = doc.css('.issue .author a[3]').first['title'].strip
remote_type = nil
can_add_bounty = status_to_can_add_bounty(state)
priority = doc.css('.priority.attribute .value').text.strip.capitalize
severity = nil
votes_count = nil

comments = []
doc.css('#history .journal:not(.has-details)').each do |comment|
remote_id = comment.css('.journal-link').text.strip.gsub(/[^\d]/, '').to_i
body_html = comment.css('.wiki').text.strip
created_at = comment.css('h4 a[4]').first[:title].strip
comment_author_name = comment.css('.user.active').text.strip

comments << {
remote_id: remote_id,
body_html: body_html,
created_at: created_at,
author_name: comment_author_name
}
end

{
number: number,
title: title,
body: body,
state: state,
author_name: author_name,
owner: owner,
remote_created_at: remote_created_at,
remote_updated_at: remote_updated_at,
remote_type: remote_type,
can_add_bounty: can_add_bounty,
priority: priority,
severity: severity,
votes_count: votes_count,
comments: comments
}
end

def self.parse_issue_list(base_url, response = "")
results = []
doc = Nokogiri::HTML(response)
doc.css('.issue').each do |issue|
id = issue.css('.id a').first.text.strip
state = issue.css('.status').first.text.downcase
results << {
number: id.to_i,
title: issue.css('.subject a').first.text.strip,
state: state,
url: File.join(base_url, 'issues/' + id),
can_add_bounty: status_to_can_add_bounty(state),
severity: nil,
priority: issue.css('.priority').first.text.strip
}
end
results
end

private

def self.status_to_can_add_bounty(status)
['new', 'assigned', 'pending', 'need more information', 'needs design'].include?(status)
end
end
19 changes: 19 additions & 0 deletions app/models/redmine/issue.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Redmine::Issue < ::Issue
belongs_to :tracker, class_name: 'Redmine::Tracker', foreign_key: :tracker_id

def remote_sync_if_necessary(options={})
remote_sync(options) if synced_at.nil? || synced_at < 1.day.ago
end

def remote_sync(options={})
update_attributes!(synced_at: Time.now) unless new_record?
api_response = Redmine::API.fetch_issue(url: self.url)

ApplicationRecord.transaction do
comments_info = api_response.delete(:comments)
api_response.merge!(synced_at: Time.now)
update_attributes!(api_response)
sync_comments_from_array(comments_info)
end
end
end
26 changes: 26 additions & 0 deletions app/models/redmine/tracker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class Redmine::Tracker < ::Tracker
has_many :issues, class_name: "Redmine::Issue", foreign_key: :tracker_id

def remote_sync_if_necessary(options={})
if synced_at.nil?
remote_sync(options)
elsif synced_at < 1.hour.ago
delay.remote_sync(options)
end
end

def remote_sync(options={})
update_attributes synced_at: Time.now
params = {
sort: 'priority:desc,id:desc',
v: { status_id: [1, 2, 8, 9, 11] },
per_page: 100
}
api_options = {
url: Redmine::API.generate_base_url(self.url),
path: Redmine::API.generate_issue_list_path(params)
}
api_response = Redmine::API.fetch_issue_list(api_options)
sync_issues_from_array(api_response)
end
end
2 changes: 2 additions & 0 deletions app/models/tracker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class Tracker < ApplicationRecord
Savannah::API
Mantis::API
Gitlab::API
Redmine::API
)

STATIC_SUBCLASSNAMES = %w(
Expand All @@ -87,6 +88,7 @@ class Tracker < ApplicationRecord
Savannah::Tracker
Mantis::Tracker
Gitlab::Tracker
Redmine::Tracker
)

class RemoteAPI
Expand Down
4 changes: 4 additions & 0 deletions spec/factories/issue.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
association :tracker, factory: :bugzilla_tracker
end

factory :redmine_issue, class: Redmine::Issue do
association :tracker, factory: :redmine_tracker
end

factory :gitlab, class: Gitlab::Issue do
association :tracker, factory: :gitlab_tracker
end
Expand Down
5 changes: 5 additions & 0 deletions spec/factories/trackers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@
sequence(:url) { |n| "https://www.randomsite-#{n}.local/bugs/buglist.cgi?product=abc" }
end

factory :redmine_tracker, class: Redmine::Tracker do
sequence(:name) { |n| "Redmine repo #{n}" }
sequence(:url) { |n| "https://www.randomsite.local/issues" }
end

factory :sourceforge_tracker, class: SourceForge::Tracker do
sequence(:name) { |n| "SourceForge#{n}" }
sequence(:url) { |n| "http://sourceforge.net/projects/sourceforge#{n}/" }
Expand Down
24 changes: 24 additions & 0 deletions spec/models/redmine/issue_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require 'spec_helper'

describe Redmine::Issue do

describe '.remote_sync' do
let(:redmine_issue) { create(:redmine_issue) }
let(:data) do
{
number: 123,
title: 'title',
state: 'open',
priority: 'high',
comments: []
}
end
before do
expect(Redmine::API).to receive(:fetch_issue).and_return(data)
end
it "should call api and set issue attributes as api returned" do
expect(redmine_issue.remote_sync).to be_truthy
expect(redmine_issue.title).to eq('title')
end
end
end
26 changes: 26 additions & 0 deletions spec/models/redmine/tracker_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
require 'spec_helper'

describe Redmine::Tracker do

describe '.remote_sync' do
let(:tracker) { create(:redmine_tracker) }
let(:data) do
[{
number: 123,
title: 'title',
state: 'open',
priority: 'high',
url: "#{tracker.url}/issues/123"
}]
end
before do
expect(Redmine::API).to receive(:fetch_issue_list).and_return(data)
end
it "should call api and set issue attributes as api returned" do
tracker.remote_sync
expect(tracker.issues.count).to eq(1)
issue = tracker.issues.first
expect(issue.title).to eq('title')
end
end
end
12 changes: 12 additions & 0 deletions spec/trackers/all_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,18 @@
expect(action).to change(Comment, :count).by_at_least(1)
end
end

describe "Redmine::Issue" do
let(:issue_url) { "https://projects.theforeman.org/issues/863" }

it "should create new issue" do
expect(action).to change(Issue, :count).by(1)
end

it "should fetch issue comment" do
expect(action).to change(Comment, :count).by_at_least(1)
end
end
end

describe "Tracker#remote_sync" do
Expand Down
Loading