summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorKamil Trzcinski <ayufan@ayufan.eu>2017-01-24 21:52:54 +0100
committerKamil Trzcinski <ayufan@ayufan.eu>2017-01-24 21:52:54 +0100
commitac92554dfc25fc8129b1972da9a385af8d8b733f (patch)
tree14edbdb3f0479d8fad44e18eecebdc10285ffaee /lib
parentb7f4553e3e4681d5a4a53af35650bcbb1c83b390 (diff)
parent8c9a06c37c4cf9763b3781d7e567a7b442c2152c (diff)
downloadgitlab-ce-ac92554dfc25fc8129b1972da9a385af8d8b733f.tar.gz
Merge remote-tracking branch 'origin/master' into backport-ee-changes-for-build-minutes
Diffstat (limited to 'lib')
-rw-r--r--lib/api/deploy_keys.rb27
-rw-r--r--lib/api/helpers.rb6
-rw-r--r--lib/api/merge_request_diffs.rb8
-rw-r--r--lib/api/merge_requests.rb25
-rw-r--r--lib/api/notes.rb26
-rw-r--r--lib/api/services.rb48
-rw-r--r--lib/api/subscriptions.rb4
-rw-r--r--lib/api/todos.rb2
-rw-r--r--lib/gitlab/ci/status/build/factory.rb7
-rw-r--r--lib/gitlab/ci/status/build/failed_allowed.rb27
-rw-r--r--lib/gitlab/ci/status/factory.rb43
-rw-r--r--lib/gitlab/ci/status/pipeline/factory.rb2
-rw-r--r--lib/gitlab/ci/status/pipeline/success_with_warnings.rb31
-rw-r--r--lib/gitlab/ci/status/stage/factory.rb4
-rw-r--r--lib/gitlab/ci/status/success_warning.rb33
-rw-r--r--lib/gitlab/email/handler.rb3
-rw-r--r--lib/gitlab/email/handler/base_handler.rb43
-rw-r--r--lib/gitlab/email/handler/create_issue_handler.rb1
-rw-r--r--lib/gitlab/email/handler/create_note_handler.rb7
-rw-r--r--lib/gitlab/email/handler/reply_processing.rb54
-rw-r--r--lib/gitlab/email/handler/unsubscribe_handler.rb32
-rw-r--r--lib/gitlab/import_export/members_mapper.rb8
-rw-r--r--lib/gitlab/import_export/relation_factory.rb12
-rw-r--r--lib/gitlab/incoming_email.rb9
-rw-r--r--lib/gitlab/project_search_results.rb28
-rw-r--r--lib/gitlab/search_results.rb4
-rw-r--r--lib/gitlab/user_access.rb4
-rw-r--r--lib/mattermost/client.rb22
-rw-r--r--lib/mattermost/command.rb2
-rw-r--r--lib/mattermost/team.rb2
30 files changed, 352 insertions, 172 deletions
diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb
index 85360730841..64da7d6b86f 100644
--- a/lib/api/deploy_keys.rb
+++ b/lib/api/deploy_keys.rb
@@ -38,26 +38,25 @@ module API
present key, with: Entities::SSHKey
end
- # TODO: for 9.0 we should check if params are there with the params block
- # grape provides, at this point we'd change behaviour so we can't
- # Behaviour now if you don't provide all required params: it renders a
- # validation error or two.
desc 'Add new deploy key to currently authenticated user' do
success Entities::SSHKey
end
+ params do
+ requires :key, type: String, desc: 'The new deploy key'
+ requires :title, type: String, desc: 'The name of the deploy key'
+ end
post ":id/#{path}" do
- attrs = attributes_for_keys [:title, :key]
- attrs[:key].strip! if attrs[:key]
+ params[:key].strip!
# Check for an existing key joined to this project
- key = user_project.deploy_keys.find_by(key: attrs[:key])
+ key = user_project.deploy_keys.find_by(key: params[:key])
if key
present key, with: Entities::SSHKey
break
end
# Check for available deploy keys in other projects
- key = current_user.accessible_deploy_keys.find_by(key: attrs[:key])
+ key = current_user.accessible_deploy_keys.find_by(key: params[:key])
if key
user_project.deploy_keys << key
present key, with: Entities::SSHKey
@@ -65,7 +64,7 @@ module API
end
# Create a new deploy key
- key = DeployKey.new attrs
+ key = DeployKey.new(declared_params(include_missing: false))
if key.valid? && user_project.deploy_keys << key
present key, with: Entities::SSHKey
else
@@ -105,15 +104,19 @@ module API
present key.deploy_key, with: Entities::SSHKey
end
- desc 'Delete existing deploy key of currently authenticated user' do
+ desc 'Delete deploy key for a project' do
success Key
end
params do
requires :key_id, type: Integer, desc: 'The ID of the deploy key'
end
delete ":id/#{path}/:key_id" do
- key = user_project.deploy_keys.find(params[:key_id])
- key.destroy
+ key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id])
+ if key
+ key.destroy
+ else
+ not_found!('Deploy Key')
+ end
end
end
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 49c5f0652ab..a1d7b323f4f 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -90,6 +90,12 @@ module API
MergeRequestsFinder.new(current_user, project_id: user_project.id).find(id)
end
+ def find_merge_request_with_access(id, access_level = :read_merge_request)
+ merge_request = user_project.merge_requests.find(id)
+ authorize! access_level, merge_request
+ merge_request
+ end
+
def authenticate!
unauthorized! unless current_user
end
diff --git a/lib/api/merge_request_diffs.rb b/lib/api/merge_request_diffs.rb
index 07435d78468..bc3d69f6904 100644
--- a/lib/api/merge_request_diffs.rb
+++ b/lib/api/merge_request_diffs.rb
@@ -15,10 +15,8 @@ module API
end
get ":id/merge_requests/:merge_request_id/versions" do
- merge_request = user_project.merge_requests.
- find(params[:merge_request_id])
+ merge_request = find_merge_request_with_access(params[:merge_request_id])
- authorize! :read_merge_request, merge_request
present merge_request.merge_request_diffs, with: Entities::MergeRequestDiff
end
@@ -34,10 +32,8 @@ module API
end
get ":id/merge_requests/:merge_request_id/versions/:version_id" do
- merge_request = user_project.merge_requests.
- find(params[:merge_request_id])
+ merge_request = find_merge_request_with_access(params[:merge_request_id])
- authorize! :read_merge_request, merge_request
present merge_request.merge_request_diffs.find(params[:version_id]), with: Entities::MergeRequestDiffFull
end
end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index e77af4b7a0d..7ffb38e62da 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -118,8 +118,8 @@ module API
success Entities::MergeRequest
end
get path do
- merge_request = find_project_merge_request(params[:merge_request_id])
- authorize! :read_merge_request, merge_request
+ merge_request = find_merge_request_with_access(params[:merge_request_id])
+
present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
end
@@ -127,8 +127,8 @@ module API
success Entities::RepoCommit
end
get "#{path}/commits" do
- merge_request = find_project_merge_request(params[:merge_request_id])
- authorize! :read_merge_request, merge_request
+ merge_request = find_merge_request_with_access(params[:merge_request_id])
+
present merge_request.commits, with: Entities::RepoCommit
end
@@ -136,8 +136,8 @@ module API
success Entities::MergeRequestChanges
end
get "#{path}/changes" do
- merge_request = find_project_merge_request(params[:merge_request_id])
- authorize! :read_merge_request, merge_request
+ merge_request = find_merge_request_with_access(params[:merge_request_id])
+
present merge_request, with: Entities::MergeRequestChanges, current_user: current_user
end
@@ -155,8 +155,7 @@ module API
:remove_source_branch
end
put path do
- merge_request = find_project_merge_request(params.delete(:merge_request_id))
- authorize! :update_merge_request, merge_request
+ merge_request = find_merge_request_with_access(params.delete(:merge_request_id), :update_merge_request)
mr_params = declared_params(include_missing: false)
mr_params[:force_remove_source_branch] = mr_params.delete(:remove_source_branch) if mr_params[:remove_source_branch].present?
@@ -235,10 +234,7 @@ module API
use :pagination
end
get "#{path}/comments" do
- merge_request = find_project_merge_request(params[:merge_request_id])
-
- authorize! :read_merge_request, merge_request
-
+ merge_request = find_merge_request_with_access(params[:merge_request_id])
present paginate(merge_request.notes.fresh), with: Entities::MRNote
end
@@ -250,8 +246,7 @@ module API
requires :note, type: String, desc: 'The text of the comment'
end
post "#{path}/comments" do
- merge_request = find_project_merge_request(params[:merge_request_id])
- authorize! :create_note, merge_request
+ merge_request = find_merge_request_with_access(params[:merge_request_id], :create_note)
opts = {
note: params[:note],
@@ -275,7 +270,7 @@ module API
use :pagination
end
get "#{path}/closes_issues" do
- merge_request = find_project_merge_request(params[:merge_request_id])
+ merge_request = find_merge_request_with_access(params[:merge_request_id])
issues = ::Kaminari.paginate_array(merge_request.closes_issues(current_user))
present paginate(issues), with: issue_entity(user_project), current_user: current_user
end
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index 284e4cf549a..4d2a8f48267 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -70,21 +70,27 @@ module API
end
post ":id/#{noteables_str}/:noteable_id/notes" do
opts = {
- note: params[:body],
- noteable_type: noteables_str.classify,
- noteable_id: params[:noteable_id]
+ note: params[:body],
+ noteable_type: noteables_str.classify,
+ noteable_id: params[:noteable_id]
}
- if params[:created_at] && (current_user.is_admin? || user_project.owner == current_user)
- opts[:created_at] = params[:created_at]
- end
+ noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id])
+
+ if can?(current_user, noteable_read_ability_name(noteable), noteable)
+ if params[:created_at] && (current_user.is_admin? || user_project.owner == current_user)
+ opts[:created_at] = params[:created_at]
+ end
- note = ::Notes::CreateService.new(user_project, current_user, opts).execute
+ note = ::Notes::CreateService.new(user_project, current_user, opts).execute
- if note.valid?
- present note, with: Entities::const_get(note.class.name)
+ if note.valid?
+ present note, with: Entities::const_get(note.class.name)
+ else
+ not_found!("Note #{note.errors.messages}")
+ end
else
- not_found!("Note #{note.errors.messages}")
+ not_found!("Note")
end
end
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 3a9dfbb237c..a0abec49438 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -145,7 +145,7 @@ module API
name: :room,
type: String,
desc: 'Campfire room'
- },
+ }
],
'custom-issue-tracker' => [
{
@@ -534,7 +534,36 @@ module API
desc: 'The password of the user'
}
]
- }.freeze
+ }
+
+ service_classes = [
+ AsanaService,
+ AssemblaService,
+ BambooService,
+ BugzillaService,
+ BuildkiteService,
+ BuildsEmailService,
+ CampfireService,
+ CustomIssueTrackerService,
+ DroneCiService,
+ EmailsOnPushService,
+ ExternalWikiService,
+ FlowdockService,
+ GemnasiumService,
+ HipchatService,
+ IrkerService,
+ JiraService,
+ KubernetesService,
+ MattermostSlashCommandsService,
+ SlackSlashCommandsService,
+ PipelinesEmailService,
+ PivotaltrackerService,
+ PushoverService,
+ RedmineService,
+ SlackService,
+ MattermostService,
+ TeamcityService,
+ ].freeze
trigger_services = {
'mattermost-slash-commands' => [
@@ -568,6 +597,19 @@ module API
services.each do |service_slug, settings|
desc "Set #{service_slug} service for project"
params do
+ service_classes.each do |service|
+ event_names = service.try(:event_names) || []
+ event_names.each do |event_name|
+ services[service.to_param.tr("_", "-")] << {
+ required: false,
+ name: event_name.to_sym,
+ type: String,
+ desc: ServicesHelper.service_event_description(event_name)
+ }
+ end
+ end
+ services.freeze
+
settings.each do |setting|
if setting[:required]
requires setting[:name], type: setting[:type], desc: setting[:desc]
@@ -581,7 +623,7 @@ module API
service_params = declared_params(include_missing: false).merge(active: true)
if service.update_attributes(service_params)
- true
+ present service, with: Entities::ProjectService, include_passwords: current_user.is_admin?
else
render_api_error!('400 Bad Request', 400)
end
diff --git a/lib/api/subscriptions.rb b/lib/api/subscriptions.rb
index 10749b34004..e11d7537cc9 100644
--- a/lib/api/subscriptions.rb
+++ b/lib/api/subscriptions.rb
@@ -3,8 +3,8 @@ module API
before { authenticate! }
subscribable_types = {
- 'merge_request' => proc { |id| user_project.merge_requests.find(id) },
- 'merge_requests' => proc { |id| user_project.merge_requests.find(id) },
+ 'merge_request' => proc { |id| find_merge_request_with_access(id, :update_merge_request) },
+ 'merge_requests' => proc { |id| find_merge_request_with_access(id, :update_merge_request) },
'issues' => proc { |id| find_project_issue(id) },
'labels' => proc { |id| find_project_label(id) },
}
diff --git a/lib/api/todos.rb b/lib/api/todos.rb
index ed8f48aa1e3..9bd077263a7 100644
--- a/lib/api/todos.rb
+++ b/lib/api/todos.rb
@@ -5,7 +5,7 @@ module API
before { authenticate! }
ISSUABLE_TYPES = {
- 'merge_requests' => ->(id) { user_project.merge_requests.find(id) },
+ 'merge_requests' => ->(id) { find_merge_request_with_access(id) },
'issues' => ->(id) { find_project_issue(id) }
}
diff --git a/lib/gitlab/ci/status/build/factory.rb b/lib/gitlab/ci/status/build/factory.rb
index eee9a64120b..38ac6edc9f1 100644
--- a/lib/gitlab/ci/status/build/factory.rb
+++ b/lib/gitlab/ci/status/build/factory.rb
@@ -4,8 +4,11 @@ module Gitlab
module Build
class Factory < Status::Factory
def self.extended_statuses
- [Status::Build::Stop, Status::Build::Play,
- Status::Build::Cancelable, Status::Build::Retryable]
+ [[Status::Build::Cancelable,
+ Status::Build::Retryable],
+ [Status::Build::FailedAllowed,
+ Status::Build::Play,
+ Status::Build::Stop]]
end
def self.common_helpers
diff --git a/lib/gitlab/ci/status/build/failed_allowed.rb b/lib/gitlab/ci/status/build/failed_allowed.rb
new file mode 100644
index 00000000000..807afe24bd5
--- /dev/null
+++ b/lib/gitlab/ci/status/build/failed_allowed.rb
@@ -0,0 +1,27 @@
+module Gitlab
+ module Ci
+ module Status
+ module Build
+ class FailedAllowed < SimpleDelegator
+ include Status::Extended
+
+ def label
+ 'failed (allowed to fail)'
+ end
+
+ def icon
+ 'icon_status_warning'
+ end
+
+ def group
+ 'failed_with_warnings'
+ end
+
+ def self.matches?(build, user)
+ build.failed? && build.allow_failure?
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/status/factory.rb b/lib/gitlab/ci/status/factory.rb
index ae9ef895df4..15836c699c7 100644
--- a/lib/gitlab/ci/status/factory.rb
+++ b/lib/gitlab/ci/status/factory.rb
@@ -5,41 +5,46 @@ module Gitlab
def initialize(subject, user)
@subject = subject
@user = user
+ @status = subject.status || HasStatus::DEFAULT_STATUS
end
def fabricate!
- if extended_status
- extended_status.new(core_status)
- else
+ if extended_statuses.none?
core_status
+ else
+ compound_extended_status
end
end
- def self.extended_statuses
- []
+ def core_status
+ Gitlab::Ci::Status
+ .const_get(@status.capitalize)
+ .new(@subject, @user)
+ .extend(self.class.common_helpers)
end
- def self.common_helpers
- Module.new
+ def compound_extended_status
+ extended_statuses.inject(core_status) do |status, extended|
+ extended.new(status)
+ end
end
- private
+ def extended_statuses
+ return @extended_statuses if defined?(@extended_statuses)
- def simple_status
- @simple_status ||= @subject.status || :created
+ groups = self.class.extended_statuses.map do |group|
+ Array(group).find { |status| status.matches?(@subject, @user) }
+ end
+
+ @extended_statuses = groups.flatten.compact
end
- def core_status
- Gitlab::Ci::Status
- .const_get(simple_status.capitalize)
- .new(@subject, @user)
- .extend(self.class.common_helpers)
+ def self.extended_statuses
+ []
end
- def extended_status
- @extended ||= self.class.extended_statuses.find do |status|
- status.matches?(@subject, @user)
- end
+ def self.common_helpers
+ Module.new
end
end
end
diff --git a/lib/gitlab/ci/status/pipeline/factory.rb b/lib/gitlab/ci/status/pipeline/factory.rb
index 16dcb326be9..13c8343b12a 100644
--- a/lib/gitlab/ci/status/pipeline/factory.rb
+++ b/lib/gitlab/ci/status/pipeline/factory.rb
@@ -4,7 +4,7 @@ module Gitlab
module Pipeline
class Factory < Status::Factory
def self.extended_statuses
- [Pipeline::SuccessWithWarnings]
+ [Status::SuccessWarning]
end
def self.common_helpers
diff --git a/lib/gitlab/ci/status/pipeline/success_with_warnings.rb b/lib/gitlab/ci/status/pipeline/success_with_warnings.rb
deleted file mode 100644
index 24bf8b869e0..00000000000
--- a/lib/gitlab/ci/status/pipeline/success_with_warnings.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-module Gitlab
- module Ci
- module Status
- module Pipeline
- class SuccessWithWarnings < SimpleDelegator
- include Status::Extended
-
- def text
- 'passed'
- end
-
- def label
- 'passed with warnings'
- end
-
- def icon
- 'icon_status_warning'
- end
-
- def group
- 'success_with_warnings'
- end
-
- def self.matches?(pipeline, user)
- pipeline.success? && pipeline.has_warnings?
- end
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/ci/status/stage/factory.rb b/lib/gitlab/ci/status/stage/factory.rb
index 689a5dd45bc..4c37f084d07 100644
--- a/lib/gitlab/ci/status/stage/factory.rb
+++ b/lib/gitlab/ci/status/stage/factory.rb
@@ -3,6 +3,10 @@ module Gitlab
module Status
module Stage
class Factory < Status::Factory
+ def self.extended_statuses
+ [Status::SuccessWarning]
+ end
+
def self.common_helpers
Status::Stage::Common
end
diff --git a/lib/gitlab/ci/status/success_warning.rb b/lib/gitlab/ci/status/success_warning.rb
new file mode 100644
index 00000000000..d4cdab6957a
--- /dev/null
+++ b/lib/gitlab/ci/status/success_warning.rb
@@ -0,0 +1,33 @@
+module Gitlab
+ module Ci
+ module Status
+ ##
+ # Extended status used when pipeline or stage passed conditionally.
+ # This means that failed jobs that are allowed to fail were present.
+ #
+ class SuccessWarning < SimpleDelegator
+ include Status::Extended
+
+ def text
+ 'passed'
+ end
+
+ def label
+ 'passed with warnings'
+ end
+
+ def icon
+ 'icon_status_warning'
+ end
+
+ def group
+ 'success_with_warnings'
+ end
+
+ def self.matches?(subject, user)
+ subject.success? && subject.has_warnings?
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/handler.rb b/lib/gitlab/email/handler.rb
index bd3267e2a80..bd2f5d3615e 100644
--- a/lib/gitlab/email/handler.rb
+++ b/lib/gitlab/email/handler.rb
@@ -1,10 +1,11 @@
require 'gitlab/email/handler/create_note_handler'
require 'gitlab/email/handler/create_issue_handler'
+require 'gitlab/email/handler/unsubscribe_handler'
module Gitlab
module Email
module Handler
- HANDLERS = [CreateNoteHandler, CreateIssueHandler]
+ HANDLERS = [UnsubscribeHandler, CreateNoteHandler, CreateIssueHandler]
def self.for(mail, mail_key)
HANDLERS.find do |klass|
diff --git a/lib/gitlab/email/handler/base_handler.rb b/lib/gitlab/email/handler/base_handler.rb
index 7cccf465334..3f6ace0311a 100644
--- a/lib/gitlab/email/handler/base_handler.rb
+++ b/lib/gitlab/email/handler/base_handler.rb
@@ -9,52 +9,13 @@ module Gitlab
@mail_key = mail_key
end
- def message
- @message ||= process_message
- end
-
- def author
+ def can_execute?
raise NotImplementedError
end
- def project
+ def execute
raise NotImplementedError
end
-
- private
-
- def validate_permission!(permission)
- raise UserNotFoundError unless author
- raise UserBlockedError if author.blocked?
- raise ProjectNotFound unless author.can?(:read_project, project)
- raise UserNotAuthorizedError unless author.can?(permission, project)
- end
-
- def process_message
- message = ReplyParser.new(mail).execute.strip
- add_attachments(message)
- end
-
- def add_attachments(reply)
- attachments = Email::AttachmentUploader.new(mail).execute(project)
-
- reply + attachments.map do |link|
- "\n\n#{link[:markdown]}"
- end.join
- end
-
- def verify_record!(record:, invalid_exception:, record_name:)
- return if record.persisted?
- return if record.errors.key?(:commands_only)
-
- error_title = "The #{record_name} could not be created for the following reasons:"
-
- msg = error_title + record.errors.full_messages.map do |error|
- "\n\n- #{error}"
- end.join
-
- raise invalid_exception, msg
- end
end
end
end
diff --git a/lib/gitlab/email/handler/create_issue_handler.rb b/lib/gitlab/email/handler/create_issue_handler.rb
index 9f90a3ec2b2..127fae159d5 100644
--- a/lib/gitlab/email/handler/create_issue_handler.rb
+++ b/lib/gitlab/email/handler/create_issue_handler.rb
@@ -5,6 +5,7 @@ module Gitlab
module Email
module Handler
class CreateIssueHandler < BaseHandler
+ include ReplyProcessing
attr_reader :project_path, :incoming_email_token
def initialize(mail, mail_key)
diff --git a/lib/gitlab/email/handler/create_note_handler.rb b/lib/gitlab/email/handler/create_note_handler.rb
index 447c7a6a6b9..d87ba427f4b 100644
--- a/lib/gitlab/email/handler/create_note_handler.rb
+++ b/lib/gitlab/email/handler/create_note_handler.rb
@@ -1,10 +1,13 @@
require 'gitlab/email/handler/base_handler'
+require 'gitlab/email/handler/reply_processing'
module Gitlab
module Email
module Handler
class CreateNoteHandler < BaseHandler
+ include ReplyProcessing
+
def can_handle?
mail_key =~ /\A\w+\z/
end
@@ -24,6 +27,8 @@ module Gitlab
record_name: 'comment')
end
+ private
+
def author
sent_notification.recipient
end
@@ -36,8 +41,6 @@ module Gitlab
@sent_notification ||= SentNotification.for(mail_key)
end
- private
-
def create_note
Notes::CreateService.new(
project,
diff --git a/lib/gitlab/email/handler/reply_processing.rb b/lib/gitlab/email/handler/reply_processing.rb
new file mode 100644
index 00000000000..32c5caf93e8
--- /dev/null
+++ b/lib/gitlab/email/handler/reply_processing.rb
@@ -0,0 +1,54 @@
+module Gitlab
+ module Email
+ module Handler
+ module ReplyProcessing
+ private
+
+ def author
+ raise NotImplementedError
+ end
+
+ def project
+ raise NotImplementedError
+ end
+
+ def message
+ @message ||= process_message
+ end
+
+ def process_message
+ message = ReplyParser.new(mail).execute.strip
+ add_attachments(message)
+ end
+
+ def add_attachments(reply)
+ attachments = Email::AttachmentUploader.new(mail).execute(project)
+
+ reply + attachments.map do |link|
+ "\n\n#{link[:markdown]}"
+ end.join
+ end
+
+ def validate_permission!(permission)
+ raise UserNotFoundError unless author
+ raise UserBlockedError if author.blocked?
+ raise ProjectNotFound unless author.can?(:read_project, project)
+ raise UserNotAuthorizedError unless author.can?(permission, project)
+ end
+
+ def verify_record!(record:, invalid_exception:, record_name:)
+ return if record.persisted?
+ return if record.errors.key?(:commands_only)
+
+ error_title = "The #{record_name} could not be created for the following reasons:"
+
+ msg = error_title + record.errors.full_messages.map do |error|
+ "\n\n- #{error}"
+ end.join
+
+ raise invalid_exception, msg
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/handler/unsubscribe_handler.rb b/lib/gitlab/email/handler/unsubscribe_handler.rb
new file mode 100644
index 00000000000..97d7a8d65ff
--- /dev/null
+++ b/lib/gitlab/email/handler/unsubscribe_handler.rb
@@ -0,0 +1,32 @@
+require 'gitlab/email/handler/base_handler'
+
+module Gitlab
+ module Email
+ module Handler
+ class UnsubscribeHandler < BaseHandler
+ def can_handle?
+ mail_key =~ /\A\w+#{Regexp.escape(Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX)}\z/
+ end
+
+ def execute
+ raise SentNotificationNotFoundError unless sent_notification
+ return unless sent_notification.unsubscribable?
+
+ noteable = sent_notification.noteable
+ raise NoteableNotFoundError unless noteable
+ noteable.unsubscribe(sent_notification.recipient)
+ end
+
+ private
+
+ def sent_notification
+ @sent_notification ||= SentNotification.for(reply_key)
+ end
+
+ def reply_key
+ mail_key.sub(Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX, '')
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/import_export/members_mapper.rb b/lib/gitlab/import_export/members_mapper.rb
index b790733f4a7..2405b94db50 100644
--- a/lib/gitlab/import_export/members_mapper.rb
+++ b/lib/gitlab/import_export/members_mapper.rb
@@ -1,13 +1,10 @@
module Gitlab
module ImportExport
class MembersMapper
- attr_reader :missing_author_ids
-
def initialize(exported_members:, user:, project:)
- @exported_members = exported_members
+ @exported_members = user.admin? ? exported_members : []
@user = user
@project = project
- @missing_author_ids = []
# This needs to run first, as second call would be from #map
# which means project members already exist.
@@ -39,7 +36,6 @@ module Gitlab
def missing_keys_tracking_hash
Hash.new do |_, key|
- @missing_author_ids << key
default_user_id
end
end
@@ -64,7 +60,7 @@ module Gitlab
end
def find_project_user_query(member)
- user_arel[:username].eq(member['user']['username']).or(user_arel[:email].eq(member['user']['email']))
+ user_arel[:email].eq(member['user']['email']).or(user_arel[:username].eq(member['user']['username']))
end
def user_arel
diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb
index 7a649f28340..19e43cce768 100644
--- a/lib/gitlab/import_export/relation_factory.rb
+++ b/lib/gitlab/import_export/relation_factory.rb
@@ -14,7 +14,7 @@ module Gitlab
priorities: :label_priorities,
label: :project_label }.freeze
- USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id created_by_id merge_user_id].freeze
+ USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id created_by_id merge_user_id resolved_by_id].freeze
PROJECT_REFERENCES = %w[project_id source_project_id gl_project_id target_project_id].freeze
@@ -80,17 +80,13 @@ module Gitlab
# is left.
def set_note_author
old_author_id = @relation_hash['author_id']
-
- # Users with admin access can map users
- @relation_hash['author_id'] = admin_user? ? @members_mapper.map[old_author_id] : @members_mapper.default_user_id
-
author = @relation_hash.delete('author')
- update_note_for_missing_author(author['name']) if missing_author?(old_author_id)
+ update_note_for_missing_author(author['name']) unless has_author?(old_author_id)
end
- def missing_author?(old_author_id)
- !admin_user? || @members_mapper.missing_author_ids.include?(old_author_id)
+ def has_author?(old_author_id)
+ admin_user? && @members_mapper.map.keys.include?(old_author_id)
end
def missing_author_note(updated_at, author_name)
diff --git a/lib/gitlab/incoming_email.rb b/lib/gitlab/incoming_email.rb
index 801dfde9a36..b91012d6405 100644
--- a/lib/gitlab/incoming_email.rb
+++ b/lib/gitlab/incoming_email.rb
@@ -1,5 +1,6 @@
module Gitlab
module IncomingEmail
+ UNSUBSCRIBE_SUFFIX = '+unsubscribe'.freeze
WILDCARD_PLACEHOLDER = '%{key}'.freeze
class << self
@@ -18,7 +19,11 @@ module Gitlab
end
def reply_address(key)
- config.address.gsub(WILDCARD_PLACEHOLDER, key)
+ config.address.sub(WILDCARD_PLACEHOLDER, key)
+ end
+
+ def unsubscribe_address(key)
+ config.address.sub(WILDCARD_PLACEHOLDER, "#{key}#{UNSUBSCRIBE_SUFFIX}")
end
def key_from_address(address)
@@ -49,7 +54,7 @@ module Gitlab
return nil unless wildcard_address
regex = Regexp.escape(wildcard_address)
- regex = regex.gsub(Regexp.escape('%{key}'), "(.+)")
+ regex = regex.sub(Regexp.escape(WILDCARD_PLACEHOLDER), '(.+)')
Regexp.new(regex).freeze
end
end
diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb
index 6bdf3db9cb8..db325c00705 100644
--- a/lib/gitlab/project_search_results.rb
+++ b/lib/gitlab/project_search_results.rb
@@ -71,6 +71,14 @@ module Gitlab
)
end
+ def single_commit_result?
+ commits_count == 1 && total_result_count == 1
+ end
+
+ def total_result_count
+ issues_count + merge_requests_count + milestones_count + notes_count + blobs_count + wiki_blobs_count + commits_count
+ end
+
private
def blobs
@@ -114,7 +122,25 @@ module Gitlab
end
def commits
- @commits ||= project.repository.find_commits_by_message(query)
+ @commits ||= find_commits(query)
+ end
+
+ def find_commits(query)
+ return [] unless Ability.allowed?(@current_user, :download_code, @project)
+
+ commits = find_commits_by_message(query)
+ commit_by_sha = find_commit_by_sha(query)
+ commits |= [commit_by_sha] if commit_by_sha
+ commits
+ end
+
+ def find_commits_by_message(query)
+ project.repository.find_commits_by_message(query)
+ end
+
+ def find_commit_by_sha(query)
+ key = query.strip
+ project.repository.commit(key) if Commit.valid_hash?(key)
end
def project_ids_relation
diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb
index 35212992698..c9c65f76f4b 100644
--- a/lib/gitlab/search_results.rb
+++ b/lib/gitlab/search_results.rb
@@ -43,6 +43,10 @@ module Gitlab
@milestones_count ||= milestones.count
end
+ def single_commit_result?
+ false
+ end
+
private
def projects
diff --git a/lib/gitlab/user_access.rb b/lib/gitlab/user_access.rb
index 6c7e673fb9f..6ce9b229294 100644
--- a/lib/gitlab/user_access.rb
+++ b/lib/gitlab/user_access.rb
@@ -35,7 +35,9 @@ module Gitlab
return true if project.empty_repo? && project.user_can_push_to_empty_repo?(user)
access_levels = project.protected_branches.matching(ref).map(&:push_access_levels).flatten
- access_levels.any? { |access_level| access_level.check_access(user) }
+ has_access = access_levels.any? { |access_level| access_level.check_access(user) }
+
+ has_access || !project.repository.branch_exists?(ref) && can_merge_to_branch?(ref)
else
user.can?(:push_code, project)
end
diff --git a/lib/mattermost/client.rb b/lib/mattermost/client.rb
index ec2903b7ec6..e55c0d6ac49 100644
--- a/lib/mattermost/client.rb
+++ b/lib/mattermost/client.rb
@@ -8,21 +8,31 @@ module Mattermost
@user = user
end
- private
-
def with_session(&blk)
Mattermost::Session.new(user).with_session(&blk)
end
- def json_get(path, options = {})
+ private
+
+ # Should be used in a session manually
+ def get(session, path, options = {})
+ json_response session.get(path, options)
+ end
+
+ # Should be used in a session manually
+ def post(session, path, options = {})
+ json_response session.post(path, options)
+ end
+
+ def session_get(path, options = {})
with_session do |session|
- json_response session.get(path, options)
+ get(session, path, options)
end
end
- def json_post(path, options = {})
+ def session_post(path, options = {})
with_session do |session|
- json_response session.post(path, options)
+ post(session, path, options)
end
end
diff --git a/lib/mattermost/command.rb b/lib/mattermost/command.rb
index d1e4bb0eccf..33e450d7f0a 100644
--- a/lib/mattermost/command.rb
+++ b/lib/mattermost/command.rb
@@ -1,7 +1,7 @@
module Mattermost
class Command < Client
def create(params)
- response = json_post("/api/v3/teams/#{params[:team_id]}/commands/create",
+ response = session_post("/api/v3/teams/#{params[:team_id]}/commands/create",
body: params.to_json)
response['token']
diff --git a/lib/mattermost/team.rb b/lib/mattermost/team.rb
index 784eca6ab5a..09dfd082b3a 100644
--- a/lib/mattermost/team.rb
+++ b/lib/mattermost/team.rb
@@ -1,7 +1,7 @@
module Mattermost
class Team < Client
def all
- json_get('/api/v3/teams/all')
+ session_get('/api/v3/teams/all')
end
end
end