summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLin Jen-Shin <godfat@godfat.org>2016-12-06 20:53:07 +0800
committerLin Jen-Shin <godfat@godfat.org>2016-12-06 20:53:07 +0800
commitd6287ab230d1507893895397e759eaf38377cb18 (patch)
tree76030a4c6599c04245b1d13ed062dd299a568a01 /lib
parent28102ec28e1ef3d3203db3d05aa89ab3da234e70 (diff)
parente27fa5bc14382abb4383ad5df2867f23bf9f1092 (diff)
downloadgitlab-ce-d6287ab230d1507893895397e759eaf38377cb18.tar.gz
Merge remote-tracking branch 'upstream/master' into feature/1376-allow-write-access-deploy-keys
* upstream/master: (488 commits) Merge branch 'issue_25064' into 'security' It's secret variables, not secure Fix dead links, add example of debug trace output, simplify titles Authorize users into imported GitLab project Document button secondary states. Update icons and color section Remove unused votes.scss Remove unused errors css Fixed MR widget content wrapping for XS viewports NIGNX -> Nginx Use pry-byebug instead byebug Fixed influence from other specs. Accept `issue new` as command to create an issue Update paranoia from 2.1.4 to 2.2.0. Use the pagination helper in the API Added changelog for #25221 Fixed top margin for Builds page status header information Satisfied eslint Fix compatibility with Internet Explorer 11 for merge requests change the date label to match the date used fix gfm doc typo about two spaces for next line transfer ...
Diffstat (limited to 'lib')
-rw-r--r--lib/api/access_requests.rb5
-rw-r--r--lib/api/award_emoji.rb5
-rw-r--r--lib/api/broadcast_messages.rb5
-rw-r--r--lib/api/builds.rb7
-rw-r--r--lib/api/commit_statuses.rb6
-rw-r--r--lib/api/commits.rb7
-rw-r--r--lib/api/deployments.rb5
-rw-r--r--lib/api/entities.rb9
-rw-r--r--lib/api/environments.rb5
-rw-r--r--lib/api/files.rb153
-rw-r--r--lib/api/groups.rb22
-rw-r--r--lib/api/helpers.rb71
-rw-r--r--lib/api/helpers/members_helpers.rb2
-rw-r--r--lib/api/issues.rb78
-rw-r--r--lib/api/members.rb6
-rw-r--r--lib/api/merge_requests.rb49
-rw-r--r--lib/api/milestones.rb10
-rw-r--r--lib/api/namespaces.rb4
-rw-r--r--lib/api/notes.rb4
-rw-r--r--lib/api/pagination_params.rb24
-rw-r--r--lib/api/pipelines.rb5
-rw-r--r--lib/api/project_hooks.rb12
-rw-r--r--lib/api/project_snippets.rb6
-rw-r--r--lib/api/projects.rb613
-rw-r--r--lib/api/runners.rb5
-rw-r--r--lib/api/services.rb2
-rw-r--r--lib/api/sidekiq_metrics.rb36
-rw-r--r--lib/api/todos.rb10
-rw-r--r--lib/api/triggers.rb7
-rw-r--r--lib/api/users.rb14
-rw-r--r--lib/api/variables.rb7
-rw-r--r--lib/backup/manager.rb18
-rw-r--r--lib/banzai/filter/abstract_reference_filter.rb23
-rw-r--r--lib/banzai/filter/commit_range_reference_filter.rb2
-rw-r--r--lib/banzai/filter/commit_reference_filter.rb2
-rw-r--r--lib/banzai/filter/label_reference_filter.rb54
-rw-r--r--lib/banzai/filter/milestone_reference_filter.rb20
-rw-r--r--lib/banzai/filter/table_of_contents_filter.rb8
-rw-r--r--lib/constraints/constrainer_helper.rb15
-rw-r--r--lib/constraints/group_url_constrainer.rb20
-rw-r--r--lib/constraints/project_url_constrainer.rb13
-rw-r--r--lib/constraints/user_url_constrainer.rb12
-rw-r--r--lib/email_template_interceptor.rb13
-rw-r--r--lib/gitlab/chat_commands/base_command.rb4
-rw-r--r--lib/gitlab/chat_commands/command.rb1
-rw-r--r--lib/gitlab/chat_commands/issue_command.rb6
-rw-r--r--lib/gitlab/chat_commands/issue_create.rb8
-rw-r--r--lib/gitlab/chat_commands/issue_search.rb17
-rw-r--r--lib/gitlab/chat_commands/issue_show.rb2
-rw-r--r--lib/gitlab/ci/config/entry/job.rb2
-rw-r--r--lib/gitlab/cycle_analytics/base_event.rb2
-rw-r--r--lib/gitlab/cycle_analytics/plan_event.rb2
-rw-r--r--lib/gitlab/database.rb7
-rw-r--r--lib/gitlab/diff/file_collection/merge_request_diff.rb6
-rw-r--r--lib/gitlab/ee_compat_check.rb8
-rw-r--r--lib/gitlab/git_access.rb4
-rw-r--r--lib/gitlab/git_access_wiki.rb8
-rw-r--r--lib/gitlab/github_import/branch_formatter.rb2
-rw-r--r--lib/gitlab/identifier.rb6
-rw-r--r--lib/gitlab/o_auth/user.rb2
-rw-r--r--lib/gitlab/regex.rb14
-rw-r--r--lib/gitlab/search_results.rb2
-rw-r--r--lib/tasks/gitlab/helpers.rake8
-rw-r--r--lib/tasks/gitlab/shell.rake42
-rw-r--r--lib/tasks/gitlab/task_helpers.rake140
-rw-r--r--lib/tasks/gitlab/task_helpers.rb190
-rw-r--r--lib/tasks/gitlab/workhorse.rake23
67 files changed, 982 insertions, 918 deletions
diff --git a/lib/api/access_requests.rb b/lib/api/access_requests.rb
index ed723b94cfd..789f45489eb 100644
--- a/lib/api/access_requests.rb
+++ b/lib/api/access_requests.rb
@@ -1,5 +1,7 @@
module API
class AccessRequests < Grape::API
+ include PaginationParams
+
before { authenticate! }
helpers ::API::Helpers::MembersHelpers
@@ -13,6 +15,9 @@ module API
detail 'This feature was introduced in GitLab 8.11.'
success Entities::AccessRequester
end
+ params do
+ use :pagination
+ end
get ":id/access_requests" do
source = find_source(source_type, params[:id])
diff --git a/lib/api/award_emoji.rb b/lib/api/award_emoji.rb
index e9ccba3b465..58a4df54bea 100644
--- a/lib/api/award_emoji.rb
+++ b/lib/api/award_emoji.rb
@@ -1,5 +1,7 @@
module API
class AwardEmoji < Grape::API
+ include PaginationParams
+
before { authenticate! }
AWARDABLES = %w[issue merge_request snippet]
@@ -21,6 +23,9 @@ module API
detail 'This feature was introduced in 8.9'
success Entities::AwardEmoji
end
+ params do
+ use :pagination
+ end
get endpoint do
if can_read_awardable?
awards = paginate(awardable.award_emoji)
diff --git a/lib/api/broadcast_messages.rb b/lib/api/broadcast_messages.rb
index b6281a7f0ac..1217002bf8e 100644
--- a/lib/api/broadcast_messages.rb
+++ b/lib/api/broadcast_messages.rb
@@ -1,5 +1,7 @@
module API
class BroadcastMessages < Grape::API
+ include PaginationParams
+
before { authenticate! }
before { authenticated_as_admin! }
@@ -15,8 +17,7 @@ module API
success Entities::BroadcastMessage
end
params do
- optional :page, type: Integer, desc: 'Current page number'
- optional :per_page, type: Integer, desc: 'Number of messages per page'
+ use :pagination
end
get do
messages = BroadcastMessage.all
diff --git a/lib/api/builds.rb b/lib/api/builds.rb
index 67adca6605f..af61be343be 100644
--- a/lib/api/builds.rb
+++ b/lib/api/builds.rb
@@ -1,6 +1,7 @@
module API
- # Projects builds API
class Builds < Grape::API
+ include PaginationParams
+
before { authenticate! }
params do
@@ -28,6 +29,7 @@ module API
end
params do
use :optional_scope
+ use :pagination
end
get ':id/builds' do
builds = user_project.builds.order('id DESC')
@@ -41,8 +43,9 @@ module API
success Entities::Build
end
params do
- requires :sha, type: String, desc: 'The SHA id of a commit'
+ requires :sha, type: String, desc: 'The SHA id of a commit'
use :optional_scope
+ use :pagination
end
get ':id/repository/commits/:sha/builds' do
authorize_read_builds!
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index f54d4f06627..4bbdf06a49c 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -1,9 +1,10 @@
require 'mime/types'
module API
- # Project commit statuses API
class CommitStatuses < Grape::API
resource :projects do
+ include PaginationParams
+
before { authenticate! }
desc "Get a commit's statuses" do
@@ -16,6 +17,7 @@ module API
optional :stage, type: String, desc: 'The stage'
optional :name, type: String, desc: 'The name'
optional :all, type: String, desc: 'Show all statuses, default: false'
+ use :pagination
end
get ':id/repository/commits/:sha/statuses' do
authorize!(:read_commit_status, user_project)
@@ -77,7 +79,7 @@ module API
)
begin
- case params[:state].to_s
+ case params[:state]
when 'pending'
status.enqueue!
when 'running'
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index f412e1da1bf..2670a2d413a 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -3,6 +3,8 @@ require 'mime/types'
module API
# Projects commits API
class Commits < Grape::API
+ include PaginationParams
+
before { authenticate! }
before { authorize! :download_code, user_project }
@@ -46,7 +48,7 @@ module API
requires :id, type: Integer, desc: 'The project ID'
requires :branch_name, type: String, desc: 'The name of branch'
requires :commit_message, type: String, desc: 'Commit message'
- requires :actions, type: Array, desc: 'Actions to perform in commit'
+ requires :actions, type: Array[Hash], desc: 'Actions to perform in commit'
optional :author_email, type: String, desc: 'Author email for commit'
optional :author_name, type: String, desc: 'Author name for commit'
end
@@ -107,9 +109,8 @@ module API
failure [[404, 'Not Found']]
end
params do
+ use :pagination
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
- optional :per_page, type: Integer, desc: 'The amount of items per page for paginaion'
- optional :page, type: Integer, desc: 'The page number for pagination'
end
get ':id/repository/commits/:sha/comments' do
commit = user_project.commit(params[:sha])
diff --git a/lib/api/deployments.rb b/lib/api/deployments.rb
index f782bcaf7e9..c5feb49b22f 100644
--- a/lib/api/deployments.rb
+++ b/lib/api/deployments.rb
@@ -1,6 +1,8 @@
module API
# Deployments RESTfull API endpoints
class Deployments < Grape::API
+ include PaginationParams
+
before { authenticate! }
params do
@@ -12,8 +14,7 @@ module API
success Entities::Deployment
end
params do
- optional :page, type: Integer, desc: 'Page number of the current request'
- optional :per_page, type: Integer, desc: 'Number of items per page'
+ use :pagination
end
get ':id/deployments' do
authorize! :read_deployment, user_project
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 4cf8a478f1d..66ef4792222 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -141,8 +141,12 @@ module API
options[:project].repository.commit(repo_branch.dereferenced_target)
end
+ expose :merged do |repo_branch, options|
+ options[:project].repository.merged_to_root_ref?(repo_branch.name)
+ end
+
expose :protected do |repo_branch, options|
- options[:project].protected_branch? repo_branch.name
+ options[:project].protected_branch?(repo_branch.name)
end
expose :developers_can_push do |repo_branch, options|
@@ -170,6 +174,7 @@ module API
class RepoCommit < Grape::Entity
expose :id, :short_id, :title, :author_name, :author_email, :created_at
+ expose :committer_name, :committer_email
expose :safe_message, as: :message
end
@@ -210,6 +215,7 @@ module API
class Milestone < ProjectEntity
expose :due_date
+ expose :start_date
end
class Issue < ProjectEntity
@@ -606,6 +612,7 @@ module API
expose :user, with: Entities::UserBasic
expose :created_at, :updated_at, :started_at, :finished_at, :committed_at
expose :duration
+ expose :coverage
end
class EnvironmentBasic < Grape::Entity
diff --git a/lib/api/environments.rb b/lib/api/environments.rb
index 00c901937b1..80bbd9bb6e4 100644
--- a/lib/api/environments.rb
+++ b/lib/api/environments.rb
@@ -1,6 +1,8 @@
module API
# Environments RESTfull API endpoints
class Environments < Grape::API
+ include PaginationParams
+
before { authenticate! }
params do
@@ -12,8 +14,7 @@ module API
success Entities::Environment
end
params do
- optional :page, type: Integer, desc: 'Page number of the current request'
- optional :per_page, type: Integer, desc: 'Number of items per page'
+ use :pagination
end
get ':id/environments' do
authorize! :read_environment, user_project
diff --git a/lib/api/files.rb b/lib/api/files.rb
index 96510e651a3..28f306e45f3 100644
--- a/lib/api/files.rb
+++ b/lib/api/files.rb
@@ -23,140 +23,107 @@ module API
branch_name: attrs[:branch_name]
}
end
+
+ params :simple_file_params do
+ requires :file_path, type: String, desc: 'The path to new file. Ex. lib/class.rb'
+ requires :branch_name, type: String, desc: 'The name of branch'
+ requires :commit_message, type: String, desc: 'Commit Message'
+ optional :author_email, type: String, desc: 'The email of the author'
+ optional :author_name, type: String, desc: 'The name of the author'
+ end
+
+ params :extended_file_params do
+ use :simple_file_params
+ requires :content, type: String, desc: 'File content'
+ optional :encoding, type: String, values: %w[base64], desc: 'File encoding'
+ end
end
+ params do
+ requires :id, type: String, desc: 'The project ID'
+ end
resource :projects do
- # Get file from repository
- # File content is Base64 encoded
- #
- # Parameters:
- # file_path (required) - The path to the file. Ex. lib/class.rb
- # ref (required) - The name of branch, tag or commit
- #
- # Example Request:
- # GET /projects/:id/repository/files
- #
- # Example response:
- # {
- # "file_name": "key.rb",
- # "file_path": "app/models/key.rb",
- # "size": 1476,
- # "encoding": "base64",
- # "content": "IyA9PSBTY2hlbWEgSW5mb3...",
- # "ref": "master",
- # "blob_id": "79f7bbd25901e8334750839545a9bd021f0e4c83",
- # "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50",
- # "last_commit_id": "570e7b2abdd848b95f2f578043fc23bd6f6fd24d",
- # }
- #
+ desc 'Get a file from repository'
+ params do
+ requires :file_path, type: String, desc: 'The path to the file. Ex. lib/class.rb'
+ requires :ref, type: String, desc: 'The name of branch, tag, or commit'
+ end
get ":id/repository/files" do
authorize! :download_code, user_project
- required_attributes! [:file_path, :ref]
- attrs = attributes_for_keys [:file_path, :ref]
- ref = attrs.delete(:ref)
- file_path = attrs.delete(:file_path)
-
- commit = user_project.commit(ref)
- not_found! 'Commit' unless commit
+ commit = user_project.commit(params[:ref])
+ not_found!('Commit') unless commit
repo = user_project.repository
- blob = repo.blob_at(commit.sha, file_path)
+ blob = repo.blob_at(commit.sha, params[:file_path])
+ not_found!('File') unless blob
- if blob
- blob.load_all_data!(repo)
- status(200)
+ blob.load_all_data!(repo)
+ status(200)
- {
- file_name: blob.name,
- file_path: blob.path,
- size: blob.size,
- encoding: "base64",
- content: Base64.strict_encode64(blob.data),
- ref: ref,
- blob_id: blob.id,
- commit_id: commit.id,
- last_commit_id: repo.last_commit_for_path(commit.sha, file_path).id
- }
- else
- not_found! 'File'
- end
+ {
+ file_name: blob.name,
+ file_path: blob.path,
+ size: blob.size,
+ encoding: "base64",
+ content: Base64.strict_encode64(blob.data),
+ ref: params[:ref],
+ blob_id: blob.id,
+ commit_id: commit.id,
+ last_commit_id: repo.last_commit_for_path(commit.sha, params[:file_path]).id
+ }
end
- # Create new file in repository
- #
- # Parameters:
- # file_path (required) - The path to new file. Ex. lib/class.rb
- # branch_name (required) - The name of branch
- # content (required) - File content
- # commit_message (required) - Commit message
- #
- # Example Request:
- # POST /projects/:id/repository/files
- #
+ desc 'Create new file in repository'
+ params do
+ use :extended_file_params
+ end
post ":id/repository/files" do
authorize! :push_code, user_project
- required_attributes! [:file_path, :branch_name, :content, :commit_message]
- attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message, :encoding, :author_email, :author_name]
- result = ::Files::CreateService.new(user_project, current_user, commit_params(attrs)).execute
+ file_params = declared_params(include_missing: false)
+ result = ::Files::CreateService.new(user_project, current_user, commit_params(file_params)).execute
if result[:status] == :success
status(201)
- commit_response(attrs)
+ commit_response(file_params)
else
render_api_error!(result[:message], 400)
end
end
- # Update existing file in repository
- #
- # Parameters:
- # file_path (optional) - The path to file. Ex. lib/class.rb
- # branch_name (required) - The name of branch
- # content (required) - File content
- # commit_message (required) - Commit message
- #
- # Example Request:
- # PUT /projects/:id/repository/files
- #
+ desc 'Update existing file in repository'
+ params do
+ use :extended_file_params
+ end
put ":id/repository/files" do
authorize! :push_code, user_project
- required_attributes! [:file_path, :branch_name, :content, :commit_message]
- attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message, :encoding, :author_email, :author_name]
- result = ::Files::UpdateService.new(user_project, current_user, commit_params(attrs)).execute
+ file_params = declared_params(include_missing: false)
+ result = ::Files::UpdateService.new(user_project, current_user, commit_params(file_params)).execute
if result[:status] == :success
status(200)
- commit_response(attrs)
+ commit_response(file_params)
else
http_status = result[:http_status] || 400
render_api_error!(result[:message], http_status)
end
end
- # Delete existing file in repository
- #
- # Parameters:
- # file_path (optional) - The path to file. Ex. lib/class.rb
- # branch_name (required) - The name of branch
- # content (required) - File content
- # commit_message (required) - Commit message
- #
- # Example Request:
- # DELETE /projects/:id/repository/files
- #
+ desc 'Delete an existing file in repository'
+ params do
+ use :simple_file_params
+ end
delete ":id/repository/files" do
authorize! :push_code, user_project
- required_attributes! [:file_path, :branch_name, :commit_message]
- attrs = attributes_for_keys [:file_path, :branch_name, :commit_message, :author_email, :author_name]
- result = ::Files::DeleteService.new(user_project, current_user, commit_params(attrs)).execute
+ file_params = declared_params(include_missing: false)
+ result = ::Files::DeleteService.new(user_project, current_user, commit_params(file_params)).execute
if result[:status] == :success
status(200)
- commit_response(attrs)
+ commit_response(file_params)
else
render_api_error!(result[:message], 400)
end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 48ad3b80ae0..fbf7513302b 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -1,5 +1,7 @@
module API
class Groups < Grape::API
+ include PaginationParams
+
before { authenticate! }
helpers do
@@ -21,6 +23,7 @@ module API
optional :search, type: String, desc: 'Search for a specific group'
optional :order_by, type: String, values: %w[name path], default: 'name', desc: 'Order by name or path'
optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
+ use :pagination
end
get do
groups = if current_user.admin
@@ -33,7 +36,7 @@ module API
groups = groups.search(params[:search]) if params[:search].present?
groups = groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present?
- groups = groups.reorder(params[:order_by] => params[:sort].to_sym)
+ groups = groups.reorder(params[:order_by] => params[:sort])
present paginate(groups), with: Entities::Group
end
@@ -41,6 +44,9 @@ module API
desc 'Get list of owned groups for authenticated user' do
success Entities::Group
end
+ params do
+ use :pagination
+ end
get '/owned' do
groups = current_user.owned_groups
present paginate(groups), with: Entities::Group, user: current_user
@@ -82,7 +88,7 @@ module API
:lfs_enabled, :request_access_enabled
end
put ':id' do
- group = find_group(params[:id])
+ group = find_group!(params[:id])
authorize! :admin_group, group
if ::Groups::UpdateService.new(group, current_user, declared_params(include_missing: false)).execute
@@ -96,13 +102,13 @@ module API
success Entities::GroupDetail
end
get ":id" do
- group = find_group(params[:id])
+ group = find_group!(params[:id])
present group, with: Entities::GroupDetail
end
desc 'Remove a group.'
delete ":id" do
- group = find_group(params[:id])
+ group = find_group!(params[:id])
authorize! :admin_group, group
DestroyGroupService.new(group, current_user).execute
end
@@ -110,11 +116,13 @@ module API
desc 'Get a list of projects in this group.' do
success Entities::Project
end
+ params do
+ use :pagination
+ end
get ":id/projects" do
- group = find_group(params[:id])
+ group = find_group!(params[:id])
projects = GroupProjectsFinder.new(group).execute(current_user)
- projects = paginate projects
- present projects, with: Entities::Project, user: current_user
+ present paginate(projects), with: Entities::Project, user: current_user
end
desc 'Transfer a project to the group namespace. Available only for admin.' do
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 2c593dbb4ea..7f94ede7940 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -68,7 +68,7 @@ module API
end
def user_project
- @project ||= find_project(params[:id])
+ @project ||= find_project!(params[:id])
end
def available_labels
@@ -76,7 +76,15 @@ module API
end
def find_project(id)
- project = Project.find_with_namespace(id) || Project.find_by(id: id)
+ if id =~ /^\d+$/
+ Project.find_by(id: id)
+ else
+ Project.find_with_namespace(id)
+ end
+ end
+
+ def find_project!(id)
+ project = find_project(id)
if can?(current_user, :read_project, project)
project
@@ -97,7 +105,15 @@ module API
end
def find_group(id)
- group = Group.find_by(path: id) || Group.find_by(id: id)
+ if id =~ /^\d+$/
+ Group.find_by(id: id)
+ else
+ Group.find_by(path: id)
+ end
+ end
+
+ def find_group!(id)
+ group = find_group(id)
if can?(current_user, :read_group, group)
group
@@ -112,9 +128,7 @@ module API
end
def find_project_issue(id)
- issue = user_project.issues.find(id)
- not_found! unless can?(current_user, :read_issue, issue)
- issue
+ IssuesFinder.new(current_user, project_id: user_project.id).find(id)
end
def paginate(relation)
@@ -127,6 +141,10 @@ module API
unauthorized! unless current_user
end
+ def authenticate_non_get!
+ authenticate! unless %w[GET HEAD].include?(route.route_method)
+ end
+
def authenticate_by_gitlab_shell_token!
input = params['secret_token'].try(:chomp)
unless Devise.secure_compare(secret_token, input)
@@ -135,6 +153,7 @@ module API
end
def authenticated_as_admin!
+ authenticate!
forbidden! unless current_user.is_admin?
end
@@ -182,20 +201,6 @@ module API
ActionController::Parameters.new(attrs).permit!
end
- # Helper method for validating all labels against its names
- def validate_label_params(params)
- errors = {}
-
- params[:labels].to_s.split(',').each do |label_name|
- label = available_labels.find_or_initialize_by(title: label_name.strip)
- next if label.valid?
-
- errors[label.title] = label.errors
- end
-
- errors
- end
-
# Checks the occurrences of datetime attributes, each attribute if present in the params hash must be in ISO 8601
# format (YYYY-MM-DDTHH:MM:SSZ) or a Bad Request error is invoked.
#
@@ -308,11 +313,6 @@ module API
# Projects helpers
def filter_projects(projects)
- # If the archived parameter is passed, limit results accordingly
- if params[:archived].present?
- projects = projects.where(archived: to_boolean(params[:archived]))
- end
-
if params[:search].present?
projects = projects.search(params[:search])
end
@@ -321,25 +321,8 @@ module API
projects = projects.search_by_visibility(params[:visibility])
end
- projects.reorder(project_order_by => project_sort)
- end
-
- def project_order_by
- order_fields = %w(id name path created_at updated_at last_activity_at)
-
- if order_fields.include?(params['order_by'])
- params['order_by']
- else
- 'created_at'
- end
- end
-
- def project_sort
- if params["sort"] == 'asc'
- :asc
- else
- :desc
- end
+ projects = projects.where(archived: params[:archived])
+ projects.reorder(params[:order_by] => params[:sort])
end
# file helpers
diff --git a/lib/api/helpers/members_helpers.rb b/lib/api/helpers/members_helpers.rb
index 90114f6f667..d9cae1501f8 100644
--- a/lib/api/helpers/members_helpers.rb
+++ b/lib/api/helpers/members_helpers.rb
@@ -2,7 +2,7 @@ module API
module Helpers
module MembersHelpers
def find_source(source_type, id)
- public_send("find_#{source_type}", id)
+ public_send("find_#{source_type}!", id)
end
def authorize_admin_source!(source_type, source)
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index eea5b91d4f9..049b4fb214c 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -19,6 +19,15 @@ module API
def filter_issues_milestone(issues, milestone)
issues.includes(:milestone).where('milestones.title' => milestone)
end
+
+ def issue_params
+ new_params = declared(params, include_parent_namespace: false, include_missing: false).to_h
+ new_params = new_params.with_indifferent_access
+ new_params.delete(:id)
+ new_params.delete(:issue_id)
+
+ new_params
+ end
end
resource :issues do
@@ -68,7 +77,7 @@ module API
# GET /groups/:id/issues?milestone=1.0.0
# GET /groups/:id/issues?milestone=1.0.0&state=closed
get ":id/issues" do
- group = find_group(params[:id])
+ group = find_group!(params[:id])
params[:state] ||= 'opened'
params[:group_id] = group.id
@@ -86,6 +95,10 @@ module API
end
end
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+
resource :projects do
# Get a list of project issues
#
@@ -109,7 +122,7 @@ module API
# GET /projects/:id/issues?milestone=1.0.0&state=closed
# GET /issues?iid=42
get ":id/issues" do
- issues = user_project.issues.inc_notes_with_associations.visible_to_user(current_user)
+ issues = IssuesFinder.new(current_user, project_id: user_project.id).execute.inc_notes_with_associations
issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
issues = filter_by_iid(issues, params[:iid]) unless params[:iid].nil?
@@ -152,17 +165,10 @@ module API
post ':id/issues' do
required_attributes! [:title]
- keys = [:title, :description, :assignee_id, :milestone_id, :due_date, :confidential]
+ keys = [:title, :description, :assignee_id, :milestone_id, :due_date, :confidential, :labels]
keys << :created_at if current_user.admin? || user_project.owner == current_user
attrs = attributes_for_keys(keys)
- # Validate label names in advance
- if (errors = validate_label_params(params)).any?
- render_api_error!({ labels: errors }, 400)
- end
-
- attrs[:labels] = params[:labels] if params[:labels]
-
# Convert and filter out invalid confidential flags
attrs['confidential'] = to_boolean(attrs['confidential'])
attrs.delete('confidential') if attrs['confidential'].nil?
@@ -180,41 +186,35 @@ module API
end
end
- # Update an existing issue
- #
- # Parameters:
- # id (required) - The ID of a project
- # issue_id (required) - The ID of a project issue
- # title (optional) - The title of an issue
- # description (optional) - The description of an issue
- # assignee_id (optional) - The ID of a user to assign issue
- # milestone_id (optional) - The ID of a milestone to assign issue
- # labels (optional) - The labels of an issue
- # state_event (optional) - The state event of an issue (close|reopen)
- # updated_at (optional) - Date time string, ISO 8601 formatted
- # due_date (optional) - Date time string in the format YEAR-MONTH-DAY
- # confidential (optional) - Boolean parameter if the issue should be confidential
- # Example Request:
- # PUT /projects/:id/issues/:issue_id
+ desc 'Update an existing issue' do
+ success Entities::Issue
+ end
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ requires :issue_id, type: Integer, desc: "The ID of a project issue"
+ optional :title, type: String, desc: 'The new title of the issue'
+ optional :description, type: String, desc: 'The description of an issue'
+ optional :assignee_id, type: Integer, desc: 'The ID of a user to assign issue'
+ optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign issue'
+ optional :labels, type: String, desc: 'The labels of an issue'
+ optional :state_event, type: String, values: ['close', 'reopen'], desc: 'The state event of an issue'
+ # TODO 9.0, use the Grape DateTime type here
+ optional :updated_at, type: String, desc: 'Date time string, ISO 8601 formatted'
+ optional :due_date, type: String, desc: 'Date time string in the format YEAR-MONTH-DAY'
+ # TODO 9.0, use the Grape boolean type here
+ optional :confidential, type: String, desc: 'Boolean parameter if the issue should be confidential'
+ end
put ':id/issues/:issue_id' do
issue = user_project.issues.find(params[:issue_id])
authorize! :update_issue, issue
- keys = [:title, :description, :assignee_id, :milestone_id, :state_event, :due_date, :confidential]
- keys << :updated_at if current_user.admin? || user_project.owner == current_user
- attrs = attributes_for_keys(keys)
-
- # Validate label names in advance
- if (errors = validate_label_params(params)).any?
- render_api_error!({ labels: errors }, 400)
- end
-
- attrs[:labels] = params[:labels] if params[:labels]
# Convert and filter out invalid confidential flags
- attrs['confidential'] = to_boolean(attrs['confidential'])
- attrs.delete('confidential') if attrs['confidential'].nil?
+ params[:confidential] = to_boolean(params[:confidential])
+ params.delete(:confidential) if params[:confidential].nil?
+
+ params.delete(:updated_at) unless current_user.admin? || user_project.owner == current_user
- issue = ::Issues::UpdateService.new(user_project, current_user, attrs).execute(issue)
+ issue = ::Issues::UpdateService.new(user_project, current_user, issue_params).execute(issue)
if issue.valid?
present issue, with: Entities::Issue, current_user: current_user, project: user_project
diff --git a/lib/api/members.rb b/lib/api/members.rb
index 2d4d5cedf20..d85f1f78cd6 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -1,5 +1,7 @@
module API
class Members < Grape::API
+ include PaginationParams
+
before { authenticate! }
helpers ::API::Helpers::MembersHelpers
@@ -14,15 +16,15 @@ module API
end
params do
optional :query, type: String, desc: 'A query string to search for members'
+ use :pagination
end
get ":id/members" do
source = find_source(source_type, params[:id])
users = source.users
users = users.merge(User.search(params[:query])) if params[:query]
- users = paginate(users)
- present users, with: Entities::Member, source: source
+ present paginate(users), with: Entities::Member, source: source
end
desc 'Gets a member of a group or project.' do
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 4176c7eec06..253460830ff 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -1,5 +1,7 @@
module API
class MergeRequests < Grape::API
+ include PaginationParams
+
DEPRECATION_MESSAGE = 'This endpoint is deprecated and will be removed in GitLab 9.0.'.freeze
before { authenticate! }
@@ -41,15 +43,14 @@ module API
desc: 'Return merge requests ordered by `created_at` or `updated_at` fields.'
optional :sort, type: String, values: %w[asc desc], default: 'desc',
desc: 'Return merge requests sorted in `asc` or `desc` order.'
- optional :iid, type: Integer, desc: 'The IID of the merge requests'
+ optional :iid, type: Array[Integer], desc: 'The IID of the merge requests'
+ use :pagination
end
get ":id/merge_requests" do
authorize! :read_merge_request, user_project
- merge_requests = user_project.merge_requests.inc_notes_with_associations
- unless params[:iid].nil?
- merge_requests = filter_by_iid(merge_requests, params[:iid])
- end
+ merge_requests = user_project.merge_requests.inc_notes_with_associations
+ merge_requests = filter_by_iid(merge_requests, params[:iid]) if params[:iid].present?
merge_requests =
case params[:state]
@@ -59,7 +60,7 @@ module API
else merge_requests
end
- merge_requests = merge_requests.reorder(issuable_order_by => issuable_sort)
+ merge_requests = merge_requests.reorder(params[:order_by] => params[:sort])
present paginate(merge_requests), with: Entities::MergeRequest, current_user: current_user, project: user_project
end
@@ -79,11 +80,6 @@ module API
mr_params = declared_params
- # Validate label names in advance
- if (errors = validate_label_params(mr_params)).any?
- render_api_error!({ labels: errors }, 400)
- end
-
merge_request = ::MergeRequests::CreateService.new(user_project, current_user, mr_params).execute
if merge_request.valid?
@@ -159,11 +155,6 @@ module API
mr_params = declared_params(include_missing: false)
- # Validate label names in advance
- if (errors = validate_label_params(mr_params)).any?
- render_api_error!({ labels: errors }, 400)
- end
-
merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, mr_params).execute(merge_request)
if merge_request.valid?
@@ -181,7 +172,7 @@ module API
optional :should_remove_source_branch, type: Boolean,
desc: 'When true, the source branch will be deleted if possible'
optional :merge_when_build_succeeds, type: Boolean,
- desc: 'When true, this merge request will be merged when the build succeeds'
+ desc: 'When true, this merge request will be merged when the pipeline succeeds'
optional :sha, type: String, desc: 'When present, must have the HEAD SHA of the source branch'
end
put "#{path}/merge" do
@@ -204,18 +195,20 @@ module API
should_remove_source_branch: params[:should_remove_source_branch]
}
- if params[:merge_when_build_succeeds] && merge_request.pipeline && merge_request.pipeline.active?
- ::MergeRequests::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user, merge_params).
- execute(merge_request)
+ if params[:merge_when_build_succeeds] && merge_request.head_pipeline && merge_request.head_pipeline.active?
+ ::MergeRequests::MergeWhenPipelineSucceedsService
+ .new(merge_request.target_project, current_user, merge_params)
+ .execute(merge_request)
else
- ::MergeRequests::MergeService.new(merge_request.target_project, current_user, merge_params).
- execute(merge_request)
+ ::MergeRequests::MergeService
+ .new(merge_request.target_project, current_user, merge_params)
+ .execute(merge_request)
end
present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
end
- desc 'Cancel merge if "Merge when build succeeds" is enabled' do
+ desc 'Cancel merge if "Merge When Pipeline Succeeds" is enabled' do
success Entities::MergeRequest
end
post "#{path}/cancel_merge_when_build_succeeds" do
@@ -223,13 +216,18 @@ module API
unauthorized! unless merge_request.can_cancel_merge_when_build_succeeds?(current_user)
- ::MergeRequest::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user).cancel(merge_request)
+ ::MergeRequest::MergeWhenPipelineSucceedsService
+ .new(merge_request.target_project, current_user)
+ .cancel(merge_request)
end
desc 'Get the comments of a merge request' do
detail 'Duplicate. DEPRECATED and WILL BE REMOVED in 9.0'
success Entities::MRNote
end
+ params do
+ use :pagination
+ end
get "#{path}/comments" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
@@ -267,6 +265,9 @@ module API
desc 'List issues that will be closed on merge' do
success Entities::MRNote
end
+ params do
+ use :pagination
+ end
get "#{path}/closes_issues" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
issues = ::Kaminari.paginate_array(merge_request.closes_issues(current_user))
diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb
index 937c118779d..3c373a84ec5 100644
--- a/lib/api/milestones.rb
+++ b/lib/api/milestones.rb
@@ -1,6 +1,7 @@
module API
- # Milestones API
class Milestones < Grape::API
+ include PaginationParams
+
before { authenticate! }
helpers do
@@ -14,7 +15,8 @@ module API
params :optional_params do
optional :description, type: String, desc: 'The description of the milestone'
- optional :due_date, type: String, desc: 'The due date of the milestone'
+ optional :due_date, type: String, desc: 'The due date of the milestone. The ISO 8601 date format (%Y-%m-%d)'
+ optional :start_date, type: String, desc: 'The start date of the milestone. The ISO 8601 date format (%Y-%m-%d)'
end
end
@@ -28,7 +30,8 @@ module API
params do
optional :state, type: String, values: %w[active closed all], default: 'all',
desc: 'Return "active", "closed", or "all" milestones'
- optional :iid, type: Integer, desc: 'The IID of the milestone'
+ optional :iid, type: Array[Integer], desc: 'The IID of the milestone'
+ use :pagination
end
get ":id/milestones" do
authorize! :read_milestone, user_project
@@ -102,6 +105,7 @@ module API
end
params do
requires :milestone_id, type: Integer, desc: 'The ID of a project milestone'
+ use :pagination
end
get ":id/milestones/:milestone_id/issues" do
authorize! :read_milestone, user_project
diff --git a/lib/api/namespaces.rb b/lib/api/namespaces.rb
index fe981d7b9fa..30761cb9b55 100644
--- a/lib/api/namespaces.rb
+++ b/lib/api/namespaces.rb
@@ -1,6 +1,7 @@
module API
- # namespaces API
class Namespaces < Grape::API
+ include PaginationParams
+
before { authenticate! }
resource :namespaces do
@@ -9,6 +10,7 @@ module API
end
params do
optional :search, type: String, desc: "Search query for namespaces"
+ use :pagination
end
get do
namespaces = current_user.admin ? Namespace.all : current_user.namespaces
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index b255b47742b..d0faf17714b 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -1,6 +1,7 @@
module API
- # Notes API
class Notes < Grape::API
+ include PaginationParams
+
before { authenticate! }
NOTEABLE_TYPES = [Issue, MergeRequest, Snippet]
@@ -17,6 +18,7 @@ module API
end
params do
requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
+ use :pagination
end
get ":id/#{noteables_str}/:noteable_id/notes" do
noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id])
diff --git a/lib/api/pagination_params.rb b/lib/api/pagination_params.rb
new file mode 100644
index 00000000000..8c1e4381a74
--- /dev/null
+++ b/lib/api/pagination_params.rb
@@ -0,0 +1,24 @@
+module API
+ # Concern for declare pagination params.
+ #
+ # @example
+ # class CustomApiResource < Grape::API
+ # include PaginationParams
+ #
+ # params do
+ # use :pagination
+ # end
+ # end
+ module PaginationParams
+ extend ActiveSupport::Concern
+
+ included do
+ helpers do
+ params :pagination do
+ optional :page, type: Integer, desc: 'Current page number'
+ optional :per_page, type: Integer, desc: 'Number of items per page'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/pipelines.rb b/lib/api/pipelines.rb
index e69b0569612..b634b1d0222 100644
--- a/lib/api/pipelines.rb
+++ b/lib/api/pipelines.rb
@@ -1,5 +1,7 @@
module API
class Pipelines < Grape::API
+ include PaginationParams
+
before { authenticate! }
params do
@@ -11,8 +13,7 @@ module API
success Entities::Pipeline
end
params do
- optional :page, type: Integer, desc: 'Page number of the current request'
- optional :per_page, type: Integer, desc: 'Number of items per page'
+ use :pagination
optional :scope, type: String, values: ['running', 'branches', 'tags'],
desc: 'Either running, branches, or tags'
end
diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb
index 2b36ef7c426..dcc0fb7a911 100644
--- a/lib/api/project_hooks.rb
+++ b/lib/api/project_hooks.rb
@@ -1,6 +1,10 @@
module API
- # Projects API
class ProjectHooks < Grape::API
+ include PaginationParams
+
+ before { authenticate! }
+ before { authorize_admin_project }
+
helpers do
params :project_hook_properties do
requires :url, type: String, desc: "The URL to send the request to"
@@ -17,9 +21,6 @@ module API
end
end
- before { authenticate! }
- before { authorize_admin_project }
-
params do
requires :id, type: String, desc: 'The ID of a project'
end
@@ -27,6 +28,9 @@ module API
desc 'Get project hooks' do
success Entities::ProjectHook
end
+ params do
+ use :pagination
+ end
get ":id/hooks" do
hooks = paginate user_project.hooks
diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb
index d0ee9c9a5b2..9d8c5b63685 100644
--- a/lib/api/project_snippets.rb
+++ b/lib/api/project_snippets.rb
@@ -1,6 +1,7 @@
module API
- # Projects API
class ProjectSnippets < Grape::API
+ include PaginationParams
+
before { authenticate! }
params do
@@ -24,6 +25,9 @@ module API
desc 'Get all project snippets' do
success Entities::ProjectSnippet
end
+ params do
+ use :pagination
+ end
get ":id/snippets" do
present paginate(snippets_for_current_user), with: Entities::ProjectSnippet
end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 6b856128c2e..2929d2157dc 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -1,293 +1,295 @@
module API
# Projects API
class Projects < Grape::API
- before { authenticate! }
+ include PaginationParams
+
+ before { authenticate_non_get! }
+
+ helpers do
+ params :optional_params do
+ optional :description, type: String, desc: 'The description of the project'
+ optional :issues_enabled, type: Boolean, desc: 'Flag indication if the issue tracker is enabled'
+ optional :merge_requests_enabled, type: Boolean, desc: 'Flag indication if merge requests are enabled'
+ optional :wiki_enabled, type: Boolean, desc: 'Flag indication if the wiki is enabled'
+ optional :builds_enabled, type: Boolean, desc: 'Flag indication if builds are enabled'
+ optional :snippets_enabled, type: Boolean, desc: 'Flag indication if snippets are enabled'
+ optional :shared_runners_enabled, type: Boolean, desc: 'Flag indication if shared runners are enabled for that project'
+ optional :container_registry_enabled, type: Boolean, desc: 'Flag indication if the container registry is enabled for that project'
+ optional :lfs_enabled, type: Boolean, desc: 'Flag indication if Git LFS is enabled for that project'
+ optional :public, type: Boolean, desc: 'Create a public project. The same as visibility_level = 20.'
+ optional :visibility_level, type: Integer, values: [
+ Gitlab::VisibilityLevel::PRIVATE,
+ Gitlab::VisibilityLevel::INTERNAL,
+ Gitlab::VisibilityLevel::PUBLIC ], desc: 'Create a public project. The same as visibility_level = 20.'
+ optional :public_builds, type: Boolean, desc: 'Perform public builds'
+ optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
+ optional :only_allow_merge_if_build_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed'
+ optional :only_allow_merge_if_all_discussions_are_resolved, type: Boolean, desc: 'Only allow to merge if all discussions are resolved'
+ end
- resource :projects, requirements: { id: /[^\/]+/ } do
- helpers do
- def map_public_to_visibility_level(attrs)
- publik = attrs.delete(:public)
- if publik.present? && !attrs[:visibility_level].present?
- publik = to_boolean(publik)
- # Since setting the public attribute to private could mean either
- # private or internal, use the more conservative option, private.
- attrs[:visibility_level] = (publik == true) ? Gitlab::VisibilityLevel::PUBLIC : Gitlab::VisibilityLevel::PRIVATE
- end
- attrs
+ def map_public_to_visibility_level(attrs)
+ publik = attrs.delete(:public)
+ if !publik.nil? && !attrs[:visibility_level].present?
+ # Since setting the public attribute to private could mean either
+ # private or internal, use the more conservative option, private.
+ attrs[:visibility_level] = (publik == true) ? Gitlab::VisibilityLevel::PUBLIC : Gitlab::VisibilityLevel::PRIVATE
end
+ attrs
end
+ end
- # Get a projects list for authenticated user
- #
- # Example Request:
- # GET /projects
- get do
- projects = current_user.authorized_projects
- projects = filter_projects(projects)
- projects = paginate projects
- entity = params[:simple] ? Entities::BasicProjectDetails : Entities::ProjectWithAccess
+ resource :projects do
+ helpers do
+ params :sort_params do
+ optional :order_by, type: String, values: %w[id name path created_at updated_at last_activity_at],
+ default: 'created_at', desc: 'Return projects ordered by field'
+ optional :sort, type: String, values: %w[asc desc], default: 'desc',
+ desc: 'Return projects sorted in ascending and descending order'
+ end
+
+ params :filter_params do
+ optional :archived, type: Boolean, default: false, desc: 'Limit by archived status'
+ optional :visibility, type: String, values: %w[public internal private],
+ desc: 'Limit by visibility'
+ optional :search, type: String, desc: 'Return list of authorized projects matching the search criteria'
+ use :sort_params
+ end
- present projects, with: entity, user: current_user
+ params :create_params do
+ optional :namespace_id, type: Integer, desc: 'Namespace ID for the new project. Default to the user namespace.'
+ optional :import_url, type: String, desc: 'URL from which the project is imported'
+ end
end
- # Get a list of visible projects for authenticated user
- #
- # Example Request:
- # GET /projects/visible
+ desc 'Get a list of visible projects for authenticated user' do
+ success Entities::BasicProjectDetails
+ end
+ params do
+ optional :simple, type: Boolean, default: false,
+ desc: 'Return only the ID, URL, name, and path of each project'
+ use :filter_params
+ use :pagination
+ end
get '/visible' do
projects = ProjectsFinder.new.execute(current_user)
projects = filter_projects(projects)
- projects = paginate projects
+ entity = params[:simple] || !current_user ? Entities::BasicProjectDetails : Entities::ProjectWithAccess
+
+ present paginate(projects), with: entity, user: current_user
+ end
+
+ desc 'Get a projects list for authenticated user' do
+ success Entities::BasicProjectDetails
+ end
+ params do
+ optional :simple, type: Boolean, default: false,
+ desc: 'Return only the ID, URL, name, and path of each project'
+ use :filter_params
+ use :pagination
+ end
+ get do
+ authenticate!
+
+ projects = current_user.authorized_projects
+ projects = filter_projects(projects)
entity = params[:simple] ? Entities::BasicProjectDetails : Entities::ProjectWithAccess
- present projects, with: entity, user: current_user
+ present paginate(projects), with: entity, user: current_user
end
- # Get an owned projects list for authenticated user
- #
- # Example Request:
- # GET /projects/owned
+ desc 'Get an owned projects list for authenticated user' do
+ success Entities::BasicProjectDetails
+ end
+ params do
+ use :filter_params
+ use :pagination
+ end
get '/owned' do
+ authenticate!
+
projects = current_user.owned_projects
projects = filter_projects(projects)
- projects = paginate projects
- present projects, with: Entities::ProjectWithAccess, user: current_user
+
+ present paginate(projects), with: Entities::ProjectWithAccess, user: current_user
end
- # Gets starred project for the authenticated user
- #
- # Example Request:
- # GET /projects/starred
+ desc 'Gets starred project for the authenticated user' do
+ success Entities::BasicProjectDetails
+ end
+ params do
+ use :filter_params
+ use :pagination
+ end
get '/starred' do
+ authenticate!
+
projects = current_user.viewable_starred_projects
projects = filter_projects(projects)
- projects = paginate projects
- present projects, with: Entities::Project, user: current_user
+
+ present paginate(projects), with: Entities::Project, user: current_user
end
- # Get all projects for admin user
- #
- # Example Request:
- # GET /projects/all
+ desc 'Get all projects for admin user' do
+ success Entities::BasicProjectDetails
+ end
+ params do
+ use :filter_params
+ use :pagination
+ end
get '/all' do
authenticated_as_admin!
+
projects = Project.all
projects = filter_projects(projects)
- projects = paginate projects
- present projects, with: Entities::ProjectWithAccess, user: current_user
+
+ present paginate(projects), with: Entities::ProjectWithAccess, user: current_user
end
- # Get a single project
- #
- # Parameters:
- # id (required) - The ID of a project
- # Example Request:
- # GET /projects/:id
- get ":id" do
- present user_project, with: Entities::ProjectWithAccess, user: current_user,
- user_can_admin_project: can?(current_user, :admin_project, user_project)
+ desc 'Search for projects the current user has access to' do
+ success Entities::Project
end
+ params do
+ requires :query, type: String, desc: 'The project name to be searched'
+ use :sort_params
+ use :pagination
+ end
+ get "/search/:query" do
+ search_service = Search::GlobalService.new(current_user, search: params[:query]).execute
+ projects = search_service.objects('projects', params[:page])
+ projects = projects.reorder(params[:order_by] => params[:sort])
- # Get events for a single project
- #
- # Parameters:
- # id (required) - The ID of a project
- # Example Request:
- # GET /projects/:id/events
- get ":id/events" do
- events = paginate user_project.events.recent
- present events, with: Entities::Event
- end
-
- # Create new project
- #
- # Parameters:
- # name (required) - name for new project
- # description (optional) - short project description
- # issues_enabled (optional)
- # merge_requests_enabled (optional)
- # builds_enabled (optional)
- # wiki_enabled (optional)
- # snippets_enabled (optional)
- # container_registry_enabled (optional)
- # shared_runners_enabled (optional)
- # namespace_id (optional) - defaults to user namespace
- # public (optional) - if true same as setting visibility_level = 20
- # visibility_level (optional) - 0 by default
- # import_url (optional)
- # public_builds (optional)
- # lfs_enabled (optional)
- # request_access_enabled (optional) - Allow users to request member access
- # Example Request
- # POST /projects
+ present paginate(projects), with: Entities::Project
+ end
+
+ desc 'Create new project' do
+ success Entities::Project
+ end
+ params do
+ requires :name, type: String, desc: 'The name of the project'
+ optional :path, type: String, desc: 'The path of the repository'
+ use :optional_params
+ use :create_params
+ end
post do
- required_attributes! [:name]
- attrs = attributes_for_keys [:builds_enabled,
- :container_registry_enabled,
- :description,
- :import_url,
- :issues_enabled,
- :lfs_enabled,
- :merge_requests_enabled,
- :name,
- :namespace_id,
- :only_allow_merge_if_build_succeeds,
- :path,
- :public,
- :public_builds,
- :request_access_enabled,
- :shared_runners_enabled,
- :snippets_enabled,
- :visibility_level,
- :wiki_enabled,
- :only_allow_merge_if_all_discussions_are_resolved]
- attrs = map_public_to_visibility_level(attrs)
- @project = ::Projects::CreateService.new(current_user, attrs).execute
- if @project.saved?
- present @project, with: Entities::Project,
- user_can_admin_project: can?(current_user, :admin_project, @project)
+ attrs = map_public_to_visibility_level(declared_params(include_missing: false))
+ project = ::Projects::CreateService.new(current_user, attrs).execute
+
+ if project.saved?
+ present project, with: Entities::Project,
+ user_can_admin_project: can?(current_user, :admin_project, project)
else
- if @project.errors[:limit_reached].present?
- error!(@project.errors[:limit_reached], 403)
+ if project.errors[:limit_reached].present?
+ error!(project.errors[:limit_reached], 403)
end
- render_validation_error!(@project)
+ render_validation_error!(project)
end
end
- # Create new project for a specified user. Only available to admin users.
- #
- # Parameters:
- # user_id (required) - The ID of a user
- # name (required) - name for new project
- # description (optional) - short project description
- # default_branch (optional) - 'master' by default
- # issues_enabled (optional)
- # merge_requests_enabled (optional)
- # builds_enabled (optional)
- # wiki_enabled (optional)
- # snippets_enabled (optional)
- # container_registry_enabled (optional)
- # shared_runners_enabled (optional)
- # public (optional) - if true same as setting visibility_level = 20
- # visibility_level (optional)
- # import_url (optional)
- # public_builds (optional)
- # lfs_enabled (optional)
- # request_access_enabled (optional) - Allow users to request member access
- # Example Request
- # POST /projects/user/:user_id
+ desc 'Create new project for a specified user. Only available to admin users.' do
+ success Entities::Project
+ end
+ params do
+ requires :name, type: String, desc: 'The name of the project'
+ requires :user_id, type: Integer, desc: 'The ID of a user'
+ optional :default_branch, type: String, desc: 'The default branch of the project'
+ use :optional_params
+ use :create_params
+ end
post "user/:user_id" do
authenticated_as_admin!
- user = User.find(params[:user_id])
- attrs = attributes_for_keys [:builds_enabled,
- :default_branch,
- :description,
- :import_url,
- :issues_enabled,
- :lfs_enabled,
- :merge_requests_enabled,
- :name,
- :only_allow_merge_if_build_succeeds,
- :public,
- :public_builds,
- :request_access_enabled,
- :shared_runners_enabled,
- :snippets_enabled,
- :visibility_level,
- :wiki_enabled,
- :only_allow_merge_if_all_discussions_are_resolved]
- attrs = map_public_to_visibility_level(attrs)
- @project = ::Projects::CreateService.new(user, attrs).execute
- if @project.saved?
- present @project, with: Entities::Project,
- user_can_admin_project: can?(current_user, :admin_project, @project)
+ user = User.find_by(id: params.delete(:user_id))
+ not_found!('User') unless user
+
+ attrs = map_public_to_visibility_level(declared_params(include_missing: false))
+ project = ::Projects::CreateService.new(user, attrs).execute
+
+ if project.saved?
+ present project, with: Entities::Project,
+ user_can_admin_project: can?(current_user, :admin_project, project)
else
- render_validation_error!(@project)
+ render_validation_error!(project)
end
end
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource :projects, requirements: { id: /[^\/]+/ } do
+ desc 'Get a single project' do
+ success Entities::ProjectWithAccess
+ end
+ get ":id" do
+ entity = current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails
+ present user_project, with: entity, user: current_user,
+ user_can_admin_project: can?(current_user, :admin_project, user_project)
+ end
+
+ desc 'Get events for a single project' do
+ success Entities::Event
+ end
+ params do
+ use :pagination
+ end
+ get ":id/events" do
+ present paginate(user_project.events.recent), with: Entities::Event
+ end
- # Fork new project for the current user or provided namespace.
- #
- # Parameters:
- # id (required) - The ID of a project
- # namespace (optional) - The ID or name of the namespace that the project will be forked into.
- # Example Request
- # POST /projects/fork/:id
+ desc 'Fork new project for the current user or provided namespace.' do
+ success Entities::Project
+ end
+ params do
+ optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be forked into'
+ end
post 'fork/:id' do
- attrs = {}
- namespace_id = params[:namespace]
+ fork_params = declared_params(include_missing: false)
+ namespace_id = fork_params[:namespace]
if namespace_id.present?
- namespace = Namespace.find_by(id: namespace_id) || Namespace.find_by_path_or_name(namespace_id)
+ fork_params[:namespace] = if namespace_id =~ /^\d+$/
+ Namespace.find_by(id: namespace_id)
+ else
+ Namespace.find_by_path_or_name(namespace_id)
+ end
- unless namespace && can?(current_user, :create_projects, namespace)
+ unless fork_params[:namespace] && can?(current_user, :create_projects, fork_params[:namespace])
not_found!('Target Namespace')
end
-
- attrs[:namespace] = namespace
end
- @forked_project =
- ::Projects::ForkService.new(user_project,
- current_user,
- attrs).execute
+ forked_project = ::Projects::ForkService.new(user_project, current_user, fork_params).execute
- if @forked_project.errors.any?
- conflict!(@forked_project.errors.messages)
+ if forked_project.errors.any?
+ conflict!(forked_project.errors.messages)
else
- present @forked_project, with: Entities::Project,
- user_can_admin_project: can?(current_user, :admin_project, @forked_project)
+ present forked_project, with: Entities::Project,
+ user_can_admin_project: can?(current_user, :admin_project, forked_project)
end
end
- # Update an existing project
- #
- # Parameters:
- # id (required) - the id of a project
- # name (optional) - name of a project
- # path (optional) - path of a project
- # description (optional) - short project description
- # issues_enabled (optional)
- # merge_requests_enabled (optional)
- # builds_enabled (optional)
- # wiki_enabled (optional)
- # snippets_enabled (optional)
- # container_registry_enabled (optional)
- # shared_runners_enabled (optional)
- # public (optional) - if true same as setting visibility_level = 20
- # visibility_level (optional) - visibility level of a project
- # public_builds (optional)
- # lfs_enabled (optional)
- # Example Request
- # PUT /projects/:id
+ desc 'Update an existing project' do
+ success Entities::Project
+ end
+ params do
+ optional :name, type: String, desc: 'The name of the project'
+ optional :default_branch, type: String, desc: 'The default branch of the project'
+ optional :path, type: String, desc: 'The path of the repository'
+ use :optional_params
+ at_least_one_of :name, :description, :issues_enabled, :merge_requests_enabled,
+ :wiki_enabled, :builds_enabled, :snippets_enabled,
+ :shared_runners_enabled, :container_registry_enabled,
+ :lfs_enabled, :public, :visibility_level, :public_builds,
+ :request_access_enabled, :only_allow_merge_if_build_succeeds,
+ :only_allow_merge_if_all_discussions_are_resolved, :path,
+ :default_branch
+ end
put ':id' do
- attrs = attributes_for_keys [:builds_enabled,
- :container_registry_enabled,
- :default_branch,
- :description,
- :issues_enabled,
- :lfs_enabled,
- :merge_requests_enabled,
- :name,
- :only_allow_merge_if_build_succeeds,
- :path,
- :public,
- :public_builds,
- :request_access_enabled,
- :shared_runners_enabled,
- :snippets_enabled,
- :visibility_level,
- :wiki_enabled,
- :only_allow_merge_if_all_discussions_are_resolved]
- attrs = map_public_to_visibility_level(attrs)
authorize_admin_project
+ attrs = map_public_to_visibility_level(declared_params(include_missing: false))
authorize! :rename_project, user_project if attrs[:name].present?
- if attrs[:visibility_level].present?
- authorize! :change_visibility_level, user_project
- end
+ authorize! :change_visibility_level, user_project if attrs[:visibility_level].present?
- ::Projects::UpdateService.new(user_project,
- current_user, attrs).execute
+ ::Projects::UpdateService.new(user_project, current_user, attrs).execute
if user_project.errors.any?
render_validation_error!(user_project)
@@ -297,12 +299,9 @@ module API
end
end
- # Archive project
- #
- # Parameters:
- # id (required) - The ID of a project
- # Example Request:
- # PUT /projects/:id/archive
+ desc 'Archive a project' do
+ success Entities::Project
+ end
post ':id/archive' do
authorize!(:archive_project, user_project)
@@ -311,12 +310,9 @@ module API
present user_project, with: Entities::Project
end
- # Unarchive project
- #
- # Parameters:
- # id (required) - The ID of a project
- # Example Request:
- # PUT /projects/:id/unarchive
+ desc 'Unarchive a project' do
+ success Entities::Project
+ end
post ':id/unarchive' do
authorize!(:archive_project, user_project)
@@ -325,12 +321,9 @@ module API
present user_project, with: Entities::Project
end
- # Star project
- #
- # Parameters:
- # id (required) - The ID of a project
- # Example Request:
- # POST /projects/:id/star
+ desc 'Star a project' do
+ success Entities::Project
+ end
post ':id/star' do
if current_user.starred?(user_project)
not_modified!
@@ -342,12 +335,9 @@ module API
end
end
- # Unstar project
- #
- # Parameters:
- # id (required) - The ID of a project
- # Example Request:
- # DELETE /projects/:id/star
+ desc 'Unstar a project' do
+ success Entities::Project
+ end
delete ':id/star' do
if current_user.starred?(user_project)
current_user.toggle_star(user_project)
@@ -359,67 +349,51 @@ module API
end
end
- # Remove project
- #
- # Parameters:
- # id (required) - The ID of a project
- # Example Request:
- # DELETE /projects/:id
+ desc 'Remove a project'
delete ":id" do
authorize! :remove_project, user_project
::Projects::DestroyService.new(user_project, current_user, {}).async_execute
end
- # Mark this project as forked from another
- #
- # Parameters:
- # id: (required) - The ID of the project being marked as a fork
- # forked_from_id: (required) - The ID of the project it was forked from
- # Example Request:
- # POST /projects/:id/fork/:forked_from_id
+ desc 'Mark this project as forked from another'
+ params do
+ requires :forked_from_id, type: String, desc: 'The ID of the project it was forked from'
+ end
post ":id/fork/:forked_from_id" do
authenticated_as_admin!
- forked_from_project = find_project(params[:forked_from_id])
- unless forked_from_project.nil?
- if user_project.forked_from_project.nil?
- user_project.create_forked_project_link(forked_to_project_id: user_project.id, forked_from_project_id: forked_from_project.id)
- else
- render_api_error!("Project already forked", 409)
- end
+
+ forked_from_project = find_project!(params[:forked_from_id])
+ not_found!("Source Project") unless forked_from_project
+
+ if user_project.forked_from_project.nil?
+ user_project.create_forked_project_link(forked_to_project_id: user_project.id, forked_from_project_id: forked_from_project.id)
else
- not_found!("Source Project")
+ render_api_error!("Project already forked", 409)
end
end
- # Remove a forked_from relationship
- #
- # Parameters:
- # id: (required) - The ID of the project being marked as a fork
- # Example Request:
- # DELETE /projects/:id/fork
+ desc 'Remove a forked_from relationship'
delete ":id/fork" do
authorize! :remove_fork_project, user_project
+
if user_project.forked?
user_project.forked_project_link.destroy
+ else
+ not_modified!
end
end
- # Share project with group
- #
- # Parameters:
- # id (required) - The ID of a project
- # group_id (required) - The ID of a group
- # group_access (required) - Level of permissions for sharing
- # expires_at (optional) - Share expiration date
- #
- # Example Request:
- # POST /projects/:id/share
+ desc 'Share the project with a group' do
+ success Entities::ProjectGroupLink
+ end
+ params do
+ requires :group_id, type: Integer, desc: 'The ID of a group'
+ requires :group_access, type: Integer, values: Gitlab::Access.values, desc: 'The group access level'
+ optional :expires_at, type: Date, desc: 'Share expiration date'
+ end
post ":id/share" do
authorize! :admin_project, user_project
- required_attributes! [:group_id, :group_access]
- attrs = attributes_for_keys [:group_id, :group_access, :expires_at]
-
- group = Group.find_by_id(attrs[:group_id])
+ group = Group.find_by_id(params[:group_id])
unless group && can?(current_user, :read_group, group)
not_found!('Group')
@@ -429,7 +403,7 @@ module API
return render_api_error!("The project sharing with group is disabled", 400)
end
- link = user_project.project_group_links.new(attrs)
+ link = user_project.project_group_links.new(declared_params(include_missing: false))
if link.save
present link, with: Entities::ProjectGroupLink
@@ -438,40 +412,39 @@ module API
end
end
- # Upload a file
- #
- # Parameters:
- # id: (required) - The ID of the project
- # file: (required) - The file to be uploaded
- post ":id/uploads" do
- ::Projects::UploadService.new(user_project, params[:file]).execute
+ params do
+ requires :group_id, type: Integer, desc: 'The ID of the group'
end
+ delete ":id/share/:group_id" do
+ authorize! :admin_project, user_project
- # search for projects current_user has access to
- #
- # Parameters:
- # query (required) - A string contained in the project name
- # per_page (optional) - number of projects to return per page
- # page (optional) - the page to retrieve
- # Example Request:
- # GET /projects/search/:query
- get "/search/:query" do
- search_service = Search::GlobalService.new(current_user, search: params[:query]).execute
- projects = search_service.objects('projects', params[:page])
- projects = projects.reorder(project_order_by => project_sort)
+ link = user_project.project_group_links.find_by(group_id: params[:group_id])
+ not_found!('Group Link') unless link
- present paginate(projects), with: Entities::Project
+ link.destroy
+ no_content!
+ end
+
+ desc 'Upload a file'
+ params do
+ requires :file, type: File, desc: 'The file to be uploaded'
+ end
+ post ":id/uploads" do
+ ::Projects::UploadService.new(user_project, params[:file]).execute
end
- # Get a users list
- #
- # Example Request:
- # GET /users
+ desc 'Get the users list of a project' do
+ success Entities::UserBasic
+ end
+ params do
+ optional :search, type: String, desc: 'Return list of users matching the search criteria'
+ use :pagination
+ end
get ':id/users' do
- @users = User.where(id: user_project.team.users.map(&:id))
- @users = @users.search(params[:search]) if params[:search].present?
- @users = paginate @users
- present @users, with: Entities::UserBasic
+ users = user_project.team.users
+ users = users.search(params[:search]) if params[:search].present?
+
+ present paginate(users), with: Entities::UserBasic
end
end
end
diff --git a/lib/api/runners.rb b/lib/api/runners.rb
index b145cce7e3e..4816b5ed1b7 100644
--- a/lib/api/runners.rb
+++ b/lib/api/runners.rb
@@ -1,5 +1,7 @@
module API
class Runners < Grape::API
+ include PaginationParams
+
before { authenticate! }
resource :runners do
@@ -9,6 +11,7 @@ module API
params do
optional :scope, type: String, values: %w[active paused online],
desc: 'The scope of specific runners to show'
+ use :pagination
end
get do
runners = filter_runners(current_user.ci_authorized_runners, params[:scope], without: ['specific', 'shared'])
@@ -21,6 +24,7 @@ module API
params do
optional :scope, type: String, values: %w[active paused online specific shared],
desc: 'The scope of specific runners to show'
+ use :pagination
end
get 'all' do
authenticated_as_admin!
@@ -91,6 +95,7 @@ module API
params do
optional :scope, type: String, values: %w[active paused online specific shared],
desc: 'The scope of specific runners to show'
+ use :pagination
end
get ':id/runners' do
runners = filter_runners(Ci::Runner.owned_or_shared(user_project.id), params[:scope])
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 4d23499aa39..bc427705777 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -65,7 +65,7 @@ module API
detail 'Added in GitLab 8.13'
end
post ':id/services/:service_slug/trigger' do
- project = Project.find_with_namespace(params[:id]) || Project.find_by(id: params[:id])
+ project = find_project(params[:id])
# This is not accurate, but done to prevent leakage of the project names
not_found!('Service') unless project
diff --git a/lib/api/sidekiq_metrics.rb b/lib/api/sidekiq_metrics.rb
index d3d6827dc54..11f2b40269a 100644
--- a/lib/api/sidekiq_metrics.rb
+++ b/lib/api/sidekiq_metrics.rb
@@ -39,50 +39,22 @@ module API
end
end
- # Get Sidekiq Queue metrics
- #
- # Parameters:
- # None
- #
- # Example:
- # GET /sidekiq/queue_metrics
- #
+ desc 'Get the Sidekiq queue metrics'
get 'sidekiq/queue_metrics' do
{ queues: queue_metrics }
end
- # Get Sidekiq Process metrics
- #
- # Parameters:
- # None
- #
- # Example:
- # GET /sidekiq/process_metrics
- #
+ desc 'Get the Sidekiq process metrics'
get 'sidekiq/process_metrics' do
{ processes: process_metrics }
end
- # Get Sidekiq Job statistics
- #
- # Parameters:
- # None
- #
- # Example:
- # GET /sidekiq/job_stats
- #
+ desc 'Get the Sidekiq job statistics'
get 'sidekiq/job_stats' do
{ jobs: job_stats }
end
- # Get Sidekiq Compound metrics. Includes all previous metrics
- #
- # Parameters:
- # None
- #
- # Example:
- # GET /sidekiq/compound_metrics
- #
+ desc 'Get the Sidekiq Compound metrics. Includes queue, process, and job statistics'
get 'sidekiq/compound_metrics' do
{ queues: queue_metrics, processes: process_metrics, jobs: job_stats }
end
diff --git a/lib/api/todos.rb b/lib/api/todos.rb
index 832b04a3bb1..ed8f48aa1e3 100644
--- a/lib/api/todos.rb
+++ b/lib/api/todos.rb
@@ -1,6 +1,7 @@
module API
- # Todos API
class Todos < Grape::API
+ include PaginationParams
+
before { authenticate! }
ISSUABLE_TYPES = {
@@ -44,10 +45,11 @@ module API
desc 'Get a todo list' do
success Entities::Todo
end
+ params do
+ use :pagination
+ end
get do
- todos = find_todos
-
- present paginate(todos), with: Entities::Todo, current_user: current_user
+ present paginate(find_todos), with: Entities::Todo, current_user: current_user
end
desc 'Mark a todo as done' do
diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb
index 569598fbd2c..87a717ba751 100644
--- a/lib/api/triggers.rb
+++ b/lib/api/triggers.rb
@@ -1,5 +1,7 @@
module API
class Triggers < Grape::API
+ include PaginationParams
+
params do
requires :id, type: String, desc: 'The ID of a project'
end
@@ -13,7 +15,7 @@ module API
optional :variables, type: Hash, desc: 'The list of variables to be injected into build'
end
post ":id/(ref/:ref/)trigger/builds" do
- project = Project.find_with_namespace(params[:id]) || Project.find_by(id: params[:id])
+ 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
@@ -42,6 +44,9 @@ module API
desc 'Get triggers list' do
success Entities::Trigger
end
+ params do
+ use :pagination
+ end
get ':id/triggers' do
authenticate!
authorize! :admin_build, user_project
diff --git a/lib/api/users.rb b/lib/api/users.rb
index c07539194ed..bc2362aa72e 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -1,6 +1,7 @@
module API
- # Users API
class Users < Grape::API
+ include PaginationParams
+
before { authenticate! }
resource :users, requirements: { uid: /[0-9]*/, id: /[0-9]*/ } do
@@ -33,6 +34,7 @@ module API
optional :active, type: Boolean, default: false, desc: 'Filters only active users'
optional :external, type: Boolean, default: false, desc: 'Filters only external users'
optional :blocked, type: Boolean, default: false, desc: 'Filters only blocked users'
+ use :pagination
end
get do
unless can?(current_user, :read_users_list, nil)
@@ -140,7 +142,8 @@ module API
User.where(username: params[:username]).
where.not(id: user.id).count > 0
- identity_attrs = params.slice(:provider, :extern_uid)
+ user_params = declared_params(include_missing: false)
+ identity_attrs = user_params.slice(:provider, :extern_uid)
if identity_attrs.any?
identity = user.identities.find_by(provider: identity_attrs[:provider])
@@ -154,10 +157,10 @@ module API
end
# Delete already handled parameters
- params.delete(:extern_uid)
- params.delete(:provider)
+ user_params.delete(:extern_uid)
+ user_params.delete(:provider)
- if user.update_attributes(declared_params(include_missing: false))
+ if user.update_attributes(user_params)
present user, with: Entities::UserFull
else
render_validation_error!(user)
@@ -329,6 +332,7 @@ module API
end
params do
requires :id, type: Integer, desc: 'The ID of the user'
+ use :pagination
end
get ':id/events' do
user = User.find_by(id: params[:id])
diff --git a/lib/api/variables.rb b/lib/api/variables.rb
index b9fb3c21dbb..f623b1dfe9f 100644
--- a/lib/api/variables.rb
+++ b/lib/api/variables.rb
@@ -1,6 +1,8 @@
module API
# Projects variables API
class Variables < Grape::API
+ include PaginationParams
+
before { authenticate! }
before { authorize! :admin_build, user_project }
@@ -13,8 +15,7 @@ module API
success Entities::Variable
end
params do
- optional :page, type: Integer, desc: 'The page number for pagination'
- optional :per_page, type: Integer, desc: 'The value of items per page to show'
+ use :pagination
end
get ':id/variables' do
variables = user_project.variables
@@ -29,7 +30,7 @@ module API
end
get ':id/variables/:key' do
key = params[:key]
- variable = user_project.variables.find_by(key: key.to_s)
+ variable = user_project.variables.find_by(key: key)
return not_found!('Variable') unless variable
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index 0dfffaf0bc6..96c20100541 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -14,7 +14,7 @@ module Backup
s[:gitlab_version] = Gitlab::VERSION
s[:tar_version] = tar_version
s[:skipped] = ENV["SKIP"]
- tar_file = "#{s[:backup_created_at].to_i}_gitlab_backup.tar"
+ tar_file = s[:backup_created_at].strftime('%s_%Y_%m_%d') + '_gitlab_backup.tar'
Dir.chdir(Gitlab.config.backup.path) do
File.open("#{Gitlab.config.backup.path}/backup_information.yml",
@@ -83,10 +83,14 @@ module Backup
Dir.chdir(Gitlab.config.backup.path) do
file_list = Dir.glob('*_gitlab_backup.tar')
- file_list.map! { |f| $1.to_i if f =~ /(\d+)_gitlab_backup.tar/ }
- file_list.sort.each do |timestamp|
- if Time.at(timestamp) < (Time.now - keep_time)
- if Kernel.system(*%W(rm #{timestamp}_gitlab_backup.tar))
+ file_list.map! do |path_string|
+ if path_string =~ /(\d+)(?:_\d{4}_\d{2}_\d{2})?_gitlab_backup\.tar/
+ { timestamp: $1.to_i, path: path_string }
+ end
+ end
+ file_list.sort.each do |file|
+ if Time.at(file[:timestamp]) < (Time.now - keep_time)
+ if Kernel.system(*%W(rm #{file[:path]}))
removed += 1
end
end
@@ -103,7 +107,7 @@ module Backup
Dir.chdir(Gitlab.config.backup.path)
# check for existing backups in the backup dir
- file_list = Dir.glob("*_gitlab_backup.tar").each.map { |f| f.split(/_/).first.to_i }
+ file_list = Dir.glob("*_gitlab_backup.tar")
puts "no backups found" if file_list.count == 0
if file_list.count > 1 && ENV["BACKUP"].nil?
@@ -112,7 +116,7 @@ module Backup
exit 1
end
- tar_file = ENV["BACKUP"].nil? ? File.join("#{file_list.first}_gitlab_backup.tar") : File.join(ENV["BACKUP"] + "_gitlab_backup.tar")
+ tar_file = ENV["BACKUP"].nil? ? file_list.first : file_list.grep(ENV['BACKUP']).first
unless File.exist?(tar_file)
puts "The specified backup doesn't exist!"
diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
index 3740d4fb4cd..d904a8bd4ae 100644
--- a/lib/banzai/filter/abstract_reference_filter.rb
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -33,7 +33,7 @@ module Banzai
# Returns a String replaced with the return of the block.
def self.references_in(text, pattern = object_class.reference_pattern)
text.gsub(pattern) do |match|
- yield match, $~[object_sym].to_i, $~[:project], $~
+ yield match, $~[object_sym].to_i, $~[:project], $~[:namespace], $~
end
end
@@ -145,8 +145,9 @@ module Banzai
# Returns a String with references replaced with links. All links
# have `gfm` and `gfm-OBJECT_NAME` class names attached for styling.
def object_link_filter(text, pattern, link_content: nil)
- references_in(text, pattern) do |match, id, project_ref, matches|
- project = project_from_ref_cached(project_ref)
+ references_in(text, pattern) do |match, id, project_ref, namespace_ref, matches|
+ project_path = full_project_path(namespace_ref, project_ref)
+ project = project_from_ref_cached(project_path)
if project && object = find_object_cached(project, id)
title = object_link_title(object)
@@ -217,10 +218,9 @@ module Banzai
nodes.each do |node|
node.to_html.scan(regex) do
- project = $~[:project] || current_project_path
+ project_path = full_project_path($~[:namespace], $~[:project])
symbol = $~[object_sym]
-
- refs[project] << symbol if object_class.reference_valid?(symbol)
+ refs[project_path] << symbol if object_class.reference_valid?(symbol)
end
end
@@ -272,8 +272,19 @@ module Banzai
@current_project_path ||= project.path_with_namespace
end
+ def current_project_namespace_path
+ @current_project_namespace_path ||= project.namespace.path
+ end
+
private
+ def full_project_path(namespace, project_ref)
+ return current_project_path unless project_ref
+
+ namespace_ref = namespace || current_project_namespace_path
+ "#{namespace_ref}/#{project_ref}"
+ end
+
def project_refs_cache
RequestStore[:banzai_project_refs] ||= {}
end
diff --git a/lib/banzai/filter/commit_range_reference_filter.rb b/lib/banzai/filter/commit_range_reference_filter.rb
index 4358bf45549..eaacb9591b1 100644
--- a/lib/banzai/filter/commit_range_reference_filter.rb
+++ b/lib/banzai/filter/commit_range_reference_filter.rb
@@ -12,7 +12,7 @@ module Banzai
def self.references_in(text, pattern = CommitRange.reference_pattern)
text.gsub(pattern) do |match|
- yield match, $~[:commit_range], $~[:project], $~
+ yield match, $~[:commit_range], $~[:project], $~[:namespace], $~
end
end
diff --git a/lib/banzai/filter/commit_reference_filter.rb b/lib/banzai/filter/commit_reference_filter.rb
index a26dd09c25a..69c06117eda 100644
--- a/lib/banzai/filter/commit_reference_filter.rb
+++ b/lib/banzai/filter/commit_reference_filter.rb
@@ -12,7 +12,7 @@ module Banzai
def self.references_in(text, pattern = Commit.reference_pattern)
text.gsub(pattern) do |match|
- yield match, $~[:commit], $~[:project], $~
+ yield match, $~[:commit], $~[:project], $~[:namespace], $~
end
end
diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb
index 9f9a96cdc65..a605dea149e 100644
--- a/lib/banzai/filter/label_reference_filter.rb
+++ b/lib/banzai/filter/label_reference_filter.rb
@@ -14,16 +14,18 @@ module Banzai
def self.references_in(text, pattern = Label.reference_pattern)
unescape_html_entities(text).gsub(pattern) do |match|
- yield match, $~[:label_id].to_i, $~[:label_name], $~[:project], $~
+ yield match, $~[:label_id].to_i, $~[:label_name], $~[:project], $~[:namespace], $~
end
end
def references_in(text, pattern = Label.reference_pattern)
unescape_html_entities(text).gsub(pattern) do |match|
- label = find_label($~[:project], $~[:label_id], $~[:label_name])
+ namespace, project = $~[:namespace], $~[:project]
+ project_path = full_project_path(namespace, project)
+ label = find_label(project_path, $~[:label_id], $~[:label_name])
if label
- yield match, label.id, $~[:project], $~
+ yield match, label.id, project, namespace, $~
else
match
end
@@ -64,48 +66,12 @@ module Banzai
end
def object_link_text(object, matches)
- if same_group?(object) && namespace_match?(matches)
- render_same_project_label(object)
- elsif same_project?(object)
- render_same_project_label(object)
- else
- render_cross_project_label(object, matches)
- end
- end
-
- def same_group?(object)
- object.is_a?(GroupLabel) && object.group == project.group
- end
-
- def namespace_match?(matches)
- matches[:project].blank? || matches[:project] == project.path_with_namespace
- end
-
- def same_project?(object)
- object.is_a?(ProjectLabel) && object.project == project
- end
-
- def user
- context[:current_user] || context[:author]
- end
-
- def project
- context[:project]
- end
-
- def render_same_project_label(object)
- LabelsHelper.render_colored_label(object)
- end
-
- def render_cross_project_label(object, matches)
- source_project =
- if matches[:project]
- Project.find_with_namespace(matches[:project])
- else
- object.project
- end
+ project_path = full_project_path(matches[:namespace], matches[:project])
+ project_from_ref = project_from_ref_cached(project_path)
+ reference = project_from_ref.to_human_reference(project)
+ label_suffix = " <i>in #{reference}</i>" if reference.present?
- LabelsHelper.render_colored_cross_project_label(object, source_project)
+ LabelsHelper.render_colored_label(object, label_suffix)
end
def unescape_html_entities(text)
diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb
index 58fff496d00..f12014e191f 100644
--- a/lib/banzai/filter/milestone_reference_filter.rb
+++ b/lib/banzai/filter/milestone_reference_filter.rb
@@ -19,18 +19,20 @@ module Banzai
return super(text, pattern) if pattern != Milestone.reference_pattern
text.gsub(pattern) do |match|
- milestone = find_milestone($~[:project], $~[:milestone_iid], $~[:milestone_name])
+ milestone = find_milestone($~[:project], $~[:namespace], $~[:milestone_iid], $~[:milestone_name])
if milestone
- yield match, milestone.iid, $~[:project], $~
+ yield match, milestone.iid, $~[:project], $~[:namespace], $~
else
match
end
end
end
- def find_milestone(project_ref, milestone_id, milestone_name)
- project = project_from_ref(project_ref)
+ def find_milestone(project_ref, namespace_ref, milestone_id, milestone_name)
+ project_path = full_project_path(namespace_ref, project_ref)
+ project = project_from_ref(project_path)
+
return unless project
milestone_params = milestone_params(milestone_id, milestone_name)
@@ -52,11 +54,13 @@ module Banzai
end
def object_link_text(object, matches)
- if context[:project] == object.project
- super
+ milestone_link = escape_once(super)
+ reference = object.project.to_reference(project)
+
+ if reference.present?
+ "#{milestone_link} <i>in #{reference}</i>".html_safe
else
- "#{escape_once(super)} <i>in #{escape_once(object.project.path_with_namespace)}</i>".
- html_safe
+ milestone_link
end
end
diff --git a/lib/banzai/filter/table_of_contents_filter.rb b/lib/banzai/filter/table_of_contents_filter.rb
index a4eda6fdf76..8e7084f2543 100644
--- a/lib/banzai/filter/table_of_contents_filter.rb
+++ b/lib/banzai/filter/table_of_contents_filter.rb
@@ -35,9 +35,11 @@ module Banzai
headers[id] += 1
if header_content = node.children.first
+ # namespace detection will be automatically handled via javascript (see issue #22781)
+ namespace = "user-content-"
href = "#{id}#{uniq}"
push_toc(href, text)
- header_content.add_previous_sibling(anchor_tag(href))
+ header_content.add_previous_sibling(anchor_tag("#{namespace}#{href}", href))
end
end
@@ -48,8 +50,8 @@ module Banzai
private
- def anchor_tag(href)
- %Q{<a id="#{href}" class="anchor" href="##{href}" aria-hidden="true"></a>}
+ def anchor_tag(id, href)
+ %Q{<a id="#{id}" class="anchor" href="##{href}" aria-hidden="true"></a>}
end
def push_toc(href, text)
diff --git a/lib/constraints/constrainer_helper.rb b/lib/constraints/constrainer_helper.rb
deleted file mode 100644
index ab07a6793d9..00000000000
--- a/lib/constraints/constrainer_helper.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module ConstrainerHelper
- def extract_resource_path(path)
- id = path.dup
- id.sub!(/\A#{relative_url_root}/, '') if relative_url_root
- id.sub(/\A\/+/, '').sub(/\/+\z/, '').sub(/.atom\z/, '')
- end
-
- private
-
- def relative_url_root
- if defined?(Gitlab::Application.config.relative_url_root)
- Gitlab::Application.config.relative_url_root
- end
- end
-end
diff --git a/lib/constraints/group_url_constrainer.rb b/lib/constraints/group_url_constrainer.rb
index 2af6e1a11c8..5711d96a586 100644
--- a/lib/constraints/group_url_constrainer.rb
+++ b/lib/constraints/group_url_constrainer.rb
@@ -1,15 +1,17 @@
-require_relative 'constrainer_helper'
-
class GroupUrlConstrainer
- include ConstrainerHelper
-
def matches?(request)
- id = extract_resource_path(request.path)
+ id = request.params[:id]
+
+ return false unless valid?(id)
+
+ Group.find_by(path: id).present?
+ end
+
+ private
- if id =~ Gitlab::Regex.namespace_regex
- Group.find_by(path: id).present?
- else
- false
+ def valid?(id)
+ id.split('/').all? do |namespace|
+ NamespaceValidator.valid?(namespace)
end
end
end
diff --git a/lib/constraints/project_url_constrainer.rb b/lib/constraints/project_url_constrainer.rb
new file mode 100644
index 00000000000..730b05bed97
--- /dev/null
+++ b/lib/constraints/project_url_constrainer.rb
@@ -0,0 +1,13 @@
+class ProjectUrlConstrainer
+ def matches?(request)
+ namespace_path = request.params[:namespace_id]
+ project_path = request.params[:project_id] || request.params[:id]
+ full_path = namespace_path + '/' + project_path
+
+ unless ProjectPathValidator.valid?(project_path)
+ return false
+ end
+
+ Project.find_with_namespace(full_path).present?
+ end
+end
diff --git a/lib/constraints/user_url_constrainer.rb b/lib/constraints/user_url_constrainer.rb
index 4d722ad5af2..9ab5bcb12ff 100644
--- a/lib/constraints/user_url_constrainer.rb
+++ b/lib/constraints/user_url_constrainer.rb
@@ -1,15 +1,5 @@
-require_relative 'constrainer_helper'
-
class UserUrlConstrainer
- include ConstrainerHelper
-
def matches?(request)
- id = extract_resource_path(request.path)
-
- if id =~ Gitlab::Regex.namespace_regex
- User.find_by('lower(username) = ?', id.downcase).present?
- else
- false
- end
+ User.find_by_username(request.params[:username]).present?
end
end
diff --git a/lib/email_template_interceptor.rb b/lib/email_template_interceptor.rb
new file mode 100644
index 00000000000..fb04a7824b8
--- /dev/null
+++ b/lib/email_template_interceptor.rb
@@ -0,0 +1,13 @@
+# Read about interceptors in http://guides.rubyonrails.org/action_mailer_basics.html#intercepting-emails
+class EmailTemplateInterceptor
+ include Gitlab::CurrentSettings
+
+ def self.delivering_email(message)
+ # Remove HTML part if HTML emails are disabled.
+ unless current_application_settings.html_emails_enabled
+ message.part.delete_if do |part|
+ part.content_type.try(:start_with?, 'text/html')
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/chat_commands/base_command.rb b/lib/gitlab/chat_commands/base_command.rb
index e59d69b72b9..25da8474e95 100644
--- a/lib/gitlab/chat_commands/base_command.rb
+++ b/lib/gitlab/chat_commands/base_command.rb
@@ -40,9 +40,7 @@ module Gitlab
private
def find_by_iid(iid)
- resource = collection.find_by(iid: iid)
-
- readable?(resource) ? resource : nil
+ collection.find_by(iid: iid)
end
end
end
diff --git a/lib/gitlab/chat_commands/command.rb b/lib/gitlab/chat_commands/command.rb
index 0ec358debc7..b0d3fdbc48a 100644
--- a/lib/gitlab/chat_commands/command.rb
+++ b/lib/gitlab/chat_commands/command.rb
@@ -4,6 +4,7 @@ module Gitlab
COMMANDS = [
Gitlab::ChatCommands::IssueShow,
Gitlab::ChatCommands::IssueCreate,
+ Gitlab::ChatCommands::IssueSearch,
Gitlab::ChatCommands::Deploy,
].freeze
diff --git a/lib/gitlab/chat_commands/issue_command.rb b/lib/gitlab/chat_commands/issue_command.rb
index f1bc36239d5..84de3e44c70 100644
--- a/lib/gitlab/chat_commands/issue_command.rb
+++ b/lib/gitlab/chat_commands/issue_command.rb
@@ -6,11 +6,7 @@ module Gitlab
end
def collection
- project.issues
- end
-
- def readable?(issue)
- self.class.can?(current_user, :read_issue, issue)
+ IssuesFinder.new(current_user, project_id: project.id).execute
end
end
end
diff --git a/lib/gitlab/chat_commands/issue_create.rb b/lib/gitlab/chat_commands/issue_create.rb
index 98338ebfa27..1dba85c1b51 100644
--- a/lib/gitlab/chat_commands/issue_create.rb
+++ b/lib/gitlab/chat_commands/issue_create.rb
@@ -2,11 +2,13 @@ module Gitlab
module ChatCommands
class IssueCreate < IssueCommand
def self.match(text)
- /\Aissue\s+create\s+(?<title>[^\n]*)\n*(?<description>.*)\z/.match(text)
+ # we can not match \n with the dot by passing the m modifier as than
+ # the title and description are not seperated
+ /\Aissue\s+(new|create)\s+(?<title>[^\n]*)\n*(?<description>(.|\n)*)/.match(text)
end
def self.help_message
- 'issue create <title>\n<description>'
+ 'issue new <title>\n<description>'
end
def self.allowed?(project, user)
@@ -15,7 +17,7 @@ module Gitlab
def execute(match)
title = match[:title]
- description = match[:description]
+ description = match[:description].to_s.rstrip
Issues::CreateService.new(project, current_user, title: title, description: description).execute
end
diff --git a/lib/gitlab/chat_commands/issue_search.rb b/lib/gitlab/chat_commands/issue_search.rb
new file mode 100644
index 00000000000..51bf80c800b
--- /dev/null
+++ b/lib/gitlab/chat_commands/issue_search.rb
@@ -0,0 +1,17 @@
+module Gitlab
+ module ChatCommands
+ class IssueSearch < IssueCommand
+ def self.match(text)
+ /\Aissue\s+search\s+(?<query>.*)/.match(text)
+ end
+
+ def self.help_message
+ "issue search <your query>"
+ end
+
+ def execute(match)
+ collection.search(match[:query]).limit(QUERY_LIMIT)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/chat_commands/issue_show.rb b/lib/gitlab/chat_commands/issue_show.rb
index f5bceb038e5..2a45d49cf6b 100644
--- a/lib/gitlab/chat_commands/issue_show.rb
+++ b/lib/gitlab/chat_commands/issue_show.rb
@@ -2,7 +2,7 @@ module Gitlab
module ChatCommands
class IssueShow < IssueCommand
def self.match(text)
- /\Aissue\s+show\s+(?<iid>\d+)/.match(text)
+ /\Aissue\s+show\s+#{Issue.reference_prefix}?(?<iid>\d+)/.match(text)
end
def self.help_message
diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb
index 20dcc024b4e..a55362f0b6b 100644
--- a/lib/gitlab/ci/config/entry/job.rb
+++ b/lib/gitlab/ci/config/entry/job.rb
@@ -108,7 +108,7 @@ module Gitlab
self.class.nodes.each_key do |key|
global_entry = deps[key]
- job_entry = @entries[key]
+ job_entry = self[key]
if global_entry.specified? && !job_entry.specified?
@entries[key] = global_entry
diff --git a/lib/gitlab/cycle_analytics/base_event.rb b/lib/gitlab/cycle_analytics/base_event.rb
index 486139b1687..53a148ad703 100644
--- a/lib/gitlab/cycle_analytics/base_event.rb
+++ b/lib/gitlab/cycle_analytics/base_event.rb
@@ -16,7 +16,7 @@ module Gitlab
event_result.map do |event|
serialize(event) if has_permission?(event['id'])
- end
+ end.compact
end
def custom_query(_base_query); end
diff --git a/lib/gitlab/cycle_analytics/plan_event.rb b/lib/gitlab/cycle_analytics/plan_event.rb
index b1ae215f348..7c3f0e9989f 100644
--- a/lib/gitlab/cycle_analytics/plan_event.rb
+++ b/lib/gitlab/cycle_analytics/plan_event.rb
@@ -27,6 +27,8 @@ module Gitlab
end
def first_time_reference_commit(commits, event)
+ return nil if commits.blank?
+
YAML.load(commits).find do |commit|
next unless commit[:committed_date] && event['first_mentioned_in_commit_at']
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 2d5c9232425..55b8f888d53 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -35,13 +35,6 @@ module Gitlab
order
end
- def self.serialized_transaction
- opts = {}
- opts[:isolation] = :serializable unless Rails.env.test? && connection.transaction_open?
-
- connection.transaction(opts) { yield }
- end
-
def self.random
Gitlab::Database.postgresql? ? "RANDOM()" : "RAND()"
end
diff --git a/lib/gitlab/diff/file_collection/merge_request_diff.rb b/lib/gitlab/diff/file_collection/merge_request_diff.rb
index fe7adb7bed6..56530448f36 100644
--- a/lib/gitlab/diff/file_collection/merge_request_diff.rb
+++ b/lib/gitlab/diff/file_collection/merge_request_diff.rb
@@ -20,7 +20,7 @@ module Gitlab
# Extracted method to highlight in the same iteration to the diff_collection.
def decorate_diff!(diff)
diff_file = super
- cache_highlight!(diff_file) if cacheable?
+ cache_highlight!(diff_file) if cacheable?(diff_file)
diff_file
end
@@ -60,8 +60,8 @@ module Gitlab
Rails.cache.write(cache_key, highlight_cache) if @highlight_cache_was_empty
end
- def cacheable?
- @merge_request_diff.present?
+ def cacheable?(diff_file)
+ @merge_request_diff.present? && diff_file.blob && diff_file.blob.text?
end
def cache_key
diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb
index f4d1505ea91..c8e36d8ff4a 100644
--- a/lib/gitlab/ee_compat_check.rb
+++ b/lib/gitlab/ee_compat_check.rb
@@ -149,7 +149,7 @@ module Gitlab
end
def ce_patch_name
- @ce_patch_name ||= "#{ce_branch}.patch"
+ @ce_patch_name ||= patch_name_from_branch(ce_branch)
end
def ce_patch_full_path
@@ -161,13 +161,17 @@ module Gitlab
end
def ee_patch_name
- @ee_patch_name ||= "#{ee_branch}.patch"
+ @ee_patch_name ||= patch_name_from_branch(ee_branch)
end
def ee_patch_full_path
@ee_patch_full_path ||= patches_dir.join(ee_patch_name)
end
+ def patch_name_from_branch(branch_name)
+ branch_name.parameterize << '.patch'
+ end
+
def step(desc, cmd = nil)
puts "\n=> #{desc}\n"
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index b87ca316240..9563fa7cafb 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -79,6 +79,10 @@ module Gitlab
check_change_access!(changes)
end
+ def guest_can_downlod_code?
+ Guest.can?(:download_code, project)
+ end
+
def user_can_download_code?
authentication_abilities.include?(:download_code) && user_access.can_do_action?(:download_code)
end
diff --git a/lib/gitlab/git_access_wiki.rb b/lib/gitlab/git_access_wiki.rb
index f7976a64ef5..74171f4f90e 100644
--- a/lib/gitlab/git_access_wiki.rb
+++ b/lib/gitlab/git_access_wiki.rb
@@ -1,5 +1,13 @@
module Gitlab
class GitAccessWiki < GitAccess
+ def guest_can_downlod_code?
+ Guest.can?(:download_wiki_code, project)
+ end
+
+ def user_can_download_code?
+ authentication_abilities.include?(:download_code) && user_access.can_do_action?(:download_wiki_code)
+ end
+
def check_single_change_access(change)
if user_access.can_do_action?(:create_wiki)
build_status_object(true)
diff --git a/lib/gitlab/github_import/branch_formatter.rb b/lib/gitlab/github_import/branch_formatter.rb
index 4750675ae9d..0a8d05b5fe1 100644
--- a/lib/gitlab/github_import/branch_formatter.rb
+++ b/lib/gitlab/github_import/branch_formatter.rb
@@ -8,7 +8,7 @@ module Gitlab
end
def valid?
- repo.present?
+ sha.present? && ref.present?
end
private
diff --git a/lib/gitlab/identifier.rb b/lib/gitlab/identifier.rb
index f8809db21aa..94678b6ec40 100644
--- a/lib/gitlab/identifier.rb
+++ b/lib/gitlab/identifier.rb
@@ -21,10 +21,8 @@ module Gitlab
return if !commit || !commit.author_email
- email = commit.author_email
-
- identify_with_cache(:email, email) do
- User.find_by(email: email)
+ identify_with_cache(:email, commit.author_email) do
+ commit.author
end
end
diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb
index a8b4dc2a83f..96ed20af918 100644
--- a/lib/gitlab/o_auth/user.rb
+++ b/lib/gitlab/o_auth/user.rb
@@ -39,7 +39,7 @@ module Gitlab
log.info "(#{provider}) saving user #{auth_hash.email} from login with extern_uid => #{auth_hash.uid}"
gl_user
rescue ActiveRecord::RecordInvalid => e
- log.info "(#{provider}) Error saving user: #{gl_user.errors.full_messages}"
+ log.info "(#{provider}) Error saving user #{auth_hash.uid} (#{auth_hash.email}): #{gl_user.errors.full_messages}"
return self, e.record.errors
end
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index c12358ceef4..a06cf6a989c 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -8,8 +8,10 @@ module Gitlab
# allow non-regex validatiions, etc), `NAMESPACE_REGEX_STR_SIMPLE` serves as a Javascript-compatible version of
# `NAMESPACE_REGEX_STR`, with the negative lookbehind assertion removed. This means that the client-side validation
# will pass for usernames ending in `.atom` and `.git`, but will be caught by the server-side validation.
- NAMESPACE_REGEX_STR_SIMPLE = '[a-zA-Z0-9_\.][a-zA-Z0-9_\-\.]*[a-zA-Z0-9_\-]|[a-zA-Z0-9_]'.freeze
+ PATH_REGEX_STR = '[a-zA-Z0-9_\.][a-zA-Z0-9_\-\.]*'.freeze
+ NAMESPACE_REGEX_STR_SIMPLE = PATH_REGEX_STR + '[a-zA-Z0-9_\-]|[a-zA-Z0-9_]'.freeze
NAMESPACE_REGEX_STR = '(?:' + NAMESPACE_REGEX_STR_SIMPLE + ')(?<!\.git|\.atom)'.freeze
+ PROJECT_REGEX_STR = PATH_REGEX_STR + '(?<!\.git|\.atom)'.freeze
def namespace_regex
@namespace_regex ||= /\A#{NAMESPACE_REGEX_STR}\z/.freeze
@@ -42,7 +44,15 @@ module Gitlab
end
def project_path_regex
- @project_path_regex ||= /\A[a-zA-Z0-9_.][a-zA-Z0-9_\-\.]*(?<!\.git|\.atom)\z/.freeze
+ @project_path_regex ||= /\A#{PROJECT_REGEX_STR}\z/.freeze
+ end
+
+ def project_route_regex
+ @project_route_regex ||= /#{PROJECT_REGEX_STR}/.freeze
+ end
+
+ def project_git_route_regex
+ @project_route_git_regex ||= /#{PATH_REGEX_STR}\.git/.freeze
end
def project_path_regex_message
diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb
index 2690938fe82..47d8599e298 100644
--- a/lib/gitlab/search_results.rb
+++ b/lib/gitlab/search_results.rb
@@ -50,7 +50,7 @@ module Gitlab
end
def issues
- issues = Issue.visible_to_user(current_user).where(project_id: project_ids_relation)
+ issues = IssuesFinder.new(current_user).execute.where(project_id: project_ids_relation)
if query =~ /#(\d+)\z/
issues = issues.where(iid: $1)
diff --git a/lib/tasks/gitlab/helpers.rake b/lib/tasks/gitlab/helpers.rake
new file mode 100644
index 00000000000..dd2d5861481
--- /dev/null
+++ b/lib/tasks/gitlab/helpers.rake
@@ -0,0 +1,8 @@
+require 'tasks/gitlab/task_helpers'
+
+# Prevent StateMachine warnings from outputting during a cron task
+StateMachines::Machine.ignore_method_conflicts = true if ENV['CRON']
+
+namespace :gitlab do
+ include Gitlab::TaskHelpers
+end
diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake
index 58761a129d4..5a09cd7ce41 100644
--- a/lib/tasks/gitlab/shell.rake
+++ b/lib/tasks/gitlab/shell.rake
@@ -5,42 +5,23 @@ namespace :gitlab do
warn_user_is_not_gitlab
default_version = Gitlab::Shell.version_required
- default_version_tag = 'v' + default_version
- args.with_defaults(tag: default_version_tag, repo: "https://gitlab.com/gitlab-org/gitlab-shell.git")
+ default_version_tag = "v#{default_version}"
+ args.with_defaults(tag: default_version_tag, repo: 'https://gitlab.com/gitlab-org/gitlab-shell.git')
- user = Gitlab.config.gitlab.user
- home_dir = Rails.env.test? ? Rails.root.join('tmp/tests') : Gitlab.config.gitlab.user_home
gitlab_url = Gitlab.config.gitlab.url
# gitlab-shell requires a / at the end of the url
gitlab_url += '/' unless gitlab_url.end_with?('/')
target_dir = Gitlab.config.gitlab_shell.path
- # Clone if needed
- if File.directory?(target_dir)
- Dir.chdir(target_dir) do
- system(*%W(Gitlab.config.git.bin_path} fetch --tags --quiet))
- system(*%W(Gitlab.config.git.bin_path} checkout --quiet #{default_version_tag}))
- end
- else
- system(*%W(#{Gitlab.config.git.bin_path} clone -- #{args.repo} #{target_dir}))
- end
+ checkout_or_clone_tag(tag: default_version_tag, repo: args.repo, target_dir: target_dir)
# Make sure we're on the right tag
Dir.chdir(target_dir) do
- # First try to checkout without fetching
- # to avoid stalling tests if the Internet is down.
- reseted = reset_to_commit(args)
-
- unless reseted
- system(*%W(#{Gitlab.config.git.bin_path} fetch origin))
- reset_to_commit(args)
- end
-
config = {
- user: user,
+ user: Gitlab.config.gitlab.user,
gitlab_url: gitlab_url,
http_settings: {self_signed_cert: false}.stringify_keys,
- auth_file: File.join(home_dir, ".ssh", "authorized_keys"),
+ auth_file: File.join(user_home, ".ssh", "authorized_keys"),
redis: {
bin: %x{which redis-cli}.chomp,
namespace: "resque:gitlab"
@@ -74,7 +55,7 @@ namespace :gitlab do
# be an issue since it is more than likely that there are no "normal"
# user accounts on a gitlab server). The alternative is for the admin to
# install a ruby (1.9.3+) in the global path.
- File.open(File.join(home_dir, ".ssh", "environment"), "w+") do |f|
+ File.open(File.join(user_home, ".ssh", "environment"), "w+") do |f|
f.puts "PATH=#{ENV['PATH']}"
end
@@ -142,15 +123,4 @@ namespace :gitlab do
puts "Quitting...".color(:red)
exit 1
end
-
- def reset_to_commit(args)
- tag, status = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} describe -- #{args.tag}))
-
- unless status.zero?
- tag, status = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} describe -- origin/#{args.tag}))
- end
-
- tag = tag.strip
- system(*%W(#{Gitlab.config.git.bin_path} reset --hard #{tag}))
- end
end
diff --git a/lib/tasks/gitlab/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake
deleted file mode 100644
index 74be413423a..00000000000
--- a/lib/tasks/gitlab/task_helpers.rake
+++ /dev/null
@@ -1,140 +0,0 @@
-module Gitlab
- class TaskAbortedByUserError < StandardError; end
-end
-
-require 'rainbow/ext/string'
-
-# Prevent StateMachine warnings from outputting during a cron task
-StateMachines::Machine.ignore_method_conflicts = true if ENV['CRON']
-
-namespace :gitlab do
-
- # Ask if the user wants to continue
- #
- # Returns "yes" the user chose to continue
- # Raises Gitlab::TaskAbortedByUserError if the user chose *not* to continue
- def ask_to_continue
- answer = prompt("Do you want to continue (yes/no)? ".color(:blue), %w{yes no})
- raise Gitlab::TaskAbortedByUserError unless answer == "yes"
- end
-
- # Check which OS is running
- #
- # It will primarily use lsb_relase to determine the OS.
- # It has fallbacks to Debian, SuSE, OS X and systems running systemd.
- def os_name
- os_name = run_command(%W(lsb_release -irs))
- os_name ||= if File.readable?('/etc/system-release')
- File.read('/etc/system-release')
- end
- os_name ||= if File.readable?('/etc/debian_version')
- debian_version = File.read('/etc/debian_version')
- "Debian #{debian_version}"
- end
- os_name ||= if File.readable?('/etc/SuSE-release')
- File.read('/etc/SuSE-release')
- end
- os_name ||= if os_x_version = run_command(%W(sw_vers -productVersion))
- "Mac OS X #{os_x_version}"
- end
- os_name ||= if File.readable?('/etc/os-release')
- File.read('/etc/os-release').match(/PRETTY_NAME=\"(.+)\"/)[1]
- end
- os_name.try(:squish!)
- end
-
- # Prompt the user to input something
- #
- # message - the message to display before input
- # choices - array of strings of acceptable answers or nil for any answer
- #
- # Returns the user's answer
- def prompt(message, choices = nil)
- begin
- print(message)
- answer = STDIN.gets.chomp
- end while choices.present? && !choices.include?(answer)
- answer
- end
-
- # Runs the given command and matches the output against the given pattern
- #
- # Returns nil if nothing matched
- # Returns the MatchData if the pattern matched
- #
- # see also #run_command
- # see also String#match
- def run_and_match(command, regexp)
- run_command(command).try(:match, regexp)
- end
-
- # Runs the given command
- #
- # Returns nil if the command was not found
- # Returns the output of the command otherwise
- #
- # see also #run_and_match
- def run_command(command)
- output, _ = Gitlab::Popen.popen(command)
- output
- rescue Errno::ENOENT
- '' # if the command does not exist, return an empty string
- end
-
- def uid_for(user_name)
- run_command(%W(id -u #{user_name})).chomp.to_i
- end
-
- def gid_for(group_name)
- begin
- Etc.getgrnam(group_name).gid
- rescue ArgumentError # no group
- "group #{group_name} doesn't exist"
- end
- end
-
- def warn_user_is_not_gitlab
- unless @warned_user_not_gitlab
- gitlab_user = Gitlab.config.gitlab.user
- current_user = run_command(%W(whoami)).chomp
- unless current_user == gitlab_user
- puts " Warning ".color(:black).background(:yellow)
- puts " You are running as user #{current_user.color(:magenta)}, we hope you know what you are doing."
- puts " Things may work\/fail for the wrong reasons."
- puts " For correct results you should run this as user #{gitlab_user.color(:magenta)}."
- puts ""
- end
- @warned_user_not_gitlab = true
- end
- end
-
- # Tries to configure git itself
- #
- # Returns true if all subcommands were successfull (according to their exit code)
- # Returns false if any or all subcommands failed.
- def auto_fix_git_config(options)
- if !@warned_user_not_gitlab
- command_success = options.map do |name, value|
- system(*%W(#{Gitlab.config.git.bin_path} config --global #{name} #{value}))
- end
-
- command_success.all?
- else
- false
- end
- end
-
- def all_repos
- Gitlab.config.repositories.storages.each do |name, path|
- IO.popen(%W(find #{path} -mindepth 2 -maxdepth 2 -type d -name *.git)) do |find|
- find.each_line do |path|
- yield path.chomp
- end
- end
- end
- end
-
- def repository_storage_paths_args
- Gitlab.config.repositories.storages.values
- end
-end
diff --git a/lib/tasks/gitlab/task_helpers.rb b/lib/tasks/gitlab/task_helpers.rb
new file mode 100644
index 00000000000..e128738b5f8
--- /dev/null
+++ b/lib/tasks/gitlab/task_helpers.rb
@@ -0,0 +1,190 @@
+require 'rainbow/ext/string'
+
+module Gitlab
+ TaskFailedError = Class.new(StandardError)
+ TaskAbortedByUserError = Class.new(StandardError)
+
+ module TaskHelpers
+ # Ask if the user wants to continue
+ #
+ # Returns "yes" the user chose to continue
+ # Raises Gitlab::TaskAbortedByUserError if the user chose *not* to continue
+ def ask_to_continue
+ answer = prompt("Do you want to continue (yes/no)? ".color(:blue), %w{yes no})
+ raise Gitlab::TaskAbortedByUserError unless answer == "yes"
+ end
+
+ # Check which OS is running
+ #
+ # It will primarily use lsb_relase to determine the OS.
+ # It has fallbacks to Debian, SuSE, OS X and systems running systemd.
+ def os_name
+ os_name = run_command(%W(lsb_release -irs))
+ os_name ||= if File.readable?('/etc/system-release')
+ File.read('/etc/system-release')
+ end
+ os_name ||= if File.readable?('/etc/debian_version')
+ debian_version = File.read('/etc/debian_version')
+ "Debian #{debian_version}"
+ end
+ os_name ||= if File.readable?('/etc/SuSE-release')
+ File.read('/etc/SuSE-release')
+ end
+ os_name ||= if os_x_version = run_command(%W(sw_vers -productVersion))
+ "Mac OS X #{os_x_version}"
+ end
+ os_name ||= if File.readable?('/etc/os-release')
+ File.read('/etc/os-release').match(/PRETTY_NAME=\"(.+)\"/)[1]
+ end
+ os_name.try(:squish!)
+ end
+
+ # Prompt the user to input something
+ #
+ # message - the message to display before input
+ # choices - array of strings of acceptable answers or nil for any answer
+ #
+ # Returns the user's answer
+ def prompt(message, choices = nil)
+ begin
+ print(message)
+ answer = STDIN.gets.chomp
+ end while choices.present? && !choices.include?(answer)
+ answer
+ end
+
+ # Runs the given command and matches the output against the given pattern
+ #
+ # Returns nil if nothing matched
+ # Returns the MatchData if the pattern matched
+ #
+ # see also #run_command
+ # see also String#match
+ def run_and_match(command, regexp)
+ run_command(command).try(:match, regexp)
+ end
+
+ # Runs the given command
+ #
+ # Returns '' if the command was not found
+ # Returns the output of the command otherwise
+ #
+ # see also #run_and_match
+ def run_command(command)
+ output, _ = Gitlab::Popen.popen(command)
+ output
+ rescue Errno::ENOENT
+ '' # if the command does not exist, return an empty string
+ end
+
+ # Runs the given command and raises a Gitlab::TaskFailedError exception if
+ # the command does not exit with 0
+ #
+ # Returns the output of the command otherwise
+ def run_command!(command)
+ output, status = Gitlab::Popen.popen(command)
+
+ raise Gitlab::TaskFailedError unless status.zero?
+
+ output
+ end
+
+ def uid_for(user_name)
+ run_command(%W(id -u #{user_name})).chomp.to_i
+ end
+
+ def gid_for(group_name)
+ begin
+ Etc.getgrnam(group_name).gid
+ rescue ArgumentError # no group
+ "group #{group_name} doesn't exist"
+ end
+ end
+
+ def warn_user_is_not_gitlab
+ unless @warned_user_not_gitlab
+ gitlab_user = Gitlab.config.gitlab.user
+ current_user = run_command(%W(whoami)).chomp
+ unless current_user == gitlab_user
+ puts " Warning ".color(:black).background(:yellow)
+ puts " You are running as user #{current_user.color(:magenta)}, we hope you know what you are doing."
+ puts " Things may work\/fail for the wrong reasons."
+ puts " For correct results you should run this as user #{gitlab_user.color(:magenta)}."
+ puts ""
+ end
+ @warned_user_not_gitlab = true
+ end
+ end
+
+ # Tries to configure git itself
+ #
+ # Returns true if all subcommands were successfull (according to their exit code)
+ # Returns false if any or all subcommands failed.
+ def auto_fix_git_config(options)
+ if !@warned_user_not_gitlab
+ command_success = options.map do |name, value|
+ system(*%W(#{Gitlab.config.git.bin_path} config --global #{name} #{value}))
+ end
+
+ command_success.all?
+ else
+ false
+ end
+ end
+
+ def all_repos
+ Gitlab.config.repositories.storages.each do |name, path|
+ IO.popen(%W(find #{path} -mindepth 2 -maxdepth 2 -type d -name *.git)) do |find|
+ find.each_line do |path|
+ yield path.chomp
+ end
+ end
+ end
+ end
+
+ def repository_storage_paths_args
+ Gitlab.config.repositories.storages.values
+ end
+
+ def user_home
+ Rails.env.test? ? Rails.root.join('tmp/tests') : Gitlab.config.gitlab.user_home
+ end
+
+ def checkout_or_clone_tag(tag:, repo:, target_dir:)
+ if Dir.exist?(target_dir)
+ checkout_tag(tag, target_dir)
+ else
+ clone_repo(repo, target_dir)
+ end
+
+ reset_to_tag(tag, target_dir)
+ end
+
+ def clone_repo(repo, target_dir)
+ run_command!(%W[#{Gitlab.config.git.bin_path} clone -- #{repo} #{target_dir}])
+ end
+
+ def checkout_tag(tag, target_dir)
+ run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} fetch --tags --quiet])
+ run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} checkout --quiet #{tag}])
+ end
+
+ def reset_to_tag(tag_wanted, target_dir)
+ tag =
+ begin
+ # First try to checkout without fetching
+ # to avoid stalling tests if the Internet is down.
+ run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} describe -- #{tag_wanted}])
+ rescue Gitlab::TaskFailedError
+ run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} fetch origin])
+ run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} describe -- origin/#{tag_wanted}])
+ end
+
+ if tag
+ run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} reset --hard #{tag.strip}])
+ else
+ raise Gitlab::TaskFailedError
+ end
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/workhorse.rake b/lib/tasks/gitlab/workhorse.rake
new file mode 100644
index 00000000000..baea94bf8ca
--- /dev/null
+++ b/lib/tasks/gitlab/workhorse.rake
@@ -0,0 +1,23 @@
+namespace :gitlab do
+ namespace :workhorse do
+ desc "GitLab | Install or upgrade gitlab-workhorse"
+ task :install, [:dir] => :environment do |t, args|
+ warn_user_is_not_gitlab
+ unless args.dir.present?
+ abort %(Please specify the directory where you want to install gitlab-workhorse:\n rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]")
+ end
+
+ tag = "v#{Gitlab::Workhorse.version}"
+ repo = 'https://gitlab.com/gitlab-org/gitlab-workhorse.git'
+
+ checkout_or_clone_tag(tag: tag, repo: repo, target_dir: args.dir)
+
+ _, status = Gitlab::Popen.popen(%w[which gmake])
+ command = status.zero? ? 'gmake' : 'make'
+
+ Dir.chdir(args.dir) do
+ run_command!([command])
+ end
+ end
+ end
+end