Commit 791785af authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent a92d6b36
# frozen_string_literal: true # frozen_string_literal: true
class BranchesFinder class BranchesFinder < GitRefsFinder
def initialize(repository, params = {}) def initialize(repository, params = {})
@repository = repository super(repository, params)
@params = params
end end
def execute def execute
...@@ -15,56 +14,10 @@ class BranchesFinder ...@@ -15,56 +14,10 @@ class BranchesFinder
private private
attr_reader :repository, :params
def names def names
@params[:names].presence @params[:names].presence
end end
def search
@params[:search].presence
end
def sort
@params[:sort].presence || 'name'
end
def by_search(branches)
return branches unless search
case search
when ->(v) { v.starts_with?('^') }
filter_branches_with_prefix(branches, search.slice(1..-1).upcase)
when ->(v) { v.ends_with?('$') }
filter_branches_with_suffix(branches, search.chop.upcase)
else
matches = filter_branches_by_name(branches, search.upcase)
set_exact_match_as_first_result(matches, search)
end
end
def filter_branches_with_prefix(branches, prefix)
branches.select { |branch| branch.name.upcase.starts_with?(prefix) }
end
def filter_branches_with_suffix(branches, suffix)
branches.select { |branch| branch.name.upcase.ends_with?(suffix) }
end
def filter_branches_by_name(branches, term)
branches.select { |branch| branch.name.upcase.include?(term) }
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 { |branch| branch.name.casecmp(term) == 0 }
end
def by_names(branches) def by_names(branches)
return branches unless names return branches unless names
......
# frozen_string_literal: true
class GitRefsFinder
def initialize(repository, params = {})
@repository = repository
@params = params
end
protected
attr_reader :repository, :params
def search
@params[:search].presence
end
def sort
@params[:sort].presence || 'name'
end
def by_search(refs)
return refs unless search
case search
when ->(v) { v.starts_with?('^') }
filter_refs_with_prefix(refs, search.slice(1..-1))
when ->(v) { v.ends_with?('$') }
filter_refs_with_suffix(refs, search.chop)
else
matches = filter_refs_by_name(refs, search)
set_exact_match_as_first_result(matches, search)
end
end
def filter_refs_with_prefix(refs, prefix)
refs.select { |ref| ref.name.upcase.starts_with?(prefix.upcase) }
end
def filter_refs_with_suffix(refs, suffix)
refs.select { |ref| ref.name.upcase.ends_with?(suffix.upcase) }
end
def filter_refs_by_name(refs, term)
refs.select { |ref| ref.name.upcase.include?(term.upcase) }
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
end
# frozen_string_literal: true # frozen_string_literal: true
class TagsFinder class TagsFinder < GitRefsFinder
def initialize(repository, params) def initialize(repository, params)
@repository = repository super(repository, params)
@params = params
end end
def execute def execute
tags = @repository.tags_sorted_by(sort) tags = repository.tags_sorted_by(sort)
filter_by_name(tags) tags = by_search(tags)
end tags
private
def sort
@params[:sort].presence
end
def search
@params[:search].presence
end
def filter_by_name(tags)
if search
tags.select { |tag| tag.name.include?(search) }
else
tags
end
end end
end end
...@@ -6,12 +6,14 @@ class NotificationReason ...@@ -6,12 +6,14 @@ class NotificationReason
OWN_ACTIVITY = 'own_activity' OWN_ACTIVITY = 'own_activity'
ASSIGNED = 'assigned' ASSIGNED = 'assigned'
MENTIONED = 'mentioned' MENTIONED = 'mentioned'
SUBSCRIBED = 'subscribed'
# Priority list for selecting which reason to return in the notification # Priority list for selecting which reason to return in the notification
REASON_PRIORITY = [ REASON_PRIORITY = [
OWN_ACTIVITY, OWN_ACTIVITY,
ASSIGNED, ASSIGNED,
MENTIONED MENTIONED,
SUBSCRIBED
].freeze ].freeze
# returns the priority of a reason as an integer # returns the priority of a reason as an integer
......
...@@ -56,9 +56,6 @@ class User < ApplicationRecord ...@@ -56,9 +56,6 @@ class User < ApplicationRecord
BLOCKED_MESSAGE = "Your account has been blocked. Please contact your GitLab " \ BLOCKED_MESSAGE = "Your account has been blocked. Please contact your GitLab " \
"administrator if you think this is an error." "administrator if you think this is an error."
# Removed in GitLab 12.3. Keep until after 2019-09-22.
self.ignored_columns += %i[support_bot]
MINIMUM_INACTIVE_DAYS = 180 MINIMUM_INACTIVE_DAYS = 180
# Override Devise::Models::Trackable#update_tracked_fields! # Override Devise::Models::Trackable#update_tracked_fields!
......
...@@ -181,7 +181,7 @@ module NotificationRecipientService ...@@ -181,7 +181,7 @@ module NotificationRecipientService
def add_subscribed_users def add_subscribed_users
return unless target.respond_to? :subscribers return unless target.respond_to? :subscribers
add_recipients(target.subscribers(project), :subscription, nil) add_recipients(target.subscribers(project), :subscription, NotificationReason::SUBSCRIBED)
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
...@@ -240,7 +240,7 @@ module NotificationRecipientService ...@@ -240,7 +240,7 @@ module NotificationRecipientService
return unless target.respond_to? :labels return unless target.respond_to? :labels
(labels || target.labels).each do |label| (labels || target.labels).each do |label|
add_recipients(label.subscribers(project), :subscription, nil) add_recipients(label.subscribers(project), :subscription, NotificationReason::SUBSCRIBED)
end end
end end
end end
......
---
title: Set X-GitLab-NotificationReason header if notification reason is explicit subscription
merge_request: 18812
author:
type: added
---
title: Adding support for searching tags using '^' and '$'
merge_request: 19435
author: Cauhx Milloy
type: added
---
title: Do not escape HTML tags in Ansi2json as they are escaped in the frontend
merge_request: 19610
author:
type: fixed
...@@ -21,7 +21,7 @@ Parameters: ...@@ -21,7 +21,7 @@ Parameters:
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
|:----------|:---------------|:---------|:------------| |:----------|:---------------|:---------|:------------|
| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user.| | `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user.|
| `search` | string | no | Return list of branches containing the search string. You can use `^term` and `term$` to find branches that begin and end with `term` respectively.| | `search` | string | no | Return list of branches containing the search string. You can use `^term` and `term$` to find branches that begin and end with `term` respectively. |
Example request: Example request:
......
...@@ -17,7 +17,7 @@ Parameters: ...@@ -17,7 +17,7 @@ Parameters:
| `id` | integer/string| yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user| | `id` | integer/string| yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user|
| `order_by` | string | no | Return tags ordered by `name` or `updated` fields. Default is `updated` | | `order_by` | string | no | Return tags ordered by `name` or `updated` fields. Default is `updated` |
| `sort` | string | no | Return tags sorted in `asc` or `desc` order. Default is `desc` | | `sort` | string | no | Return tags sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Return list of tags matching the search criteria | | `search` | string | no | Return list of tags matching the search criteria. You can use `^term` and `term$` to find tags that begin and end with `term` respectively. |
> Support for `search` was [introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/54401) in GitLab 11.8. > Support for `search` was [introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/54401) in GitLab 11.8.
......
...@@ -66,14 +66,12 @@ module Gitlab ...@@ -66,14 +66,12 @@ module Gitlab
elsif scan_token(scanner, /\e(([@-_])(.*?)?)?$/) elsif scan_token(scanner, /\e(([@-_])(.*?)?)?$/)
# stop scanning # stop scanning
scanner.terminate scanner.terminate
elsif scan_token(scanner, /</)
@state.current_line << '&lt;'
elsif scan_token(scanner, /\r?\n/) elsif scan_token(scanner, /\r?\n/)
flush_current_line flush_current_line
elsif scan_token(scanner, /\r/) elsif scan_token(scanner, /\r/)
# drop last line # drop last line
@state.current_line.clear! @state.current_line.clear!
elsif scan_token(scanner, /.[^\e<\r\ns]*/m) elsif scan_token(scanner, /.[^\e\r\ns]*/m)
# this is a join from all previous tokens and first letters # this is a join from all previous tokens and first letters
# it always matches at least one character `.` # it always matches at least one character `.`
# it matches everything that is not start of: # it matches everything that is not start of:
......
...@@ -73,58 +73,76 @@ describe BranchesFinder do ...@@ -73,58 +73,76 @@ describe BranchesFinder do
expect(result.count).to eq(3) expect(result.count).to eq(3)
expect(result.map(&:name)).to eq(%w{csv fix lfs}) expect(result.map(&:name)).to eq(%w{csv fix lfs})
end end
end
context 'filter and sort' do it 'filters branches by name that begins with' do
it 'filters branches by name and sorts by recently_updated' do params = { search: '^feature_' }
params = { sort: 'updated_desc', search: 'feat' }
branches_finder = described_class.new(repository, params) branches_finder = described_class.new(repository, params)
result = branches_finder.execute result = branches_finder.execute
expect(result.first.name).to eq('feature_conflict') expect(result.first.name).to eq('feature_conflict')
expect(result.count).to eq(2) expect(result.count).to eq(1)
end end
it 'filters branches by name and sorts by recently_updated, with exact matches first' do it 'filters branches by name that ends with' do
params = { sort: 'updated_desc', search: 'feature' } params = { search: 'feature$' }
branches_finder = described_class.new(repository, params) branches_finder = described_class.new(repository, params)
result = branches_finder.execute result = branches_finder.execute
expect(result.first.name).to eq('feature') expect(result.first.name).to eq('feature')
expect(result.second.name).to eq('feature_conflict') expect(result.count).to eq(1)
expect(result.count).to eq(2)
end end
it 'filters branches by name and sorts by last_updated' do it 'filters branches by nonexistent name that begins with' do
params = { sort: 'updated_asc', search: 'feature' } params = { search: '^nope' }
branches_finder = described_class.new(repository, params) branches_finder = described_class.new(repository, params)
result = branches_finder.execute result = branches_finder.execute
expect(result.first.name).to eq('feature') expect(result.count).to eq(0)
expect(result.count).to eq(2)
end end
it 'filters branches by name that begins with' do it 'filters branches by nonexistent name that ends with' do
params = { search: '^feature_' } params = { search: 'nope$' }
branches_finder = described_class.new(repository, params)
result = branches_finder.execute
expect(result.count).to eq(0)
end
end
context 'filter and sort' do
it 'filters branches by name and sorts by recently_updated' do
params = { sort: 'updated_desc', search: 'feat' }
branches_finder = described_class.new(repository, params) branches_finder = described_class.new(repository, params)
result = branches_finder.execute result = branches_finder.execute
expect(result.first.name).to eq('feature_conflict') expect(result.first.name).to eq('feature_conflict')
expect(result.count).to eq(1) expect(result.count).to eq(2)
end end
it 'filters branches by name that ends with' do it 'filters branches by name and sorts by recently_updated, with exact matches first' do
params = { search: 'feature$' } params = { sort: 'updated_desc', search: 'feature' }
branches_finder = described_class.new(repository, params) branches_finder = described_class.new(repository, params)
result = branches_finder.execute result = branches_finder.execute
expect(result.first.name).to eq('feature') expect(result.first.name).to eq('feature')
expect(result.count).to eq(1) expect(result.second.name).to eq('feature_conflict')
expect(result.count).to eq(2)
end
it 'filters branches by name and sorts by last_updated' do
params = { sort: 'updated_asc', search: 'feature' }
branches_finder = described_class.new(repository, params)
result = branches_finder.execute
expect(result.first.name).to eq('feature')
expect(result.count).to eq(2)
end end
end end
end end
......
...@@ -54,6 +54,44 @@ describe TagsFinder do ...@@ -54,6 +54,44 @@ describe TagsFinder do
expect(result.count).to eq(0) expect(result.count).to eq(0)
end end
it 'filters tags by name that begins with' do
params = { search: '^v1.0' }
tags_finder = described_class.new(repository, params)
result = tags_finder.execute
expect(result.first.name).to eq('v1.0.0')
expect(result.count).to eq(1)
end
it 'filters tags by name that ends with' do
params = { search: '0.0$' }
tags_finder = described_class.new(repository, params)
result = tags_finder.execute
expect(result.first.name).to eq('v1.0.0')
expect(result.count).to eq(1)
end
it 'filters tags by nonexistent name that begins with' do
params = { search: '^nope' }
tags_finder = described_class.new(repository, params)
result = tags_finder.execute
expect(result.count).to eq(0)
end
it 'filters tags by nonexistent name that ends with' do
params = { search: 'nope$' }
tags_finder = described_class.new(repository, params)
result = tags_finder.execute
expect(result.count).to eq(0)
end
end end
context 'filter and sort' do context 'filter and sort' do
......
...@@ -224,17 +224,17 @@ describe Gitlab::Ci::Ansi2json do ...@@ -224,17 +224,17 @@ describe Gitlab::Ci::Ansi2json do
end end
end end
it 'prevents XSS injection' do it 'prints HTML tags as is' do
trace = "#{section_start}section_end:1:2<script>alert('XSS Hack!');</script>#{section_end}" trace = "#{section_start}section_end:1:2<div>hello</div>#{section_end}"
expect(convert_json(trace)).to eq([ expect(convert_json(trace)).to eq([
{ {
offset: 0, offset: 0,
content: [{ text: "section_end:1:2&lt;script>alert('XSS Hack!');&lt;/script>" }], content: [{ text: "section_end:1:2<div>hello</div>" }],
section: 'prepare-script', section: 'prepare-script',
section_header: true section_header: true
}, },
{ {
offset: 95, offset: 75,
content: [], content: [],
section: 'prepare-script', section: 'prepare-script',
section_duration: '01:03' section_duration: '01:03'
......
...@@ -869,6 +869,18 @@ describe NotificationService, :mailer do ...@@ -869,6 +869,18 @@ describe NotificationService, :mailer do
should_email(user_4) should_email(user_4)
end end
it 'adds "subscribed" reason to subscriber emails' do
user_1 = create(:user)
label = create(:label, project: project, issues: [issue])
issue.reload
label.subscribe(user_1)
notification.new_issue(issue, @u_disabled)
email = find_email_for(user_1)
expect(email).to have_header('X-GitLab-NotificationReason', NotificationReason::SUBSCRIBED)
end
it_behaves_like 'project emails are disabled' do it_behaves_like 'project emails are disabled' do
let(:notification_target) { issue } let(:notification_target) { issue }
let(:notification_trigger) { notification.new_issue(issue, @u_disabled) } let(:notification_trigger) { notification.new_issue(issue, @u_disabled) }
...@@ -1272,6 +1284,17 @@ describe NotificationService, :mailer do ...@@ -1272,6 +1284,17 @@ describe NotificationService, :mailer do
let(:notification_target) { issue } let(:notification_target) { issue }
let(:notification_trigger) { notification.close_issue(issue, @u_disabled) } let(:notification_trigger) { notification.close_issue(issue, @u_disabled) }
end end
it 'adds "subscribed" reason to subscriber emails' do
user_1 = create(:user)
issue.subscribe(user_1)
issue.reload
notification.close_issue(issue, @u_disabled)
email = find_email_for(user_1)
expect(email).to have_header('X-GitLab-NotificationReason', NotificationReason::SUBSCRIBED)
end
end end
describe '#reopen_issue' do describe '#reopen_issue' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment