File: git_refs_finder.rb

package info (click to toggle)
gitlab 17.6.5-19
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 629,368 kB
  • sloc: ruby: 1,915,304; javascript: 557,307; sql: 60,639; xml: 6,509; sh: 4,567; makefile: 1,239; python: 406
file content (83 lines) | stat: -rw-r--r-- 2,060 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# frozen_string_literal: true

class GitRefsFinder
  include Gitlab::Utils::StrongMemoize

  attr_reader :next_cursor

  def initialize(repository, params = {})
    @repository = repository
    @params = params
    @next_cursor = nil
  end

  protected

  attr_reader :repository, :params

  def by_search(refs)
    return refs unless search

    matches = filter_refs(refs, search)
    return matches if regex_search?

    set_exact_match_as_first_result(matches, search)
  end

  def search
    @params[:search].to_s.presence
  end
  strong_memoize_attr :search

  def sort
    @params[:sort].to_s.presence || 'name'
  end

  def pagination_params
    { limit: per_page, page_token: page_token }
  end

  def per_page
    return if params[:per_page].blank?

    Gitlab::PaginationDelegate.new(
      per_page: params[:per_page].presence, page: nil, count: nil
    ).limit_value
  end

  def filter_refs(refs, term)
    regex_string = RE2::Regexp.escape(term.downcase)
    regex_string = unescape_regex_operators(regex_string) if regex_search?
    regex_string = Gitlab::UntrustedRegexp.new(regex_string)

    refs.select { |ref| regex_string.match?(ref.name.downcase) }
  end

  def set_exact_match_as_first_result(matches, term)
    exact_match_index = find_exact_match_index(matches, term)
    matches.insert(0, matches.delete_at(exact_match_index)) if exact_match_index
    matches
  end

  def find_exact_match_index(matches, term)
    matches.index { |ref| ref.name.casecmp(term) == 0 }
  end

  def regex_search?
    Regexp.union('^', '$', '*') === search
  end
  strong_memoize_attr :regex_search?

  def unescape_regex_operators(regex_string)
    regex_string.sub('\^', '^').gsub('\*', '.*?').sub('\$', '$')
  end

  def set_next_cursor(records)
    return if records.blank?

    # TODO: Gitaly should be responsible for a cursor generation
    # Follow-up for branches: https://gitlab.com/gitlab-org/gitlab/-/issues/431903
    # Follow-up for tags: https://gitlab.com/gitlab-org/gitlab/-/issues/431904
    @next_cursor = records.last.name
  end
end