summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorGrzegorz Bizon <grzesiek.bizon@gmail.com>2016-11-25 11:05:34 +0100
committerGrzegorz Bizon <grzesiek.bizon@gmail.com>2016-11-25 11:05:34 +0100
commit895d97af87c66f4763e8d1fc0ef6cae19924b18d (patch)
tree09a596fb057d11bf94d7c3a323f38049061c6518 /lib
parent0a5a65df0c7d08e3ce041e10906549313a9ad156 (diff)
parentafe90d529c82566886d1f2513dd6bee4fa73ff94 (diff)
downloadgitlab-ce-895d97af87c66f4763e8d1fc0ef6cae19924b18d.tar.gz
Merge branch 'master' into fix/rename-mwbs-to-merge-when-pipeline-succeeds
* master: (312 commits) Fix bad selection on dropdown menu for tags filter Fixed issue boards scrolling with a lot of lists & issues You can only assign default_branch when editing a project ... Don't convert data which already is the target type Stop supporting Google and Azure as backup strategies renames some of the specs and adds changelog entry Fixed dragging issue moving wrong issue after multiple drags of issue Fixed issue boards issue sorting when dragging issue into list Rephrase some system notes to be compatible with new system note style Add missing JIRA file that redirects to the new location Fix documentation to create the `pg_trm` extension before creating the DB Document that we always use `do...end` for `before` in RSpec Backport Note#commands_changes from EE Log mv_namespace parameters Add default_branch attr to Project API payload in docs. Fix title case to sentence case properly escape username validation error message flash Remove header ids from University docs Add missing documentation. Added test that checks the correct select box is there for the LFS ... ... Conflicts: app/services/system_note_service.rb spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb spec/services/merge_requests/merge_when_pipeline_succeeds_service_spec.rb spec/services/system_note_service_spec.rb
Diffstat (limited to 'lib')
-rw-r--r--lib/api/broadcast_messages.rb5
-rw-r--r--lib/api/commit_statuses.rb2
-rw-r--r--lib/api/commits.rb7
-rw-r--r--lib/api/deployments.rb5
-rw-r--r--lib/api/entities.rb1
-rw-r--r--lib/api/environments.rb5
-rw-r--r--lib/api/groups.rb2
-rw-r--r--lib/api/merge_requests.rb10
-rw-r--r--lib/api/milestones.rb5
-rw-r--r--lib/api/pagination_params.rb24
-rw-r--r--lib/api/pipelines.rb26
-rw-r--r--lib/api/project_snippets.rb156
-rw-r--r--lib/api/projects.rb13
-rw-r--r--lib/api/sidekiq_metrics.rb36
-rw-r--r--lib/api/users.rb507
-rw-r--r--lib/api/variables.rb7
-rw-r--r--lib/ci/api/entities.rb6
-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/gitlab/chat_commands/command.rb1
-rw-r--r--lib/gitlab/chat_commands/deploy.rb57
-rw-r--r--lib/gitlab/chat_commands/issue_create.rb6
-rw-r--r--lib/gitlab/chat_commands/result.rb5
-rw-r--r--lib/gitlab/ci/build/credentials/base.rb13
-rw-r--r--lib/gitlab/ci/build/credentials/factory.rb27
-rw-r--r--lib/gitlab/ci/build/credentials/registry.rb24
-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/permissions.rb44
-rw-r--r--lib/gitlab/cycle_analytics/plan_event.rb2
-rw-r--r--lib/gitlab/ee_compat_check.rb8
-rw-r--r--lib/gitlab/file_detector.rb63
-rw-r--r--lib/gitlab/identifier.rb6
-rw-r--r--lib/gitlab/regex.rb16
-rw-r--r--lib/mattermost/presenter.rb40
37 files changed, 741 insertions, 452 deletions
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/commit_statuses.rb b/lib/api/commit_statuses.rb
index f54d4f06627..492884d162b 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -77,7 +77,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 33cb6fd3704..7a724487e02 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -210,6 +210,7 @@ module API
class Milestone < ProjectEntity
expose :due_date
+ expose :start_date
end
class Issue < ProjectEntity
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/groups.rb b/lib/api/groups.rb
index 48ad3b80ae0..fc39fdf4b67 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -33,7 +33,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
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 19f93c1c892..c8cfdb6a90e 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -41,15 +41,13 @@ 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'
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 +57,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
diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb
index 937c118779d..50d6109be3d 100644
--- a/lib/api/milestones.rb
+++ b/lib/api/milestones.rb
@@ -14,7 +14,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 +29,7 @@ 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'
end
get ":id/milestones" do
authorize! :read_milestone, user_project
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 2a0c8e1f2c0..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
@@ -22,6 +23,27 @@ module API
pipelines = PipelinesFinder.new(user_project).execute(scope: params[:scope])
present paginate(pipelines), with: Entities::Pipeline
end
+
+ desc 'Create a new pipeline' do
+ detail 'This feature was introduced in GitLab 8.14'
+ success Entities::Pipeline
+ end
+ params do
+ requires :ref, type: String, desc: 'Reference'
+ end
+ post ':id/pipeline' do
+ authorize! :create_pipeline, user_project
+
+ new_pipeline = Ci::CreatePipelineService.new(user_project,
+ current_user,
+ declared_params(include_missing: false))
+ .execute(ignore_skip_ci: true, save_on_errors: false)
+ if new_pipeline.persisted?
+ present new_pipeline, with: Entities::Pipeline
+ else
+ render_validation_error!(new_pipeline)
+ end
+ end
desc 'Gets a specific pipeline for the project' do
detail 'This feature was introduced in GitLab 8.11'
diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb
index ce1bf0d26d2..d0ee9c9a5b2 100644
--- a/lib/api/project_snippets.rb
+++ b/lib/api/project_snippets.rb
@@ -3,6 +3,9 @@ module API
class ProjectSnippets < Grape::API
before { authenticate! }
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
resource :projects do
helpers do
def handle_project_member_errors(errors)
@@ -18,111 +21,108 @@ module API
end
end
- # Get a project snippets
- #
- # Parameters:
- # id (required) - The ID of a project
- # Example Request:
- # GET /projects/:id/snippets
+ desc 'Get all project snippets' do
+ success Entities::ProjectSnippet
+ end
get ":id/snippets" do
present paginate(snippets_for_current_user), with: Entities::ProjectSnippet
end
- # Get a project snippet
- #
- # Parameters:
- # id (required) - The ID of a project
- # snippet_id (required) - The ID of a project snippet
- # Example Request:
- # GET /projects/:id/snippets/:snippet_id
+ desc 'Get a single project snippet' do
+ success Entities::ProjectSnippet
+ end
+ params do
+ requires :snippet_id, type: Integer, desc: 'The ID of a project snippet'
+ end
get ":id/snippets/:snippet_id" do
- @snippet = snippets_for_current_user.find(params[:snippet_id])
- present @snippet, with: Entities::ProjectSnippet
- end
-
- # Create a new project snippet
- #
- # Parameters:
- # id (required) - The ID of a project
- # title (required) - The title of a snippet
- # file_name (required) - The name of a snippet file
- # code (required) - The content of a snippet
- # visibility_level (required) - The snippet's visibility
- # Example Request:
- # POST /projects/:id/snippets
+ snippet = snippets_for_current_user.find(params[:snippet_id])
+ present snippet, with: Entities::ProjectSnippet
+ end
+
+ desc 'Create a new project snippet' do
+ success Entities::ProjectSnippet
+ end
+ params do
+ requires :title, type: String, desc: 'The title of the snippet'
+ requires :file_name, type: String, desc: 'The file name of the snippet'
+ requires :code, type: String, desc: 'The content of the snippet'
+ requires :visibility_level, type: Integer,
+ values: [Gitlab::VisibilityLevel::PRIVATE,
+ Gitlab::VisibilityLevel::INTERNAL,
+ Gitlab::VisibilityLevel::PUBLIC],
+ desc: 'The visibility level of the snippet'
+ end
post ":id/snippets" do
authorize! :create_project_snippet, user_project
- required_attributes! [:title, :file_name, :code, :visibility_level]
+ snippet_params = declared_params
+ snippet_params[:content] = snippet_params.delete(:code)
- attrs = attributes_for_keys [:title, :file_name, :visibility_level]
- attrs[:content] = params[:code] if params[:code].present?
- @snippet = CreateSnippetService.new(user_project, current_user,
- attrs).execute
+ snippet = CreateSnippetService.new(user_project, current_user, snippet_params).execute
- if @snippet.errors.any?
- render_validation_error!(@snippet)
+ if snippet.persisted?
+ present snippet, with: Entities::ProjectSnippet
else
- present @snippet, with: Entities::ProjectSnippet
+ render_validation_error!(snippet)
end
end
- # Update an existing project snippet
- #
- # Parameters:
- # id (required) - The ID of a project
- # snippet_id (required) - The ID of a project snippet
- # title (optional) - The title of a snippet
- # file_name (optional) - The name of a snippet file
- # code (optional) - The content of a snippet
- # visibility_level (optional) - The snippet's visibility
- # Example Request:
- # PUT /projects/:id/snippets/:snippet_id
+ desc 'Update an existing project snippet' do
+ success Entities::ProjectSnippet
+ end
+ params do
+ requires :snippet_id, type: Integer, desc: 'The ID of a project snippet'
+ optional :title, type: String, desc: 'The title of the snippet'
+ optional :file_name, type: String, desc: 'The file name of the snippet'
+ optional :code, type: String, desc: 'The content of the snippet'
+ optional :visibility_level, type: Integer,
+ values: [Gitlab::VisibilityLevel::PRIVATE,
+ Gitlab::VisibilityLevel::INTERNAL,
+ Gitlab::VisibilityLevel::PUBLIC],
+ desc: 'The visibility level of the snippet'
+ at_least_one_of :title, :file_name, :code, :visibility_level
+ end
put ":id/snippets/:snippet_id" do
- @snippet = snippets_for_current_user.find(params[:snippet_id])
- authorize! :update_project_snippet, @snippet
+ snippet = snippets_for_current_user.find_by(id: params.delete(:snippet_id))
+ not_found!('Snippet') unless snippet
+
+ authorize! :update_project_snippet, snippet
+
+ snippet_params = declared_params(include_missing: false)
+ snippet_params[:content] = snippet_params.delete(:code) if snippet_params[:code].present?
- attrs = attributes_for_keys [:title, :file_name, :visibility_level]
- attrs[:content] = params[:code] if params[:code].present?
+ UpdateSnippetService.new(user_project, current_user, snippet,
+ snippet_params).execute
- UpdateSnippetService.new(user_project, current_user, @snippet,
- attrs).execute
- if @snippet.errors.any?
- render_validation_error!(@snippet)
+ if snippet.persisted?
+ present snippet, with: Entities::ProjectSnippet
else
- present @snippet, with: Entities::ProjectSnippet
+ render_validation_error!(snippet)
end
end
- # Delete a project snippet
- #
- # Parameters:
- # id (required) - The ID of a project
- # snippet_id (required) - The ID of a project snippet
- # Example Request:
- # DELETE /projects/:id/snippets/:snippet_id
+ desc 'Delete a project snippet'
+ params do
+ requires :snippet_id, type: Integer, desc: 'The ID of a project snippet'
+ end
delete ":id/snippets/:snippet_id" do
- begin
- @snippet = snippets_for_current_user.find(params[:snippet_id])
- authorize! :update_project_snippet, @snippet
- @snippet.destroy
- rescue
- not_found!('Snippet')
- end
+ snippet = snippets_for_current_user.find_by(id: params[:snippet_id])
+ not_found!('Snippet') unless snippet
+
+ authorize! :admin_project_snippet, snippet
+ snippet.destroy
end
- # Get a raw project snippet
- #
- # Parameters:
- # id (required) - The ID of a project
- # snippet_id (required) - The ID of a project snippet
- # Example Request:
- # GET /projects/:id/snippets/:snippet_id/raw
+ desc 'Get a raw project snippet'
+ params do
+ requires :snippet_id, type: Integer, desc: 'The ID of a project snippet'
+ end
get ":id/snippets/:snippet_id/raw" do
- @snippet = snippets_for_current_user.find(params[:snippet_id])
+ snippet = snippets_for_current_user.find_by(id: params[:snippet_id])
+ not_found!('Snippet') unless snippet
env['api.format'] = :txt
content_type 'text/plain'
- present @snippet.content
+ present snippet.content
end
end
end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 6b856128c2e..ddfde178d30 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -438,6 +438,19 @@ module API
end
end
+ 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
+
+ link = user_project.project_group_links.find_by(group_id: params[:group_id])
+ not_found!('Group Link') unless link
+
+ link.destroy
+ no_content!
+ end
+
# Upload a file
#
# Parameters:
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/users.rb b/lib/api/users.rb
index aea328d2f8f..a73650dc361 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -4,89 +4,93 @@ module API
before { authenticate! }
resource :users, requirements: { uid: /[0-9]*/, id: /[0-9]*/ } do
- # Get a users list
- #
- # Example Request:
- # GET /users
- # GET /users?search=Admin
- # GET /users?username=root
- # GET /users?active=true
- # GET /users?external=true
- # GET /users?blocked=true
+ helpers do
+ params :optional_attributes do
+ optional :skype, type: String, desc: 'The Skype username'
+ optional :linkedin, type: String, desc: 'The LinkedIn username'
+ optional :twitter, type: String, desc: 'The Twitter username'
+ optional :website_url, type: String, desc: 'The website of the user'
+ optional :organization, type: String, desc: 'The organization of the user'
+ optional :projects_limit, type: Integer, desc: 'The number of projects a user can create'
+ optional :extern_uid, type: Integer, desc: 'The external authentication provider UID'
+ optional :provider, type: String, desc: 'The external provider'
+ optional :bio, type: String, desc: 'The biography of the user'
+ optional :location, type: String, desc: 'The location of the user'
+ optional :admin, type: Boolean, desc: 'Flag indicating the user is an administrator'
+ optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups'
+ optional :confirm, type: Boolean, desc: 'Flag indicating the account needs to be confirmed'
+ optional :external, type: Boolean, desc: 'Flag indicating the user is an external user'
+ all_or_none_of :extern_uid, :provider
+ end
+ end
+
+ desc 'Get the list of users' do
+ success Entities::UserBasic
+ end
+ params do
+ optional :username, type: String, desc: 'Get a single user with a specific username'
+ optional :search, type: String, desc: 'Search for a username'
+ 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'
+ end
get do
unless can?(current_user, :read_users_list, nil)
render_api_error!("Not authorized.", 403)
end
if params[:username].present?
- @users = User.where(username: params[:username])
+ users = User.where(username: params[:username])
else
- @users = User.all
- @users = @users.active if to_boolean(params[:active])
- @users = @users.search(params[:search]) if params[:search].present?
- @users = @users.blocked if to_boolean(params[:blocked])
- @users = @users.external if to_boolean(params[:external]) && current_user.is_admin?
- @users = paginate @users
+ users = User.all
+ users = users.active if params[:active]
+ users = users.search(params[:search]) if params[:search].present?
+ users = users.blocked if params[:blocked]
+ users = users.external if params[:external] && current_user.is_admin?
end
- if current_user.is_admin?
- present @users, with: Entities::UserFull
- else
- present @users, with: Entities::UserBasic
- end
+ entity = current_user.is_admin? ? Entities::UserFull : Entities::UserBasic
+ present paginate(users), with: entity
end
- # Get a single user
- #
- # Parameters:
- # id (required) - The ID of a user
- # Example Request:
- # GET /users/:id
+ desc 'Get a single user' do
+ success Entities::UserBasic
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ end
get ":id" do
- @user = User.find(params[:id])
+ user = User.find_by(id: params[:id])
+ not_found!('User') unless user
if current_user && current_user.is_admin?
- present @user, with: Entities::UserFull
- elsif can?(current_user, :read_user, @user)
- present @user, with: Entities::User
+ present user, with: Entities::UserFull
+ elsif can?(current_user, :read_user, user)
+ present user, with: Entities::User
else
render_api_error!("User not found.", 404)
end
end
- # Create user. Available only for admin
- #
- # Parameters:
- # email (required) - Email
- # password (required) - Password
- # name (required) - Name
- # username (required) - Name
- # skype - Skype ID
- # linkedin - Linkedin
- # twitter - Twitter account
- # website_url - Website url
- # organization - Organization
- # projects_limit - Number of projects user can create
- # extern_uid - External authentication provider UID
- # provider - External provider
- # bio - Bio
- # location - Location of the user
- # admin - User is admin - true or false (default)
- # can_create_group - User can create groups - true or false
- # confirm - Require user confirmation - true (default) or false
- # external - Flags the user as external - true or false(default)
- # Example Request:
- # POST /users
+ desc 'Create a user. Available only for admins.' do
+ success Entities::UserFull
+ end
+ params do
+ requires :email, type: String, desc: 'The email of the user'
+ requires :password, type: String, desc: 'The password of the new user'
+ requires :name, type: String, desc: 'The name of the user'
+ requires :username, type: String, desc: 'The username of the user'
+ use :optional_attributes
+ end
post do
authenticated_as_admin!
- required_attributes! [:email, :password, :name, :username]
- attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :bio, :location, :can_create_group, :admin, :confirm, :external, :organization]
- admin = attrs.delete(:admin)
- confirm = !(attrs.delete(:confirm) =~ /(false|f|no|0)$/i)
- user = User.build_user(attrs)
- user.admin = admin unless admin.nil?
+
+ # Filter out params which are used later
+ identity_attrs = params.slice(:provider, :extern_uid)
+ confirm = params.delete(:confirm)
+
+ user = User.build_user(declared_params(include_missing: false))
user.skip_confirmation! unless confirm
- identity_attrs = attributes_for_keys [:provider, :extern_uid]
if identity_attrs.any?
user.identities.build(identity_attrs)
@@ -107,46 +111,41 @@ module API
end
end
- # Update user. Available only for admin
- #
- # Parameters:
- # email - Email
- # name - Name
- # password - Password
- # skype - Skype ID
- # linkedin - Linkedin
- # twitter - Twitter account
- # website_url - Website url
- # organization - Organization
- # projects_limit - Limit projects each user can create
- # bio - Bio
- # location - Location of the user
- # admin - User is admin - true or false (default)
- # can_create_group - User can create groups - true or false
- # external - Flags the user as external - true or false(default)
- # Example Request:
- # PUT /users/:id
+ desc 'Update a user. Available only for admins.' do
+ success Entities::UserFull
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ optional :email, type: String, desc: 'The email of the user'
+ optional :password, type: String, desc: 'The password of the new user'
+ optional :name, type: String, desc: 'The name of the user'
+ optional :username, type: String, desc: 'The username of the user'
+ use :optional_attributes
+ at_least_one_of :email, :password, :name, :username, :skype, :linkedin,
+ :twitter, :website_url, :organization, :projects_limit,
+ :extern_uid, :provider, :bio, :location, :admin,
+ :can_create_group, :confirm, :external
+ end
put ":id" do
authenticated_as_admin!
- attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :website_url, :projects_limit, :username, :bio, :location, :can_create_group, :admin, :external, :organization]
- user = User.find(params[:id])
+ user = User.find_by(id: params.delete(:id))
not_found!('User') unless user
- admin = attrs.delete(:admin)
- user.admin = admin unless admin.nil?
-
- conflict!('Email has already been taken') if attrs[:email] &&
- User.where(email: attrs[:email]).
+ conflict!('Email has already been taken') if params[:email] &&
+ User.where(email: params[:email]).
where.not(id: user.id).count > 0
- conflict!('Username has already been taken') if attrs[:username] &&
- User.where(username: attrs[:username]).
+ conflict!('Username has already been taken') if params[:username] &&
+ User.where(username: params[:username]).
where.not(id: user.id).count > 0
- identity_attrs = attributes_for_keys [: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])
+
if identity
identity.update_attributes(identity_attrs)
else
@@ -155,28 +154,33 @@ module API
end
end
- if user.update_attributes(attrs)
+ # Delete already handled parameters
+ user_params.delete(:extern_uid)
+ user_params.delete(:provider)
+
+ if user.update_attributes(user_params)
present user, with: Entities::UserFull
else
render_validation_error!(user)
end
end
- # Add ssh key to a specified user. Only available to admin users.
- #
- # Parameters:
- # id (required) - The ID of a user
- # key (required) - New SSH Key
- # title (required) - New SSH Key's title
- # Example Request:
- # POST /users/:id/keys
+ desc 'Add an SSH key to a specified user. Available only for admins.' do
+ success Entities::SSHKey
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ requires :key, type: String, desc: 'The new SSH key'
+ requires :title, type: String, desc: 'The title of the new SSH key'
+ end
post ":id/keys" do
authenticated_as_admin!
- required_attributes! [:title, :key]
- user = User.find(params[:id])
- attrs = attributes_for_keys [:title, :key]
- key = user.keys.new attrs
+ user = User.find_by(id: params.delete(:id))
+ not_found!('User') unless user
+
+ key = user.keys.new(declared_params(include_missing: false))
+
if key.save
present key, with: Entities::SSHKey
else
@@ -184,55 +188,55 @@ module API
end
end
- # Get ssh keys of a specified user. Only available to admin users.
- #
- # Parameters:
- # uid (required) - The ID of a user
- # Example Request:
- # GET /users/:uid/keys
- get ':uid/keys' do
+ desc 'Get the SSH keys of a specified user. Available only for admins.' do
+ success Entities::SSHKey
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ end
+ get ':id/keys' do
authenticated_as_admin!
- user = User.find_by(id: params[:uid])
+
+ user = User.find_by(id: params[:id])
not_found!('User') unless user
present user.keys, with: Entities::SSHKey
end
- # Delete existing ssh key of a specified user. Only available to admin
- # users.
- #
- # Parameters:
- # uid (required) - The ID of a user
- # id (required) - SSH Key ID
- # Example Request:
- # DELETE /users/:uid/keys/:id
- delete ':uid/keys/:id' do
+ desc 'Delete an existing SSH key from a specified user. Available only for admins.' do
+ success Entities::SSHKey
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ requires :key_id, type: Integer, desc: 'The ID of the SSH key'
+ end
+ delete ':id/keys/:key_id' do
authenticated_as_admin!
- user = User.find_by(id: params[:uid])
+
+ user = User.find_by(id: params[:id])
not_found!('User') unless user
- begin
- key = user.keys.find params[:id]
- key.destroy
- rescue ActiveRecord::RecordNotFound
- not_found!('Key')
- end
+ key = user.keys.find_by(id: params[:key_id])
+ not_found!('Key') unless key
+
+ present key.destroy, with: Entities::SSHKey
end
- # Add email to a specified user. Only available to admin users.
- #
- # Parameters:
- # id (required) - The ID of a user
- # email (required) - Email address
- # Example Request:
- # POST /users/:id/emails
+ desc 'Add an email address to a specified user. Available only for admins.' do
+ success Entities::Email
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ requires :email, type: String, desc: 'The email of the user'
+ end
post ":id/emails" do
authenticated_as_admin!
- required_attributes! [:email]
- user = User.find(params[:id])
- attrs = attributes_for_keys [:email]
- email = user.emails.new attrs
+ user = User.find_by(id: params.delete(:id))
+ not_found!('User') unless user
+
+ email = user.emails.new(declared_params(include_missing: false))
+
if email.save
NotificationService.new.new_email(email)
present email, with: Entities::Email
@@ -241,98 +245,91 @@ module API
end
end
- # Get emails of a specified user. Only available to admin users.
- #
- # Parameters:
- # uid (required) - The ID of a user
- # Example Request:
- # GET /users/:uid/emails
- get ':uid/emails' do
+ desc 'Get the emails addresses of a specified user. Available only for admins.' do
+ success Entities::Email
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ end
+ get ':id/emails' do
authenticated_as_admin!
- user = User.find_by(id: params[:uid])
+ user = User.find_by(id: params[:id])
not_found!('User') unless user
present user.emails, with: Entities::Email
end
- # Delete existing email of a specified user. Only available to admin
- # users.
- #
- # Parameters:
- # uid (required) - The ID of a user
- # id (required) - Email ID
- # Example Request:
- # DELETE /users/:uid/emails/:id
- delete ':uid/emails/:id' do
+ desc 'Delete an email address of a specified user. Available only for admins.' do
+ success Entities::Email
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ requires :email_id, type: Integer, desc: 'The ID of the email'
+ end
+ delete ':id/emails/:email_id' do
authenticated_as_admin!
- user = User.find_by(id: params[:uid])
+ user = User.find_by(id: params[:id])
not_found!('User') unless user
- begin
- email = user.emails.find params[:id]
- email.destroy
+ email = user.emails.find_by(id: params[:email_id])
+ not_found!('Email') unless email
- user.update_secondary_emails!
- rescue ActiveRecord::RecordNotFound
- not_found!('Email')
- end
+ email.destroy
+ user.update_secondary_emails!
end
- # Delete user. Available only for admin
- #
- # Example Request:
- # DELETE /users/:id
+ desc 'Delete a user. Available only for admins.' do
+ success Entities::Email
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ end
delete ":id" do
authenticated_as_admin!
user = User.find_by(id: params[:id])
+ not_found!('User') unless user
- if user
- DeleteUserService.new(current_user).execute(user)
- else
- not_found!('User')
- end
+ DeleteUserService.new(current_user).execute(user)
end
- # Block user. Available only for admin
- #
- # Example Request:
- # PUT /users/:id/block
+ desc 'Block a user. Available only for admins.'
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ end
put ':id/block' do
authenticated_as_admin!
user = User.find_by(id: params[:id])
+ not_found!('User') unless user
- if !user
- not_found!('User')
- elsif !user.ldap_blocked?
+ if !user.ldap_blocked?
user.block
else
forbidden!('LDAP blocked users cannot be modified by the API')
end
end
- # Unblock user. Available only for admin
- #
- # Example Request:
- # PUT /users/:id/unblock
+ desc 'Unblock a user. Available only for admins.'
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ end
put ':id/unblock' do
authenticated_as_admin!
user = User.find_by(id: params[:id])
+ not_found!('User') unless user
- if !user
- not_found!('User')
- elsif user.ldap_blocked?
+ if user.ldap_blocked?
forbidden!('LDAP blocked users cannot be unblocked by the API')
else
user.activate
end
end
- desc 'Get contribution events of a specified user' do
+ desc 'Get the contribution events of a specified user' do
detail 'This feature was introduced in GitLab 8.13.'
success Entities::Event
end
params do
- requires :id, type: String, desc: 'The user ID'
+ requires :id, type: Integer, desc: 'The ID of the user'
end
get ':id/events' do
user = User.find_by(id: params[:id])
@@ -349,43 +346,43 @@ module API
end
resource :user do
- # Get currently authenticated user
- #
- # Example Request:
- # GET /user
+ desc 'Get the currently authenticated user' do
+ success Entities::UserFull
+ end
get do
- present @current_user, with: Entities::UserFull
+ present current_user, with: Entities::UserFull
end
- # Get currently authenticated user's keys
- #
- # Example Request:
- # GET /user/keys
+ desc "Get the currently authenticated user's SSH keys" do
+ success Entities::SSHKey
+ end
get "keys" do
present current_user.keys, with: Entities::SSHKey
end
- # Get single key owned by currently authenticated user
- #
- # Example Request:
- # GET /user/keys/:id
- get "keys/:id" do
- key = current_user.keys.find params[:id]
+ desc 'Get a single key owned by currently authenticated user' do
+ success Entities::SSHKey
+ end
+ params do
+ requires :key_id, type: Integer, desc: 'The ID of the SSH key'
+ end
+ get "keys/:key_id" do
+ key = current_user.keys.find_by(id: params[:key_id])
+ not_found!('Key') unless key
+
present key, with: Entities::SSHKey
end
- # Add new ssh key to currently authenticated user
- #
- # Parameters:
- # key (required) - New SSH Key
- # title (required) - New SSH Key's title
- # Example Request:
- # POST /user/keys
+ desc 'Add a new SSH key to the currently authenticated user' do
+ success Entities::SSHKey
+ end
+ params do
+ requires :key, type: String, desc: 'The new SSH key'
+ requires :title, type: String, desc: 'The title of the new SSH key'
+ end
post "keys" do
- required_attributes! [:title, :key]
+ key = current_user.keys.new(declared_params)
- attrs = attributes_for_keys [:title, :key]
- key = current_user.keys.new attrs
if key.save
present key, with: Entities::SSHKey
else
@@ -393,48 +390,48 @@ module API
end
end
- # Delete existing ssh key of currently authenticated user
- #
- # Parameters:
- # id (required) - SSH Key ID
- # Example Request:
- # DELETE /user/keys/:id
- delete "keys/:id" do
- begin
- key = current_user.keys.find params[:id]
- key.destroy
- rescue
- end
+ desc 'Delete an SSH key from the currently authenticated user' do
+ success Entities::SSHKey
+ end
+ params do
+ requires :key_id, type: Integer, desc: 'The ID of the SSH key'
+ end
+ delete "keys/:key_id" do
+ key = current_user.keys.find_by(id: params[:key_id])
+ not_found!('Key') unless key
+
+ present key.destroy, with: Entities::SSHKey
end
- # Get currently authenticated user's emails
- #
- # Example Request:
- # GET /user/emails
+ desc "Get the currently authenticated user's email addresses" do
+ success Entities::Email
+ end
get "emails" do
present current_user.emails, with: Entities::Email
end
- # Get single email owned by currently authenticated user
- #
- # Example Request:
- # GET /user/emails/:id
- get "emails/:id" do
- email = current_user.emails.find params[:id]
+ desc 'Get a single email address owned by the currently authenticated user' do
+ success Entities::Email
+ end
+ params do
+ requires :email_id, type: Integer, desc: 'The ID of the email'
+ end
+ get "emails/:email_id" do
+ email = current_user.emails.find_by(id: params[:email_id])
+ not_found!('Email') unless email
+
present email, with: Entities::Email
end
- # Add new email to currently authenticated user
- #
- # Parameters:
- # email (required) - Email address
- # Example Request:
- # POST /user/emails
+ desc 'Add new email address to the currently authenticated user' do
+ success Entities::Email
+ end
+ params do
+ requires :email, type: String, desc: 'The new email'
+ end
post "emails" do
- required_attributes! [:email]
+ email = current_user.emails.new(declared_params)
- attrs = attributes_for_keys [:email]
- email = current_user.emails.new attrs
if email.save
NotificationService.new.new_email(email)
present email, with: Entities::Email
@@ -443,20 +440,16 @@ module API
end
end
- # Delete existing email of currently authenticated user
- #
- # Parameters:
- # id (required) - EMail ID
- # Example Request:
- # DELETE /user/emails/:id
- delete "emails/:id" do
- begin
- email = current_user.emails.find params[:id]
- email.destroy
+ desc 'Delete an email address from the currently authenticated user'
+ params do
+ requires :email_id, type: Integer, desc: 'The ID of the email'
+ end
+ delete "emails/:email_id" do
+ email = current_user.emails.find_by(id: params[:email_id])
+ not_found!('Email') unless email
- current_user.update_secondary_emails!
- rescue
- end
+ email.destroy
+ current_user.update_secondary_emails!
end
end
end
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/ci/api/entities.rb b/lib/ci/api/entities.rb
index 66c05773b68..792ff628b09 100644
--- a/lib/ci/api/entities.rb
+++ b/lib/ci/api/entities.rb
@@ -32,6 +32,10 @@ module Ci
expose :artifacts_file, using: ArtifactFile, if: ->(build, _) { build.artifacts? }
end
+ class BuildCredentials < Grape::Entity
+ expose :type, :url, :username, :password
+ end
+
class BuildDetails < Build
expose :commands
expose :repo_url
@@ -50,6 +54,8 @@ module Ci
expose :variables
expose :depends_on_builds, using: Build
+
+ expose :credentials, using: BuildCredentials
end
class Runner < Grape::Entity
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/gitlab/chat_commands/command.rb b/lib/gitlab/chat_commands/command.rb
index 5f131703d40..0ec358debc7 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::Deploy,
].freeze
def execute
diff --git a/lib/gitlab/chat_commands/deploy.rb b/lib/gitlab/chat_commands/deploy.rb
new file mode 100644
index 00000000000..0eed1fce0dc
--- /dev/null
+++ b/lib/gitlab/chat_commands/deploy.rb
@@ -0,0 +1,57 @@
+module Gitlab
+ module ChatCommands
+ class Deploy < BaseCommand
+ include Gitlab::Routing.url_helpers
+
+ def self.match(text)
+ /\Adeploy\s+(?<from>.*)\s+to+\s+(?<to>.*)\z/.match(text)
+ end
+
+ def self.help_message
+ 'deploy <environment> to <target-environment>'
+ end
+
+ def self.available?(project)
+ project.builds_enabled?
+ end
+
+ def self.allowed?(project, user)
+ can?(user, :create_deployment, project)
+ end
+
+ def execute(match)
+ from = match[:from]
+ to = match[:to]
+
+ actions = find_actions(from, to)
+ return unless actions.present?
+
+ if actions.one?
+ play!(from, to, actions.first)
+ else
+ Result.new(:error, 'Too many actions defined')
+ end
+ end
+
+ private
+
+ def play!(from, to, action)
+ new_action = action.play(current_user)
+
+ Result.new(:success, "Deployment from #{from} to #{to} started. Follow the progress: #{url(new_action)}.")
+ end
+
+ def find_actions(from, to)
+ environment = project.environments.find_by(name: from)
+ return unless environment
+
+ environment.actions_for(to).select(&:starts_environment?)
+ end
+
+ def url(subject)
+ polymorphic_url(
+ [ subject.project.namespace.becomes(Namespace), subject.project, subject ])
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/chat_commands/issue_create.rb b/lib/gitlab/chat_commands/issue_create.rb
index 98338ebfa27..99c1382af44 100644
--- a/lib/gitlab/chat_commands/issue_create.rb
+++ b/lib/gitlab/chat_commands/issue_create.rb
@@ -2,7 +2,9 @@ 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+create\s+(?<title>[^\n]*)\n*(?<description>(.|\n)*)/.match(text)
end
def self.help_message
@@ -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/result.rb b/lib/gitlab/chat_commands/result.rb
new file mode 100644
index 00000000000..324d7ef43a3
--- /dev/null
+++ b/lib/gitlab/chat_commands/result.rb
@@ -0,0 +1,5 @@
+module Gitlab
+ module ChatCommands
+ Result = Struct.new(:type, :message)
+ end
+end
diff --git a/lib/gitlab/ci/build/credentials/base.rb b/lib/gitlab/ci/build/credentials/base.rb
new file mode 100644
index 00000000000..29a7a27c963
--- /dev/null
+++ b/lib/gitlab/ci/build/credentials/base.rb
@@ -0,0 +1,13 @@
+module Gitlab
+ module Ci
+ module Build
+ module Credentials
+ class Base
+ def type
+ self.class.name.demodulize.underscore
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/build/credentials/factory.rb b/lib/gitlab/ci/build/credentials/factory.rb
new file mode 100644
index 00000000000..2423aa8857d
--- /dev/null
+++ b/lib/gitlab/ci/build/credentials/factory.rb
@@ -0,0 +1,27 @@
+module Gitlab
+ module Ci
+ module Build
+ module Credentials
+ class Factory
+ def initialize(build)
+ @build = build
+ end
+
+ def create!
+ credentials.select(&:valid?)
+ end
+
+ private
+
+ def credentials
+ providers.map { |provider| provider.new(@build) }
+ end
+
+ def providers
+ [Registry]
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/build/credentials/registry.rb b/lib/gitlab/ci/build/credentials/registry.rb
new file mode 100644
index 00000000000..55eafcaed10
--- /dev/null
+++ b/lib/gitlab/ci/build/credentials/registry.rb
@@ -0,0 +1,24 @@
+module Gitlab
+ module Ci
+ module Build
+ module Credentials
+ class Registry < Base
+ attr_reader :username, :password
+
+ def initialize(build)
+ @username = 'gitlab-ci-token'
+ @password = build.token
+ end
+
+ def url
+ Gitlab.config.registry.host_port
+ end
+
+ def valid?
+ Gitlab.config.registry.enabled
+ end
+ end
+ end
+ end
+ end
+end
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/permissions.rb b/lib/gitlab/cycle_analytics/permissions.rb
new file mode 100644
index 00000000000..bef3b95ff1b
--- /dev/null
+++ b/lib/gitlab/cycle_analytics/permissions.rb
@@ -0,0 +1,44 @@
+module Gitlab
+ module CycleAnalytics
+ class Permissions
+ STAGE_PERMISSIONS = {
+ issue: :read_issue,
+ code: :read_merge_request,
+ test: :read_build,
+ review: :read_merge_request,
+ staging: :read_build,
+ production: :read_issue,
+ }.freeze
+
+ def self.get(*args)
+ new(*args).get
+ end
+
+ def initialize(user:, project:)
+ @user = user
+ @project = project
+ @stage_permission_hash = {}
+ end
+
+ def get
+ ::CycleAnalytics::STAGES.each do |stage|
+ @stage_permission_hash[stage] = authorized_stage?(stage)
+ end
+
+ @stage_permission_hash
+ end
+
+ private
+
+ def authorized_stage?(stage)
+ return false unless authorize_project(:read_cycle_analytics)
+
+ STAGE_PERMISSIONS[stage] ? authorize_project(STAGE_PERMISSIONS[stage]) : true
+ end
+
+ def authorize_project(permission)
+ Ability.allowed?(@user, permission, @project)
+ end
+ end
+ end
+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/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/file_detector.rb b/lib/gitlab/file_detector.rb
new file mode 100644
index 00000000000..1d93a67dc56
--- /dev/null
+++ b/lib/gitlab/file_detector.rb
@@ -0,0 +1,63 @@
+require 'set'
+
+module Gitlab
+ # Module that can be used to detect if a path points to a special file such as
+ # a README or a CONTRIBUTING file.
+ module FileDetector
+ PATTERNS = {
+ readme: /\Areadme/i,
+ changelog: /\A(changelog|history|changes|news)/i,
+ license: /\A(licen[sc]e|copying)(\..+|\z)/i,
+ contributing: /\Acontributing/i,
+ version: 'version',
+ gitignore: '.gitignore',
+ koding: '.koding.yml',
+ gitlab_ci: '.gitlab-ci.yml',
+ avatar: /\Alogo\.(png|jpg|gif)\z/
+ }
+
+ # Returns an Array of file types based on the given paths.
+ #
+ # This method can be used to check if a list of file paths (e.g. of changed
+ # files) involve any special files such as a README or a LICENSE file.
+ #
+ # Example:
+ #
+ # types_in_paths(%w{README.md foo/bar.txt}) # => [:readme]
+ def self.types_in_paths(paths)
+ types = Set.new
+
+ paths.each do |path|
+ type = type_of(path)
+
+ types << type if type
+ end
+
+ types.to_a
+ end
+
+ # Returns the type of a file path, or nil if none could be detected.
+ #
+ # Returned types are Symbols such as `:readme`, `:version`, etc.
+ #
+ # Example:
+ #
+ # type_of('README.md') # => :readme
+ # type_of('VERSION') # => :version
+ def self.type_of(path)
+ name = File.basename(path)
+
+ PATTERNS.each do |type, search|
+ did_match = if search.is_a?(Regexp)
+ name =~ search
+ else
+ name.casecmp(search) == 0
+ end
+
+ return type if did_match
+ end
+
+ nil
+ end
+ end
+end
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/regex.rb b/lib/gitlab/regex.rb
index 47ea8b7e82e..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
- NAMESPACE_REGEX_STR = "(?:#{NAMESPACE_REGEX_STR_SIMPLE})(?<!\.git|\.atom)".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/mattermost/presenter.rb b/lib/mattermost/presenter.rb
index bfbb089eb02..67eda983a74 100644
--- a/lib/mattermost/presenter.rb
+++ b/lib/mattermost/presenter.rb
@@ -24,20 +24,22 @@ module Mattermost
end
end
- def present(resource)
- return not_found unless resource
-
- if resource.respond_to?(:count)
- if resource.count > 1
- return multiple_resources(resource)
- elsif resource.count == 0
- return not_found
+ def present(subject)
+ return not_found unless subject
+
+ if subject.is_a?(Gitlab::ChatCommands::Result)
+ show_result(subject)
+ elsif subject.respond_to?(:count)
+ if subject.many?
+ multiple_resources(subject)
+ elsif subject.none?
+ not_found
else
- resource = resource.first
+ single_resource(subject)
end
+ else
+ single_resource(subject)
end
-
- single_resource(resource)
end
def access_denied
@@ -46,6 +48,15 @@ module Mattermost
private
+ def show_result(result)
+ case result.type
+ when :success
+ in_channel_response(result.message)
+ else
+ ephemeral_response(result.message)
+ end
+ end
+
def not_found
ephemeral_response("404 not found! GitLab couldn't find what you were looking for! :boom:")
end
@@ -54,7 +65,7 @@ module Mattermost
return error(resource) if resource.errors.any? || !resource.persisted?
message = "### #{title(resource)}"
- message << "\n\n#{resource.description}" if resource.description
+ message << "\n\n#{resource.description}" if resource.try(:description)
in_channel_response(message)
end
@@ -74,7 +85,10 @@ module Mattermost
end
def title(resource)
- "[#{resource.to_reference} #{resource.title}](#{url(resource)})"
+ reference = resource.try(:to_reference) || resource.try(:id)
+ title = resource.try(:title) || resource.try(:name)
+
+ "[#{reference} #{title}](#{url(resource)})"
end
def header_with_list(header, items)