Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Speicher <rspeicher@gmail.com>2015-04-15 23:01:00 +0300
committerRobert Speicher <rspeicher@gmail.com>2015-04-25 21:40:12 +0300
commit8f8a8ab32bca8fdc79d7a5115eabbd015dd44c02 (patch)
treea241bf1002d3ec0f92122f9ec22d14b6879441a1
parenta6defd157641b102c9ea4cabe99f0a386d661d80 (diff)
Refactor ReferenceExtractor to use pipeline filters
-rw-r--r--lib/gitlab/reference_extractor.rb155
-rw-r--r--spec/lib/gitlab/reference_extractor_spec.rb74
2 files changed, 36 insertions, 193 deletions
diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index 949dd5d26b1..1f8558f5ad0 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -8,151 +8,68 @@ module Gitlab
@current_user = current_user
end
- def can?(user, action, subject)
- Ability.abilities.allowed?(user, action, subject)
- end
-
def analyze(text)
- text = text.dup
-
- # Remove preformatted/code blocks so that references are not included
- text.gsub!(/^```.*?^```/m, '')
- text.gsub!(/[^`]`[^`]*?`[^`]/, '')
-
- @references = Hash.new { |hash, type| hash[type] = [] }
- parse_references(text)
+ @_text = text.dup
end
- # Given a valid project, resolve the extracted identifiers of the requested type to
- # model objects.
-
def users
- references[:user].uniq.map do |project, identifier|
- if identifier == "all"
- project.team.members.flatten
- elsif namespace = Namespace.find_by(path: identifier)
- if namespace.is_a?(Group)
- namespace.users if can?(current_user, :read_group, namespace)
- else
- namespace.owner
- end
- end
- end.flatten.compact.uniq
+ result = pipeline_result(:user)
+ result[:references][:user].flatten.compact.uniq
end
def labels
- references[:label].uniq.map do |project, identifier|
- project.labels.where(id: identifier).first
- end.compact.uniq
+ result = pipeline_result(:label)
+ result[:references][:label].compact.uniq
end
def issues
- references[:issue].uniq.map do |project, identifier|
- if project.default_issues_tracker?
- project.issues.where(iid: identifier).first
- end
- end.compact.uniq
+ # TODO (rspeicher): What about external issues?
+
+ result = pipeline_result(:issue)
+ result[:references][:issue].compact.uniq
end
def merge_requests
- references[:merge_request].uniq.map do |project, identifier|
- project.merge_requests.where(iid: identifier).first
- end.compact.uniq
+ result = pipeline_result(:merge_request)
+ result[:references][:merge_request].compact.uniq
end
def snippets
- references[:snippet].uniq.map do |project, identifier|
- project.snippets.where(id: identifier).first
- end.compact.uniq
+ result = pipeline_result(:snippet)
+ result[:references][:snippet].compact.uniq
end
def commits
- references[:commit].uniq.map do |project, identifier|
- repo = project.repository
- repo.commit(identifier) if repo
- end.compact.uniq
+ result = pipeline_result(:commit)
+ result[:references][:commit].compact.uniq
end
def commit_ranges
- references[:commit_range].uniq.map do |project, identifier|
- repo = project.repository
- if repo
- from_id, to_id = identifier.split(/\.{2,3}/, 2)
- [repo.commit(from_id), repo.commit(to_id)]
- end
- end.compact.uniq
+ result = pipeline_result(:commit_range)
+ result[:references][:commit_range].compact.uniq
end
private
- NAME_STR = Gitlab::Regex::NAMESPACE_REGEX_STR
- PROJ_STR = "(?<project>#{NAME_STR}/#{NAME_STR})"
-
- REFERENCE_PATTERN = %r{
- (?<prefix>\W)? # Prefix
- ( # Reference
- @(?<user>#{NAME_STR}) # User name
- |~(?<label>\d+) # Label ID
- |(?<issue>([A-Z\-]+-)\d+) # JIRA Issue ID
- |#{PROJ_STR}?\#(?<issue>([a-zA-Z\-]+-)?\d+) # Issue ID
- |#{PROJ_STR}?!(?<merge_request>\d+) # MR ID
- |\$(?<snippet>\d+) # Snippet ID
- |(#{PROJ_STR}@)?(?<commit_range>[\h]{6,40}\.{2,3}[\h]{6,40}) # Commit range
- |(#{PROJ_STR}@)?(?<commit>[\h]{6,40}) # Commit ID
- )
- (?<suffix>\W)? # Suffix
- }x.freeze
-
- TYPES = %i(user issue label merge_request snippet commit commit_range).freeze
-
- def parse_references(text, project = @project)
- # parse reference links
- text.gsub!(REFERENCE_PATTERN) do |match|
- type = TYPES.detect { |t| $~[t].present? }
-
- actual_project = project
- project_prefix = nil
- project_path = $LAST_MATCH_INFO[:project]
- if project_path
- actual_project = ::Project.find_with_namespace(project_path)
- actual_project = nil unless can?(current_user, :read_project, actual_project)
- project_prefix = project_path
- end
-
- parse_result($LAST_MATCH_INFO, type,
- actual_project, project_prefix) || match
- end
- end
-
- # Called from #parse_references. Attempts to build a gitlab reference
- # link. Returns nil if +type+ is nil, if the match string is an HTML
- # entity, if the reference is invalid, or if the matched text includes an
- # invalid project path.
- def parse_result(match_info, type, project, project_prefix)
- prefix = match_info[:prefix]
- suffix = match_info[:suffix]
-
- return nil if html_entity?(prefix, suffix) || type.nil?
- return nil if project.nil? && !project_prefix.nil?
-
- identifier = match_info[type]
- ref_link = reference_link(type, identifier, project, project_prefix)
-
- if ref_link
- "#{prefix}#{ref_link}#{suffix}"
- else
- nil
- end
- end
-
- # Return true if the +prefix+ and +suffix+ indicate that the matched string
- # is an HTML entity like &amp;
- def html_entity?(prefix, suffix)
- prefix && suffix && prefix[0] == '&' && suffix[-1] == ';'
- end
-
- def reference_link(type, identifier, project, _)
- references[type] << [project, identifier]
+ # Instantiate and call HTML::Pipeline with a single reference filter type,
+ # returning the result
+ #
+ # filter_type - Symbol reference type (e.g., :commit, :issue, etc.)
+ #
+ # Returns the results Hash
+ def pipeline_result(filter_type)
+ klass = filter_type.to_s.camelize + 'ReferenceFilter'
+ filter = "Gitlab::Markdown::#{klass}".constantize
+
+ context = {
+ project: project,
+ current_user: current_user,
+ # We don't actually care about the links generated
+ only_path: true
+ }
+
+ pipeline = HTML::Pipeline.new([filter], context)
+ pipeline.call(@_text)
end
end
end
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index 0f4ea2a24ed..e339f378d54 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -4,80 +4,6 @@ describe Gitlab::ReferenceExtractor do
let(:project) { create(:project) }
subject { Gitlab::ReferenceExtractor.new(project, project.creator) }
- it 'extracts username references' do
- subject.analyze('this contains a @user reference')
- expect(subject.references[:user]).to eq([[project, 'user']])
- end
-
- it 'extracts issue references' do
- subject.analyze('this one talks about issue #1234')
- expect(subject.references[:issue]).to eq([[project, '1234']])
- end
-
- it 'extracts JIRA issue references' do
- subject.analyze('this one talks about issue JIRA-1234')
- expect(subject.references[:issue]).to eq([[project, 'JIRA-1234']])
- end
-
- it 'extracts merge request references' do
- subject.analyze("and here's !43, a merge request")
- expect(subject.references[:merge_request]).to eq([[project, '43']])
- end
-
- it 'extracts snippet ids' do
- subject.analyze('snippets like $12 get extracted as well')
- expect(subject.references[:snippet]).to eq([[project, '12']])
- end
-
- it 'extracts commit shas' do
- subject.analyze('commit shas 98cf0ae3 are pulled out as Strings')
- expect(subject.references[:commit]).to eq([[project, '98cf0ae3']])
- end
-
- it 'extracts commit ranges' do
- subject.analyze('here you go, a commit range: 98cf0ae3...98cf0ae4')
- expect(subject.references[:commit_range]).to eq([[project, '98cf0ae3...98cf0ae4']])
- end
-
- it 'extracts multiple references and preserves their order' do
- subject.analyze('@me and @you both care about this')
- expect(subject.references[:user]).to eq([
- [project, 'me'],
- [project, 'you']
- ])
- end
-
- it 'leaves the original note unmodified' do
- text = 'issue #123 is just the worst, @user'
- subject.analyze(text)
- expect(text).to eq('issue #123 is just the worst, @user')
- end
-
- it 'extracts no references for <pre>..</pre> blocks' do
- subject.analyze("<pre>def puts '#1 issue'\nend\n</pre>```")
- expect(subject.issues).to be_blank
- end
-
- it 'extracts no references for <code>..</code> blocks' do
- subject.analyze("<code>def puts '!1 request'\nend\n</code>```")
- expect(subject.merge_requests).to be_blank
- end
-
- it 'extracts no references for code blocks with language' do
- subject.analyze("this code:\n```ruby\ndef puts '#1 issue'\nend\n```")
- expect(subject.issues).to be_blank
- end
-
- it 'extracts issue references for invalid code blocks' do
- subject.analyze('test: ```this one talks about issue #1234```')
- expect(subject.references[:issue]).to eq([[project, '1234']])
- end
-
- it 'handles all possible kinds of references' do
- accessors = described_class::TYPES.map { |t| "#{t}s".to_sym }
- expect(subject).to respond_to(*accessors)
- end
-
it 'accesses valid user objects' do
@u_foo = create(:user, username: 'foo')
@u_bar = create(:user, username: 'bar')