summaryrefslogtreecommitdiff
path: root/lib/api
diff options
context:
space:
mode:
Diffstat (limited to 'lib/api')
-rw-r--r--lib/api/access_requests.rb10
-rw-r--r--lib/api/award_emoji.rb5
-rw-r--r--lib/api/boards.rb12
-rw-r--r--lib/api/branches.rb40
-rw-r--r--lib/api/broadcast_messages.rb3
-rw-r--r--lib/api/commit_statuses.rb10
-rw-r--r--lib/api/deploy_keys.rb5
-rw-r--r--lib/api/deployments.rb2
-rw-r--r--lib/api/entities.rb56
-rw-r--r--lib/api/environments.rb5
-rw-r--r--lib/api/events.rb2
-rw-r--r--lib/api/files.rb14
-rw-r--r--lib/api/group_milestones.rb2
-rw-r--r--lib/api/group_variables.rb5
-rw-r--r--lib/api/groups.rb13
-rw-r--r--lib/api/helpers.rb21
-rw-r--r--lib/api/helpers/internal_helpers.rb4
-rw-r--r--lib/api/helpers/runner.rb2
-rw-r--r--lib/api/internal.rb17
-rw-r--r--lib/api/issues.rb37
-rw-r--r--lib/api/jobs.rb2
-rw-r--r--lib/api/labels.rb5
-rw-r--r--lib/api/members.rb10
-rw-r--r--lib/api/merge_request_diffs.rb2
-rw-r--r--lib/api/merge_requests.rb9
-rw-r--r--lib/api/notes.rb8
-rw-r--r--lib/api/notification_settings.rb2
-rw-r--r--lib/api/pipeline_schedules.rb86
-rw-r--r--lib/api/pipelines.rb2
-rw-r--r--lib/api/project_hooks.rb5
-rw-r--r--lib/api/project_milestones.rb2
-rw-r--r--lib/api/project_snippets.rb6
-rw-r--r--lib/api/projects.rb16
-rw-r--r--lib/api/protected_branches.rb4
-rw-r--r--lib/api/repositories.rb2
-rw-r--r--lib/api/runner.rb12
-rw-r--r--lib/api/runners.rb13
-rw-r--r--lib/api/services.rb16
-rw-r--r--lib/api/settings.rb7
-rw-r--r--lib/api/snippets.rb3
-rw-r--r--lib/api/subscriptions.rb2
-rw-r--r--lib/api/system_hooks.rb3
-rw-r--r--lib/api/tags.rb15
-rw-r--r--lib/api/todos.rb2
-rw-r--r--lib/api/triggers.rb5
-rw-r--r--lib/api/users.rb174
-rw-r--r--lib/api/v3/triggers.rb32
-rw-r--r--lib/api/variables.rb3
48 files changed, 538 insertions, 175 deletions
diff --git a/lib/api/access_requests.rb b/lib/api/access_requests.rb
index cdacf9839e5..374b611f55e 100644
--- a/lib/api/access_requests.rb
+++ b/lib/api/access_requests.rb
@@ -10,7 +10,7 @@ module API
params do
requires :id, type: String, desc: "The #{source_type} ID"
end
- resource source_type.pluralize, requirements: { id: %r{[^/]+} } do
+ resource source_type.pluralize, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc "Gets a list of access requests for a #{source_type}." do
detail 'This feature was introduced in GitLab 8.11.'
success Entities::AccessRequester
@@ -67,10 +67,12 @@ module API
end
delete ":id/access_requests/:user_id" do
source = find_source(source_type, params[:id])
+ member = source.requesters.find_by!(user_id: params[:user_id])
- status 204
- ::Members::DestroyService.new(source, current_user, params)
- .execute(:requesters)
+ destroy_conditionally!(member) do
+ ::Members::DestroyService.new(source, current_user, params)
+ .execute(:requesters)
+ end
end
end
end
diff --git a/lib/api/award_emoji.rb b/lib/api/award_emoji.rb
index 5a028fc9d0b..c3d93996816 100644
--- a/lib/api/award_emoji.rb
+++ b/lib/api/award_emoji.rb
@@ -12,7 +12,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
AWARDABLES.each do |awardable_params|
awardable_string = awardable_params[:type].pluralize
awardable_id_string = "#{awardable_params[:type]}_#{awardable_params[:find_by]}"
@@ -88,8 +88,7 @@ module API
unauthorized! unless award.user == current_user || current_user.admin?
- status 204
- award.destroy
+ destroy_conditionally!(award)
end
end
end
diff --git a/lib/api/boards.rb b/lib/api/boards.rb
index 5a2d7a681e3..366b0dc9a6f 100644
--- a/lib/api/boards.rb
+++ b/lib/api/boards.rb
@@ -7,7 +7,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get all project boards' do
detail 'This feature was introduced in 8.13'
success Entities::Board
@@ -122,13 +122,13 @@ module API
end
delete "/lists/:list_id" do
authorize!(:admin_list, user_project)
-
list = board_lists.find(params[:list_id])
- service = ::Boards::Lists::DestroyService.new(user_project, current_user)
-
- unless service.execute(list)
- render_api_error!({ error: 'List could not be deleted!' }, 400)
+ destroy_conditionally!(list) do |list|
+ service = ::Boards::Lists::DestroyService.new(user_project, current_user)
+ unless service.execute(list)
+ render_api_error!({ error: 'List could not be deleted!' }, 400)
+ end
end
end
end
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index d3dbf941298..642c1140fcc 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -24,17 +24,22 @@ module API
present paginate(branches), with: Entities::RepoBranch, project: user_project
end
- desc 'Get a single branch' do
- success Entities::RepoBranch
- end
- params do
- requires :branch, type: String, desc: 'The name of the branch'
- end
- get ':id/repository/branches/:branch', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
- branch = user_project.repository.find_branch(params[:branch])
- not_found!("Branch") unless branch
+ resource ':id/repository/branches/:branch', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
+ desc 'Get a single branch' do
+ success Entities::RepoBranch
+ end
+ params do
+ requires :branch, type: String, desc: 'The name of the branch'
+ end
+ head do
+ user_project.repository.branch_exists?(params[:branch]) ? status(204) : status(404)
+ end
+ get do
+ branch = user_project.repository.find_branch(params[:branch])
+ not_found!('Branch') unless branch
- present branch, with: Entities::RepoBranch, project: user_project
+ present branch, with: Entities::RepoBranch, project: user_project
+ end
end
# Note: This API will be deprecated in favor of the protected branches API.
@@ -125,11 +130,18 @@ module API
delete ':id/repository/branches/:branch', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
authorize_push_project
- result = DeleteBranchService.new(user_project, current_user)
- .execute(params[:branch])
+ branch = user_project.repository.find_branch(params[:branch])
+ not_found!('Branch') unless branch
+
+ commit = user_project.repository.commit(branch.dereferenced_target)
+
+ destroy_conditionally!(commit, last_updated: commit.authored_date) do
+ result = DeleteBranchService.new(user_project, current_user)
+ .execute(params[:branch])
- if result[:status] != :success
- render_api_error!(result[:message], result[:return_code])
+ if result[:status] != :success
+ render_api_error!(result[:message], result[:return_code])
+ end
end
end
diff --git a/lib/api/broadcast_messages.rb b/lib/api/broadcast_messages.rb
index 9980aec4752..0b45621ce7b 100644
--- a/lib/api/broadcast_messages.rb
+++ b/lib/api/broadcast_messages.rb
@@ -91,8 +91,7 @@ module API
delete ':id' do
message = find_message
- status 204
- message.destroy
+ destroy_conditionally!(message)
end
end
end
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index 485b680cd5f..829eef18795 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -5,7 +5,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
include PaginationParams
before { authenticate! }
@@ -74,7 +74,8 @@ module API
source: :external,
sha: commit.sha,
ref: ref,
- user: current_user)
+ user: current_user,
+ protected: @project.protected_for?(ref))
end
status = GenericCommitStatus.running_or_pending.find_or_initialize_by(
@@ -82,7 +83,8 @@ module API
pipeline: pipeline,
name: name,
ref: ref,
- user: current_user
+ user: current_user,
+ protected: @project.protected_for?(ref)
)
optional_attributes =
@@ -101,7 +103,7 @@ module API
when 'success'
status.success!
when 'failed'
- status.drop!
+ status.drop!(:api_failure)
when 'canceled'
status.cancel!
else
diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb
index 42e7c1486b0..281269b1190 100644
--- a/lib/api/deploy_keys.rb
+++ b/lib/api/deploy_keys.rb
@@ -17,7 +17,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of the project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
before { authorize_admin_project }
desc "Get a specific project's deploy keys" do
@@ -125,8 +125,7 @@ module API
key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id])
not_found!('Deploy Key') unless key
- status 204
- key.destroy
+ destroy_conditionally!(key)
end
end
end
diff --git a/lib/api/deployments.rb b/lib/api/deployments.rb
index 46b936897f6..1efee9a1324 100644
--- a/lib/api/deployments.rb
+++ b/lib/api/deployments.rb
@@ -8,7 +8,7 @@ module API
params do
requires :id, type: String, desc: 'The project ID'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get all deployments of the project' do
detail 'This feature was introduced in GitLab 8.11.'
success Entities::Deployment
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index e8dd61e493f..031dd02c6eb 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -1,11 +1,11 @@
module API
module Entities
class UserSafe < Grape::Entity
- expose :name, :username
+ expose :id, :name, :username
end
class UserBasic < UserSafe
- expose :id, :state
+ expose :state
expose :avatar_url do |user, options|
user.avatar_url(only_path: false)
end
@@ -320,7 +320,10 @@ module API
end
class IssueBasic < ProjectEntity
- expose :label_names, as: :labels
+ expose :labels do |issue, options|
+ # Avoids an N+1 query since labels are preloaded
+ issue.labels.map(&:title).sort
+ end
expose :milestone, using: Entities::Milestone
expose :assignees, :author, using: Entities::UserBasic
@@ -329,13 +332,32 @@ module API
end
expose :user_notes_count
- expose :upvotes, :downvotes
+ expose :upvotes do |issue, options|
+ if options[:issuable_metadata]
+ # Avoids an N+1 query when metadata is included
+ options[:issuable_metadata][issue.id].upvotes
+ else
+ issue.upvotes
+ end
+ end
+ expose :downvotes do |issue, options|
+ if options[:issuable_metadata]
+ # Avoids an N+1 query when metadata is included
+ options[:issuable_metadata][issue.id].downvotes
+ else
+ issue.downvotes
+ end
+ end
expose :due_date
expose :confidential
expose :web_url do |issue, options|
Gitlab::UrlBuilder.build(issue)
end
+
+ expose :time_stats, using: 'API::Entities::IssuableTimeStats' do |issue|
+ issue
+ end
end
class Issue < IssueBasic
@@ -365,10 +387,22 @@ module API
end
class IssuableTimeStats < Grape::Entity
+ format_with(:time_tracking_formatter) do |time_spent|
+ Gitlab::TimeTrackingFormatter.output(time_spent)
+ end
+
expose :time_estimate
expose :total_time_spent
expose :human_time_estimate
- expose :human_total_time_spent
+
+ with_options(format_with: :time_tracking_formatter) do
+ expose :total_time_spent, as: :human_total_time_spent
+ end
+
+ def total_time_spent
+ # Avoids an N+1 query since timelogs are preloaded
+ object.timelogs.map(&:time_spent).sum
+ end
end
class ExternalIssue < Grape::Entity
@@ -418,6 +452,10 @@ module API
expose :web_url do |merge_request, options|
Gitlab::UrlBuilder.build(merge_request)
end
+
+ expose :time_stats, using: 'API::Entities::IssuableTimeStats' do |merge_request|
+ merge_request
+ end
end
class MergeRequest < MergeRequestBasic
@@ -453,6 +491,10 @@ module API
expose :user, using: Entities::UserPublic
end
+ class GPGKey < Grape::Entity
+ expose :id, :key, :created_at
+ end
+
class Note < Grape::Entity
# Only Issue and MergeRequest have iid
NOTEABLE_TYPES_WITH_IID = %w(Issue MergeRequest).freeze
@@ -737,6 +779,7 @@ module API
expose :tag_list
expose :run_untagged
expose :locked
+ expose :access_level
expose :version, :revision, :platform, :architecture
expose :contacted_at
expose :token, if: lambda { |runner, options| options[:current_user].admin? || !runner.is_shared? }
@@ -780,7 +823,7 @@ module API
class Variable < Grape::Entity
expose :key, :value
- expose :protected?, as: :protected
+ expose :protected?, as: :protected, if: -> (entity, _) { entity.respond_to?(:protected?) }
end
class Pipeline < PipelineBasic
@@ -801,6 +844,7 @@ module API
class PipelineScheduleDetails < PipelineSchedule
expose :last_pipeline, using: Entities::PipelineBasic
+ expose :variables, using: Entities::Variable
end
class EnvironmentBasic < Grape::Entity
diff --git a/lib/api/environments.rb b/lib/api/environments.rb
index c774a5c6685..5c63ec028d9 100644
--- a/lib/api/environments.rb
+++ b/lib/api/environments.rb
@@ -9,7 +9,7 @@ module API
params do
requires :id, type: String, desc: 'The project ID'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get all environments of the project' do
detail 'This feature was introduced in GitLab 8.11.'
success Entities::Environment
@@ -79,8 +79,7 @@ module API
environment = user_project.environments.find(params[:environment_id])
- status 204
- environment.destroy
+ destroy_conditionally!(environment)
end
desc 'Stops an existing environment' do
diff --git a/lib/api/events.rb b/lib/api/events.rb
index dabdf579119..b0713ff1d54 100644
--- a/lib/api/events.rb
+++ b/lib/api/events.rb
@@ -67,7 +67,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc "List a Project's visible events" do
success Entities::Event
end
diff --git a/lib/api/files.rb b/lib/api/files.rb
index e2ac7142bc4..1598d3c00b8 100644
--- a/lib/api/files.rb
+++ b/lib/api/files.rb
@@ -1,5 +1,7 @@
module API
class Files < Grape::API
+ FILE_ENDPOINT_REQUIREMENTS = API::PROJECT_ENDPOINT_REQUIREMENTS.merge(file_path: API::NO_SLASH_URL_PART_REGEX)
+
# Prevents returning plain/text responses for files with .txt extension
after_validation { content_type "application/json" }
@@ -58,13 +60,13 @@ module API
params do
requires :id, type: String, desc: 'The project ID'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: FILE_ENDPOINT_REQUIREMENTS do
desc 'Get raw file contents from the repository'
params do
requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb'
requires :ref, type: String, desc: 'The name of branch, tag commit'
end
- get ":id/repository/files/:file_path/raw" do
+ get ":id/repository/files/:file_path/raw", requirements: FILE_ENDPOINT_REQUIREMENTS do
assign_file_vars!
send_git_blob @repo, @blob
@@ -75,7 +77,7 @@ module API
requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb'
requires :ref, type: String, desc: 'The name of branch, tag or commit'
end
- get ":id/repository/files/:file_path", requirements: { file_path: /.+/ } do
+ get ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do
assign_file_vars!
{
@@ -95,7 +97,7 @@ module API
params do
use :extended_file_params
end
- post ":id/repository/files/:file_path", requirements: { file_path: /.+/ } do
+ post ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do
authorize! :push_code, user_project
file_params = declared_params(include_missing: false)
@@ -113,7 +115,7 @@ module API
params do
use :extended_file_params
end
- put ":id/repository/files/:file_path", requirements: { file_path: /.+/ } do
+ put ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do
authorize! :push_code, user_project
file_params = declared_params(include_missing: false)
@@ -137,7 +139,7 @@ module API
params do
use :simple_file_params
end
- delete ":id/repository/files/:file_path", requirements: { file_path: /.+/ } do
+ delete ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do
authorize! :push_code, user_project
file_params = declared_params(include_missing: false)
diff --git a/lib/api/group_milestones.rb b/lib/api/group_milestones.rb
index b85eb59dc0a..93fa0b95857 100644
--- a/lib/api/group_milestones.rb
+++ b/lib/api/group_milestones.rb
@@ -10,7 +10,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a group'
end
- resource :groups, requirements: { id: %r{[^/]+} } do
+ resource :groups, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get a list of group milestones' do
success Entities::Milestone
end
diff --git a/lib/api/group_variables.rb b/lib/api/group_variables.rb
index f64da4ab77b..92800ce6450 100644
--- a/lib/api/group_variables.rb
+++ b/lib/api/group_variables.rb
@@ -9,7 +9,7 @@ module API
requires :id, type: String, desc: 'The ID of a group'
end
- resource :groups, requirements: { id: %r{[^/]+} } do
+ resource :groups, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get group-level variables' do
success Entities::Variable
end
@@ -88,8 +88,7 @@ module API
variable = user_group.variables.find_by(key: params[:key])
not_found!('GroupVariable') unless variable
- status 204
- variable.destroy
+ destroy_conditionally!(variable)
end
end
end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index e56427304a6..31a918eda60 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -7,7 +7,11 @@ module API
helpers do
params :optional_params_ce do
optional :description, type: String, desc: 'The description of the group'
- optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the group'
+ optional :visibility, type: String,
+ values: Gitlab::VisibilityLevel.string_values,
+ default: Gitlab::VisibilityLevel.string_level(
+ Gitlab::CurrentSettings.current_application_settings.default_group_visibility),
+ desc: 'The visibility of the group'
optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group'
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
optional :share_with_group_lock, type: Boolean, desc: 'Prevent sharing a project with another group within this group'
@@ -85,7 +89,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a group'
end
- resource :groups, requirements: { id: %r{[^/]+} } do
+ resource :groups, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Update a group. Available only for users who can administrate groups.' do
success Entities::Group
end
@@ -118,8 +122,9 @@ module API
group = find_group!(params[:id])
authorize! :admin_group, group
- status 204
- ::Groups::DestroyService.new(group, current_user).execute
+ destroy_conditionally!(group) do |group|
+ ::Groups::DestroyService.new(group, current_user).execute
+ end
end
desc 'Get a list of projects in this group.' do
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index b56fd2388b3..3d377fdb9eb 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -11,6 +11,27 @@ module API
declared(params, options).to_h.symbolize_keys
end
+ def check_unmodified_since!(last_modified)
+ if_unmodified_since = Time.parse(headers['If-Unmodified-Since']) rescue nil
+
+ if if_unmodified_since && last_modified && last_modified > if_unmodified_since
+ render_api_error!('412 Precondition Failed', 412)
+ end
+ end
+
+ def destroy_conditionally!(resource, last_updated: nil)
+ last_updated ||= resource.updated_at
+
+ check_unmodified_since!(last_updated)
+
+ status 204
+ if block_given?
+ yield resource
+ else
+ resource.destroy
+ end
+ end
+
def current_user
return @current_user if defined?(@current_user)
diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb
index ecb79317093..f57ff0f2632 100644
--- a/lib/api/helpers/internal_helpers.rb
+++ b/lib/api/helpers/internal_helpers.rb
@@ -42,6 +42,10 @@ module API
::Users::ActivityService.new(actor, 'Git SSH').execute if commands.include?(params[:action])
end
+ def merge_request_urls
+ ::MergeRequests::GetUrlsService.new(project).execute(params[:changes])
+ end
+
private
def set_project
diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb
index f8645e364ce..282af32ca94 100644
--- a/lib/api/helpers/runner.rb
+++ b/lib/api/helpers/runner.rb
@@ -1,6 +1,8 @@
module API
module Helpers
module Runner
+ include Gitlab::CurrentSettings
+
JOB_TOKEN_HEADER = 'HTTP_JOB_TOKEN'.freeze
JOB_TOKEN_PARAM = :token
UPDATE_RUNNER_EVERY = 10 * 60
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index 8b007869dc3..622bd9650e4 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -68,7 +68,7 @@ module API
end
get "/merge_request_urls" do
- ::MergeRequests::GetUrlsService.new(project).execute(params[:changes])
+ merge_request_urls
end
#
@@ -155,6 +155,21 @@ module API
# render_api_error!(e, 500)
# end
end
+
+ post '/post_receive' do
+ status 200
+
+ PostReceive.perform_async(params[:gl_repository], params[:identifier],
+ params[:changes])
+ broadcast_message = BroadcastMessage.current&.last&.message
+ reference_counter_decreased = Gitlab::ReferenceCounter.new(params[:gl_repository]).decrease
+
+ {
+ merge_request_urls: merge_request_urls,
+ broadcast_message: broadcast_message,
+ reference_counter_decreased: reference_counter_decreased
+ }
+ end
end
end
end
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 4cec1145f3a..1729df2aad0 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -4,6 +4,8 @@ module API
before { authenticate! }
+ helpers ::Gitlab::IssuableMetadata
+
helpers do
def find_issues(args = {})
args = params.merge(args)
@@ -13,6 +15,7 @@ module API
args[:label_name] = args.delete(:labels)
issues = IssuesFinder.new(current_user, args).execute
+ .preload(:assignees, :labels, :notes, :timelogs)
issues.reorder(args[:order_by] => args[:sort])
end
@@ -33,6 +36,7 @@ module API
optional :assignee_id, type: Integer, desc: 'Return issues which are assigned to the user with the given ID'
optional :scope, type: String, values: %w[created-by-me assigned-to-me all],
desc: 'Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all`'
+ optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji'
use :pagination
end
@@ -65,14 +69,20 @@ module API
get do
issues = find_issues
- present paginate(issues), with: Entities::IssueBasic, current_user: current_user
+ options = {
+ with: Entities::IssueBasic,
+ current_user: current_user,
+ issuable_metadata: issuable_meta_data(issues, 'Issue')
+ }
+
+ present paginate(issues), options
end
end
params do
requires :id, type: String, desc: 'The ID of a group'
end
- resource :groups, requirements: { id: %r{[^/]+} } do
+ resource :groups, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get a list of group issues' do
success Entities::IssueBasic
end
@@ -86,14 +96,20 @@ module API
issues = find_issues(group_id: group.id)
- present paginate(issues), with: Entities::IssueBasic, current_user: current_user
+ options = {
+ with: Entities::IssueBasic,
+ current_user: current_user,
+ issuable_metadata: issuable_meta_data(issues, 'Issue')
+ }
+
+ present paginate(issues), options
end
end
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
include TimeTrackingEndpoints
desc 'Get a list of project issues' do
@@ -109,7 +125,14 @@ module API
issues = find_issues(project_id: project.id)
- present paginate(issues), with: Entities::IssueBasic, current_user: current_user, project: user_project
+ options = {
+ with: Entities::IssueBasic,
+ current_user: current_user,
+ project: user_project,
+ issuable_metadata: issuable_meta_data(issues, 'Issue')
+ }
+
+ present paginate(issues), options
end
desc 'Get a single project issue' do
@@ -230,8 +253,8 @@ module API
not_found!('Issue') unless issue
authorize!(:destroy_issue, issue)
- status 204
- issue.destroy
+
+ destroy_conditionally!(issue)
end
desc 'List merge requests closing issue' do
diff --git a/lib/api/jobs.rb b/lib/api/jobs.rb
index a40018b214e..5bab96398fd 100644
--- a/lib/api/jobs.rb
+++ b/lib/api/jobs.rb
@@ -7,7 +7,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
helpers do
params :optional_scope do
optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show',
diff --git a/lib/api/labels.rb b/lib/api/labels.rb
index 4520c98d951..e41a1720ac1 100644
--- a/lib/api/labels.rb
+++ b/lib/api/labels.rb
@@ -7,7 +7,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get all labels of the project' do
success Entities::Label
end
@@ -56,8 +56,7 @@ module API
label = user_project.labels.find_by(title: params[:name])
not_found!('Label') unless label
- status 204
- label.destroy
+ destroy_conditionally!(label)
end
desc 'Update an existing label. At least one optional parameter is required.' do
diff --git a/lib/api/members.rb b/lib/api/members.rb
index bb970b7cd54..22e4bdead41 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -10,7 +10,7 @@ module API
params do
requires :id, type: String, desc: "The #{source_type} ID"
end
- resource source_type.pluralize, requirements: { id: %r{[^/]+} } do
+ resource source_type.pluralize, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Gets a list of group or project members viewable by the authenticated user.' do
success Entities::Member
end
@@ -93,11 +93,11 @@ module API
end
delete ":id/members/:user_id" do
source = find_source(source_type, params[:id])
- # Ensure that memeber exists
- source.members.find_by!(user_id: params[:user_id])
+ member = source.members.find_by!(user_id: params[:user_id])
- status 204
- ::Members::DestroyService.new(source, current_user, declared_params).execute
+ destroy_conditionally!(member) do
+ ::Members::DestroyService.new(source, current_user, declared_params).execute
+ end
end
end
end
diff --git a/lib/api/merge_request_diffs.rb b/lib/api/merge_request_diffs.rb
index 4b79eac2b8b..c3affcc6c6b 100644
--- a/lib/api/merge_request_diffs.rb
+++ b/lib/api/merge_request_diffs.rb
@@ -8,7 +8,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get a list of merge request diff versions' do
detail 'This feature was introduced in GitLab 8.12.'
success Entities::MergeRequestDiff
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 8810d4e441d..56d72d511da 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -21,7 +21,7 @@ module API
return merge_requests if args[:view] == 'simple'
merge_requests
- .preload(:notes, :author, :assignee, :milestone, :merge_request_diff, :labels)
+ .preload(:notes, :author, :assignee, :milestone, :merge_request_diff, :labels, :timelogs)
end
params :merge_requests_params do
@@ -40,6 +40,7 @@ module API
optional :assignee_id, type: Integer, desc: 'Return merge requests which are assigned to the user with the given ID'
optional :scope, type: String, values: %w[created-by-me assigned-to-me all],
desc: 'Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all`'
+ optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji'
use :pagination
end
end
@@ -72,7 +73,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
include TimeTrackingEndpoints
helpers do
@@ -164,8 +165,8 @@ module API
merge_request = find_project_merge_request(params[:merge_request_iid])
authorize!(:destroy_merge_request, merge_request)
- status 204
- merge_request.destroy
+
+ destroy_conditionally!(merge_request)
end
params do
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index 4e4e473994b..d6e7203adaf 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -9,7 +9,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
NOTEABLE_TYPES.each do |noteable_type|
noteables_str = noteable_type.to_s.underscore.pluralize
@@ -129,10 +129,12 @@ module API
end
delete ":id/#{noteables_str}/:noteable_id/notes/:note_id" do
note = user_project.notes.find(params[:note_id])
+
authorize! :admin_note, note
- status 204
- ::Notes::DestroyService.new(user_project, current_user).execute(note)
+ destroy_conditionally!(note) do |note|
+ ::Notes::DestroyService.new(user_project, current_user).execute(note)
+ end
end
end
end
diff --git a/lib/api/notification_settings.rb b/lib/api/notification_settings.rb
index 5d113c94b22..bcc0833aa5c 100644
--- a/lib/api/notification_settings.rb
+++ b/lib/api/notification_settings.rb
@@ -54,7 +54,7 @@ module API
params do
requires :id, type: String, desc: "The #{source_type} ID"
end
- resource source_type.pluralize, requirements: { id: %r{[^/]+} } do
+ resource source_type.pluralize, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc "Get #{source_type} level notification level settings, defaults to Global" do
detail 'This feature was introduced in GitLab 8.12'
success Entities::NotificationSetting
diff --git a/lib/api/pipeline_schedules.rb b/lib/api/pipeline_schedules.rb
index dbeaf9e17ef..37f32411296 100644
--- a/lib/api/pipeline_schedules.rb
+++ b/lib/api/pipeline_schedules.rb
@@ -7,7 +7,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get all pipeline schedules' do
success Entities::PipelineSchedule
end
@@ -31,10 +31,6 @@ module API
requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
end
get ':id/pipeline_schedules/:pipeline_schedule_id' do
- authorize! :read_pipeline_schedule, user_project
-
- not_found!('PipelineSchedule') unless pipeline_schedule
-
present pipeline_schedule, with: Entities::PipelineScheduleDetails
end
@@ -74,9 +70,6 @@ module API
optional :active, type: Boolean, desc: 'The activation of pipeline schedule'
end
put ':id/pipeline_schedules/:pipeline_schedule_id' do
- authorize! :read_pipeline_schedule, user_project
-
- not_found!('PipelineSchedule') unless pipeline_schedule
authorize! :update_pipeline_schedule, pipeline_schedule
if pipeline_schedule.update(declared_params(include_missing: false))
@@ -93,9 +86,6 @@ module API
requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
end
post ':id/pipeline_schedules/:pipeline_schedule_id/take_ownership' do
- authorize! :read_pipeline_schedule, user_project
-
- not_found!('PipelineSchedule') unless pipeline_schedule
authorize! :update_pipeline_schedule, pipeline_schedule
if pipeline_schedule.own!(current_user)
@@ -112,22 +102,84 @@ module API
requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
end
delete ':id/pipeline_schedules/:pipeline_schedule_id' do
- authorize! :read_pipeline_schedule, user_project
+ authorize! :admin_pipeline_schedule, pipeline_schedule
+
+ destroy_conditionally!(pipeline_schedule)
+ end
+
+ desc 'Create a new pipeline schedule variable' do
+ success Entities::Variable
+ end
+ params do
+ requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
+ requires :key, type: String, desc: 'The key of the variable'
+ requires :value, type: String, desc: 'The value of the variable'
+ end
+ post ':id/pipeline_schedules/:pipeline_schedule_id/variables' do
+ authorize! :update_pipeline_schedule, pipeline_schedule
- not_found!('PipelineSchedule') unless pipeline_schedule
+ variable_params = declared_params(include_missing: false)
+ variable = pipeline_schedule.variables.create(variable_params)
+ if variable.persisted?
+ present variable, with: Entities::Variable
+ else
+ render_validation_error!(variable)
+ end
+ end
+
+ desc 'Edit a pipeline schedule variable' do
+ success Entities::Variable
+ end
+ params do
+ requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
+ requires :key, type: String, desc: 'The key of the variable'
+ optional :value, type: String, desc: 'The value of the variable'
+ end
+ put ':id/pipeline_schedules/:pipeline_schedule_id/variables/:key' do
+ authorize! :update_pipeline_schedule, pipeline_schedule
+
+ if pipeline_schedule_variable.update(declared_params(include_missing: false))
+ present pipeline_schedule_variable, with: Entities::Variable
+ else
+ render_validation_error!(pipeline_schedule_variable)
+ end
+ end
+
+ desc 'Delete a pipeline schedule variable' do
+ success Entities::Variable
+ end
+ params do
+ requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
+ requires :key, type: String, desc: 'The key of the variable'
+ end
+ delete ':id/pipeline_schedules/:pipeline_schedule_id/variables/:key' do
authorize! :admin_pipeline_schedule, pipeline_schedule
status :accepted
- present pipeline_schedule.destroy, with: Entities::PipelineScheduleDetails
+ present pipeline_schedule_variable.destroy, with: Entities::Variable
end
end
helpers do
def pipeline_schedule
@pipeline_schedule ||=
- user_project.pipeline_schedules
- .preload(:owner, :last_pipeline)
- .find_by(id: params.delete(:pipeline_schedule_id))
+ user_project
+ .pipeline_schedules
+ .preload(:owner, :last_pipeline)
+ .find_by(id: params.delete(:pipeline_schedule_id)).tap do |pipeline_schedule|
+ unless can?(current_user, :read_pipeline_schedule, pipeline_schedule)
+ not_found!('Pipeline Schedule')
+ end
+ end
+ end
+
+ def pipeline_schedule_variable
+ @pipeline_schedule_variable ||=
+ pipeline_schedule.variables.find_by(key: params[:key]).tap do |pipeline_schedule_variable|
+ unless pipeline_schedule_variable
+ not_found!('Pipeline Schedule Variable')
+ end
+ end
end
end
end
diff --git a/lib/api/pipelines.rb b/lib/api/pipelines.rb
index e505cae3992..74b3376a1f3 100644
--- a/lib/api/pipelines.rb
+++ b/lib/api/pipelines.rb
@@ -7,7 +7,7 @@ module API
params do
requires :id, type: String, desc: 'The project ID'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get all Pipelines of the project' do
detail 'This feature was introduced in GitLab 8.11.'
success Entities::PipelineBasic
diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb
index 649dd891f56..86066e2b58f 100644
--- a/lib/api/project_hooks.rb
+++ b/lib/api/project_hooks.rb
@@ -24,7 +24,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get project hooks' do
success Entities::ProjectHook
end
@@ -96,8 +96,7 @@ module API
delete ":id/hooks/:hook_id" do
hook = user_project.hooks.find(params.delete(:hook_id))
- status 204
- hook.destroy
+ destroy_conditionally!(hook)
end
end
end
diff --git a/lib/api/project_milestones.rb b/lib/api/project_milestones.rb
index 451998c726a..0cb209a02d0 100644
--- a/lib/api/project_milestones.rb
+++ b/lib/api/project_milestones.rb
@@ -10,7 +10,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get a list of project milestones' do
success Entities::Milestone
end
diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb
index f3d905b0068..2ccda1c1aa1 100644
--- a/lib/api/project_snippets.rb
+++ b/lib/api/project_snippets.rb
@@ -7,7 +7,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
helpers do
def handle_project_member_errors(errors)
if errors[:project_access].any?
@@ -116,8 +116,8 @@ module API
not_found!('Snippet') unless snippet
authorize! :admin_project_snippet, snippet
- status 204
- snippet.destroy
+
+ destroy_conditionally!(snippet)
end
desc 'Get a raw project snippet'
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 15c3832b032..4845242a173 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -1,7 +1,6 @@
require_dependency 'declarative_policy'
module API
- # Projects API
class Projects < Grape::API
include PaginationParams
@@ -96,7 +95,7 @@ module API
end
end
- resource :users, requirements: { user_id: %r{[^/]+} } do
+ resource :users, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get a user projects' do
success Entities::BasicProjectDetails
end
@@ -184,7 +183,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get a single project' do
success Entities::ProjectWithAccess
end
@@ -334,7 +333,10 @@ module API
desc 'Remove a project'
delete ":id" do
authorize! :remove_project, user_project
- ::Projects::DestroyService.new(user_project, current_user, {}).async_execute
+
+ destroy_conditionally!(user_project) do
+ ::Projects::DestroyService.new(user_project, current_user, {}).async_execute
+ end
accepted!
end
@@ -363,8 +365,7 @@ module API
authorize! :remove_fork_project, user_project
if user_project.forked?
- status 204
- user_project.forked_project_link.destroy
+ destroy_conditionally!(user_project.forked_project_link)
else
not_modified!
end
@@ -408,8 +409,7 @@ module API
link = user_project.project_group_links.find_by(group_id: params[:group_id])
not_found!('Group Link') unless link
- status 204
- link.destroy
+ destroy_conditionally!(link)
end
desc 'Upload a file'
diff --git a/lib/api/protected_branches.rb b/lib/api/protected_branches.rb
index dccf4fa27a7..15fcb9e8e27 100644
--- a/lib/api/protected_branches.rb
+++ b/lib/api/protected_branches.rb
@@ -76,9 +76,7 @@ module API
delete ':id/protected_branches/:name', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
protected_branch = user_project.protected_branches.find_by!(name: params[:name])
- protected_branch.destroy
-
- status 204
+ destroy_conditionally!(protected_branch)
end
end
end
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 14d2bff9cb5..2255fb1b70d 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -9,7 +9,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
helpers do
def handle_project_member_errors(errors)
if errors[:project_access].any?
diff --git a/lib/api/runner.rb b/lib/api/runner.rb
index 88fc62d33df..a3987c560dd 100644
--- a/lib/api/runner.rb
+++ b/lib/api/runner.rb
@@ -45,8 +45,10 @@ module API
end
delete '/' do
authenticate_runner!
- status 204
- Ci::Runner.find_by_token(params[:token]).destroy
+
+ runner = Ci::Runner.find_by_token(params[:token])
+
+ destroy_conditionally!(runner)
end
desc 'Validates authentication credentials' do
@@ -78,7 +80,7 @@ module API
no_content! unless current_runner.active?
update_runner_info
- if current_runner.is_runner_queue_value_latest?(params[:last_update])
+ if current_runner.runner_queue_value_latest?(params[:last_update])
header 'X-GitLab-Last-Update', params[:last_update]
Gitlab::Metrics.add_event(:build_not_found_cached)
return no_content!
@@ -112,6 +114,8 @@ module API
requires :id, type: Integer, desc: %q(Job's ID)
optional :trace, type: String, desc: %q(Job's full trace)
optional :state, type: String, desc: %q(Job's status: success, failed)
+ optional :failure_reason, type: String, values: CommitStatus.failure_reasons.keys,
+ desc: %q(Job's failure_reason)
end
put '/:id' do
job = authenticate_job!
@@ -125,7 +129,7 @@ module API
when 'success'
job.success
when 'failed'
- job.drop
+ job.drop(params[:failure_reason] || :unknown_failure)
end
end
diff --git a/lib/api/runners.rb b/lib/api/runners.rb
index 31f940fe96b..d3559ef71be 100644
--- a/lib/api/runners.rb
+++ b/lib/api/runners.rb
@@ -55,7 +55,9 @@ module API
optional :tag_list, type: Array[String], desc: 'The list of tags for a runner'
optional :run_untagged, type: Boolean, desc: 'Flag indicating the runner can execute untagged jobs'
optional :locked, type: Boolean, desc: 'Flag indicating the runner is locked'
- at_least_one_of :description, :active, :tag_list, :run_untagged, :locked
+ optional :access_level, type: String, values: Ci::Runner.access_levels.keys,
+ desc: 'The access_level of the runner'
+ at_least_one_of :description, :active, :tag_list, :run_untagged, :locked, :access_level
end
put ':id' do
runner = get_runner(params.delete(:id))
@@ -77,17 +79,17 @@ module API
end
delete ':id' do
runner = get_runner(params[:id])
+
authenticate_delete_runner!(runner)
- status 204
- runner.destroy!
+ destroy_conditionally!(runner)
end
end
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
before { authorize_admin_project }
desc 'Get runners available for project' do
@@ -135,8 +137,7 @@ module API
runner = runner_project.runner
forbidden!("Only one project associated with the runner. Please remove the runner instead") if runner.projects.count == 1
- status 204
- runner_project.destroy
+ destroy_conditionally!(runner_project)
end
end
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 843c05ae32e..2cbd0517dc3 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -601,7 +601,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
before { authenticate! }
before { authorize_admin_project }
@@ -656,12 +656,14 @@ module API
delete ":id/services/:service_slug" do
service = user_project.find_or_initialize_service(params[:service_slug].underscore)
- attrs = service_attributes(service).inject({}) do |hash, key|
- hash.merge!(key => nil)
- end
+ destroy_conditionally!(service) do
+ attrs = service_attributes(service).inject({}) do |hash, key|
+ hash.merge!(key => nil)
+ end
- unless service.update_attributes(attrs.merge(active: false))
- render_api_error!('400 Bad Request', 400)
+ unless service.update_attributes(attrs.merge(active: false))
+ render_api_error!('400 Bad Request', 400)
+ end
end
end
@@ -689,7 +691,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc "Trigger a slash command for #{service_slug}" do
detail 'Added in GitLab 8.13'
end
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index 667ba468ce6..851b226e9e5 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -122,6 +122,13 @@ module API
optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.'
optional :polling_interval_multiplier, type: BigDecimal, desc: 'Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling.'
+ ApplicationSetting::SUPPORTED_KEY_TYPES.each do |type|
+ optional :"#{type}_key_restriction",
+ type: Integer,
+ values: KeyRestrictionValidator.supported_key_restrictions(type),
+ desc: "Restrictions on the complexity of uploaded #{type.upcase} keys. A value of #{ApplicationSetting::FORBIDDEN_KEY_VALUE} disables all #{type.upcase} keys."
+ end
+
optional(*::ApplicationSettingsHelper.visible_attributes)
at_least_one_of(*::ApplicationSettingsHelper.visible_attributes)
end
diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb
index 35ece56c65c..00eb7c60f16 100644
--- a/lib/api/snippets.rb
+++ b/lib/api/snippets.rb
@@ -123,8 +123,7 @@ module API
authorize! :destroy_personal_snippet, snippet
- status 204
- snippet.destroy
+ destroy_conditionally!(snippet)
end
desc 'Get a raw snippet' do
diff --git a/lib/api/subscriptions.rb b/lib/api/subscriptions.rb
index 91567909998..b3e1e23031a 100644
--- a/lib/api/subscriptions.rb
+++ b/lib/api/subscriptions.rb
@@ -12,7 +12,7 @@ module API
requires :id, type: String, desc: 'The ID of a project'
requires :subscribable_id, type: String, desc: 'The ID of a resource'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
subscribable_types.each do |type, finder|
type_singularized = type.singularize
entity_class = Entities.const_get(type_singularized.camelcase)
diff --git a/lib/api/system_hooks.rb b/lib/api/system_hooks.rb
index c0179037440..6b6a03e3300 100644
--- a/lib/api/system_hooks.rb
+++ b/lib/api/system_hooks.rb
@@ -66,8 +66,7 @@ module API
hook = SystemHook.find_by(id: params[:id])
not_found!('System hook') unless hook
- status 204
- hook.destroy
+ destroy_conditionally!(hook)
end
end
end
diff --git a/lib/api/tags.rb b/lib/api/tags.rb
index 1333747cced..912415e3a7f 100644
--- a/lib/api/tags.rb
+++ b/lib/api/tags.rb
@@ -65,11 +65,18 @@ module API
delete ':id/repository/tags/:tag_name', requirements: TAG_ENDPOINT_REQUIREMENTS do
authorize_push_project
- result = ::Tags::DestroyService.new(user_project, current_user)
- .execute(params[:tag_name])
+ tag = user_project.repository.find_tag(params[:tag_name])
+ not_found!('Tag') unless tag
+
+ commit = user_project.repository.commit(tag.dereferenced_target)
+
+ destroy_conditionally!(commit, last_updated: commit.authored_date) do
+ result = ::Tags::DestroyService.new(user_project, current_user)
+ .execute(params[:tag_name])
- if result[:status] != :success
- render_api_error!(result[:message], result[:return_code])
+ if result[:status] != :success
+ render_api_error!(result[:message], result[:return_code])
+ end
end
end
diff --git a/lib/api/todos.rb b/lib/api/todos.rb
index 55191169dd4..ffccfebe752 100644
--- a/lib/api/todos.rb
+++ b/lib/api/todos.rb
@@ -12,7 +12,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
ISSUABLE_TYPES.each do |type, finder|
type_id_str = "#{type.singularize}_iid".to_sym
diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb
index edfdb63d183..dd6801664b1 100644
--- a/lib/api/triggers.rb
+++ b/lib/api/triggers.rb
@@ -5,7 +5,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Trigger a GitLab project pipeline' do
success Entities::Pipeline
end
@@ -140,8 +140,7 @@ module API
trigger = user_project.triggers.find(params.delete(:trigger_id))
return not_found!('Trigger') unless trigger
- status 204
- trigger.destroy
+ destroy_conditionally!(trigger)
end
end
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index e2019d6d512..1825c90a23b 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -230,10 +230,89 @@ module API
key = user.keys.find_by(id: params[:key_id])
not_found!('Key') unless key
+ destroy_conditionally!(key)
+ end
+
+ desc 'Add a GPG key to a specified user. Available only for admins.' do
+ detail 'This feature was added in GitLab 10.0'
+ success Entities::GPGKey
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ requires :key, type: String, desc: 'The new GPG key'
+ end
+ post ':id/gpg_keys' do
+ authenticated_as_admin!
+
+ user = User.find_by(id: params.delete(:id))
+ not_found!('User') unless user
+
+ key = user.gpg_keys.new(declared_params(include_missing: false))
+
+ if key.save
+ present key, with: Entities::GPGKey
+ else
+ render_validation_error!(key)
+ end
+ end
+
+ desc 'Get the GPG keys of a specified user. Available only for admins.' do
+ detail 'This feature was added in GitLab 10.0'
+ success Entities::GPGKey
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ use :pagination
+ end
+ get ':id/gpg_keys' do
+ authenticated_as_admin!
+
+ user = User.find_by(id: params[:id])
+ not_found!('User') unless user
+
+ present paginate(user.gpg_keys), with: Entities::GPGKey
+ end
+
+ desc 'Delete an existing GPG key from a specified user. Available only for admins.' do
+ detail 'This feature was added in GitLab 10.0'
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ requires :key_id, type: Integer, desc: 'The ID of the GPG key'
+ end
+ delete ':id/gpg_keys/:key_id' do
+ authenticated_as_admin!
+
+ user = User.find_by(id: params[:id])
+ not_found!('User') unless user
+
+ key = user.gpg_keys.find_by(id: params[:key_id])
+ not_found!('GPG Key') unless key
+
status 204
key.destroy
end
+ desc 'Revokes an existing GPG key from a specified user. Available only for admins.' do
+ detail 'This feature was added in GitLab 10.0'
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ requires :key_id, type: Integer, desc: 'The ID of the GPG key'
+ end
+ post ':id/gpg_keys/:key_id/revoke' do
+ authenticated_as_admin!
+
+ user = User.find_by(id: params[:id])
+ not_found!('User') unless user
+
+ key = user.gpg_keys.find_by(id: params[:key_id])
+ not_found!('GPG Key') unless key
+
+ key.revoke
+ status :accepted
+ end
+
desc 'Add an email address to a specified user. Available only for admins.' do
success Entities::Email
end
@@ -287,7 +366,11 @@ module API
email = user.emails.find_by(id: params[:email_id])
not_found!('Email') unless email
- Emails::DestroyService.new(user, email: email.email).execute
+ destroy_conditionally!(email) do |email|
+ Emails::DestroyService.new(current_user, email: email.email).execute
+ end
+
+ user.update_secondary_emails!
end
desc 'Delete a user. Available only for admins.' do
@@ -299,11 +382,13 @@ module API
end
delete ":id" do
authenticated_as_admin!
+
user = User.find_by(id: params[:id])
not_found!('User') unless user
- status 204
- user.delete_async(deleted_by: current_user, params: params)
+ destroy_conditionally!(user) do
+ user.delete_async(deleted_by: current_user, params: params)
+ end
end
desc 'Block a user. Available only for admins.'
@@ -403,8 +488,11 @@ module API
requires :impersonation_token_id, type: Integer, desc: 'The ID of the impersonation token'
end
delete ':impersonation_token_id' do
- status 204
- find_impersonation_token.revoke!
+ token = find_impersonation_token
+
+ destroy_conditionally!(token) do
+ token.revoke!
+ end
end
end
end
@@ -481,6 +569,75 @@ module API
key = current_user.keys.find_by(id: params[:key_id])
not_found!('Key') unless key
+ destroy_conditionally!(key)
+ end
+
+ desc "Get the currently authenticated user's GPG keys" do
+ detail 'This feature was added in GitLab 10.0'
+ success Entities::GPGKey
+ end
+ params do
+ use :pagination
+ end
+ get 'gpg_keys' do
+ present paginate(current_user.gpg_keys), with: Entities::GPGKey
+ end
+
+ desc 'Get a single GPG key owned by currently authenticated user' do
+ detail 'This feature was added in GitLab 10.0'
+ success Entities::GPGKey
+ end
+ params do
+ requires :key_id, type: Integer, desc: 'The ID of the GPG key'
+ end
+ get 'gpg_keys/:key_id' do
+ key = current_user.gpg_keys.find_by(id: params[:key_id])
+ not_found!('GPG Key') unless key
+
+ present key, with: Entities::GPGKey
+ end
+
+ desc 'Add a new GPG key to the currently authenticated user' do
+ detail 'This feature was added in GitLab 10.0'
+ success Entities::GPGKey
+ end
+ params do
+ requires :key, type: String, desc: 'The new GPG key'
+ end
+ post 'gpg_keys' do
+ key = current_user.gpg_keys.new(declared_params)
+
+ if key.save
+ present key, with: Entities::GPGKey
+ else
+ render_validation_error!(key)
+ end
+ end
+
+ desc 'Revoke a GPG key owned by currently authenticated user' do
+ detail 'This feature was added in GitLab 10.0'
+ end
+ params do
+ requires :key_id, type: Integer, desc: 'The ID of the GPG key'
+ end
+ post 'gpg_keys/:key_id/revoke' do
+ key = current_user.gpg_keys.find_by(id: params[:key_id])
+ not_found!('GPG Key') unless key
+
+ key.revoke
+ status :accepted
+ end
+
+ desc 'Delete a GPG key from the currently authenticated user' do
+ detail 'This feature was added in GitLab 10.0'
+ end
+ params do
+ requires :key_id, type: Integer, desc: 'The ID of the SSH key'
+ end
+ delete 'gpg_keys/:key_id' do
+ key = current_user.gpg_keys.find_by(id: params[:key_id])
+ not_found!('GPG Key') unless key
+
status 204
key.destroy
end
@@ -533,8 +690,11 @@ module API
email = current_user.emails.find_by(id: params[:email_id])
not_found!('Email') unless email
- status 204
- Emails::DestroyService.new(current_user, email: email.email).execute
+ destroy_conditionally!(email) do |email|
+ Emails::DestroyService.new(current_user, email: email.email).execute
+ end
+
+ current_user.update_secondary_emails!
end
desc 'Get a list of user activities'
diff --git a/lib/api/v3/triggers.rb b/lib/api/v3/triggers.rb
index e9d4c35307b..534911fde5c 100644
--- a/lib/api/v3/triggers.rb
+++ b/lib/api/v3/triggers.rb
@@ -16,25 +16,31 @@ module API
optional :variables, type: Hash, desc: 'The list of variables to be injected into build'
end
post ":id/(ref/:ref/)trigger/builds", requirements: { ref: /.+/ } do
- project = find_project(params[:id])
- trigger = Ci::Trigger.find_by_token(params[:token].to_s)
- not_found! unless project && trigger
- unauthorized! unless trigger.project == project
-
# validate variables
- variables = params[:variables].to_h
- unless variables.all? { |key, value| key.is_a?(String) && value.is_a?(String) }
+ params[:variables] = params[:variables].to_h
+ unless params[:variables].all? { |key, value| key.is_a?(String) && value.is_a?(String) }
render_api_error!('variables needs to be a map of key-valued strings', 400)
end
- # create request and trigger builds
- result = Ci::CreateTriggerRequestService.execute(project, trigger, params[:ref].to_s, variables)
- pipeline = result.pipeline
+ project = find_project(params[:id])
+ not_found! unless project
+
+ result = Ci::PipelineTriggerService.new(project, nil, params).execute
+ not_found! unless result
- if pipeline.persisted?
- present result.trigger_request, with: ::API::V3::Entities::TriggerRequest
+ if result[:http_status]
+ render_api_error!(result[:message], result[:http_status])
else
- render_validation_error!(pipeline)
+ pipeline = result[:pipeline]
+
+ # We switched to Ci::PipelineVariable from Ci::TriggerRequest.variables.
+ # Ci::TriggerRequest doesn't save variables anymore.
+ # Here is copying Ci::PipelineVariable to Ci::TriggerRequest.variables for presenting the variables.
+ # The same endpoint in v4 API pressents Pipeline instead of TriggerRequest, so it doesn't need such a process.
+ trigger_request = pipeline.trigger_requests.last
+ trigger_request.variables = params[:variables]
+
+ present trigger_request, with: ::API::V3::Entities::TriggerRequest
end
end
diff --git a/lib/api/variables.rb b/lib/api/variables.rb
index 7c0fdd3d1be..d08876ae1b9 100644
--- a/lib/api/variables.rb
+++ b/lib/api/variables.rb
@@ -9,7 +9,7 @@ module API
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get project variables' do
success Entities::Variable
end
@@ -88,6 +88,7 @@ module API
variable = user_project.variables.find_by(key: params[:key])
not_found!('Variable') unless variable
+ # Variables don't have any timestamp. Therfore, destroy unconditionally.
status 204
variable.destroy
end