From 01f238893aaf8f5aa24eec7e33edc479d4e1315d Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 27 Oct 2016 14:04:43 +0200 Subject: Rename MWBS service to Merge When Pipeline Succeeds --- lib/api/merge_requests.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 4176c7eec06..8c55f09a9ce 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -205,11 +205,13 @@ module API } 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) + ::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 @@ -223,7 +225,9 @@ 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 -- cgit v1.2.1 From c6a4f9fc5b98024d4af872b0009b90c36bc2e595 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 21 Nov 2016 11:27:28 +0100 Subject: Update some docs to reflect MWPS name change --- lib/api/merge_requests.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 8c55f09a9ce..19f93c1c892 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -181,7 +181,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 @@ -217,7 +217,7 @@ module API 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 -- cgit v1.2.1 From 4d2e7894efa36cc1b5de9432e25fcf22b6cf1d59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 23 Nov 2016 19:18:19 +0100 Subject: Make API::Helpers find a project with only one query MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/api/helpers.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 2c593dbb4ea..60067758e95 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -76,7 +76,12 @@ module API end def find_project(id) - project = Project.find_with_namespace(id) || Project.find_by(id: id) + project = + if id =~ /^\d+$/ + Project.find_by(id: id) + else + Project.find_with_namespace(id) + end if can?(current_user, :read_project, project) project -- cgit v1.2.1 From 304163becba3610a99dfff644c13972a2f54ed3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 24 Nov 2016 13:22:38 +0100 Subject: API: Use `#find_project` in API::Triggers and API::Services MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/api/services.rb | 2 +- lib/api/triggers.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') 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/triggers.rb b/lib/api/triggers.rb index 569598fbd2c..bb4de39def1 100644 --- a/lib/api/triggers.rb +++ b/lib/api/triggers.rb @@ -13,7 +13,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 -- cgit v1.2.1 From 9dfbfbb2d10dfc6297acff1b59dfbaf43b848d96 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Thu, 24 Nov 2016 13:30:53 +0100 Subject: Don't convert data which already is the target type --- lib/api/commit_statuses.rb | 2 +- lib/api/commits.rb | 2 +- lib/api/groups.rb | 2 +- lib/api/variables.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') 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 0319d076ecb..2670a2d413a 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -48,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 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/variables.rb b/lib/api/variables.rb index 90f904b8a12..f623b1dfe9f 100644 --- a/lib/api/variables.rb +++ b/lib/api/variables.rb @@ -30,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 -- cgit v1.2.1 From 4f5ed812325845f263fc9b566651c1179b5c24bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 24 Nov 2016 14:40:35 +0100 Subject: API: Introduce `#find_project!` which also check access permission MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/api/helpers.rb | 17 ++++++++++------- lib/api/projects.rb | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 60067758e95..42f4c2ccf9d 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,12 +76,15 @@ module API end def find_project(id) - project = - if id =~ /^\d+$/ - Project.find_by(id: id) - else - Project.find_with_namespace(id) - end + 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 diff --git a/lib/api/projects.rb b/lib/api/projects.rb index ddfde178d30..2ea3c433ae2 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -379,7 +379,7 @@ module API # POST /projects/:id/fork/:forked_from_id post ":id/fork/:forked_from_id" do authenticated_as_admin! - forked_from_project = find_project(params[:forked_from_id]) + 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) -- cgit v1.2.1 From 81ba3f9177fcfd76f6b3b715c572ce4920398345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 24 Nov 2016 16:58:32 +0100 Subject: API: Introduce `#find_group!` which also check access permission MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/api/groups.rb | 8 ++++---- lib/api/helpers.rb | 10 +++++++++- lib/api/helpers/members_helpers.rb | 2 +- lib/api/issues.rb | 2 +- 4 files changed, 15 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 48ad3b80ae0..a3489c4eb92 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -82,7 +82,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 +96,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 @@ -111,7 +111,7 @@ module API success Entities::Project 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 diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 42f4c2ccf9d..0d3ddb89dc3 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -105,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 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..2fea71870b8 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -68,7 +68,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 -- cgit v1.2.1 From d71ad49fc570ef617d0bbf99af53596ef5d48892 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Mon, 21 Nov 2016 22:27:10 +0100 Subject: Accept a valid ref for issue show For example, now we support `/gitlab issue show #1`. Where the # used to trip the regex. --- lib/gitlab/chat_commands/issue_show.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') 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+(?\d+)/.match(text) + /\Aissue\s+show\s+#{Issue.reference_prefix}?(?\d+)/.match(text) end def self.help_message -- cgit v1.2.1 From 92b2c74ce14238c1032bd9faac6d178d25433532 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 24 Nov 2016 10:40:44 +0100 Subject: Refresh project authorizations using a Redis lease When I proposed using serializable transactions I was hoping we would be able to refresh data of individual users concurrently. Unfortunately upon closer inspection it was revealed this was not the case. This could result in a lot of queries failing due to serialization errors, overloading the database in the process (given enough workers trying to update the target table). To work around this we're now using a Redis lease that is cancelled upon completion. This ensures we can update the data of different users concurrently without overloading the database. The code will try to obtain the lease until it succeeds, waiting at least 1 second between retries. This is necessary as we may otherwise end up _not_ updating the data which is not an option. --- lib/gitlab/database.rb | 7 ------- 1 file changed, 7 deletions(-) (limited to 'lib') 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 -- cgit v1.2.1 From 847ada36c48107442f69338eda4c0b601ab98b48 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 23 Nov 2016 19:18:34 +0200 Subject: Fix: Timeout creating and viewing merge request for binary file --- lib/gitlab/diff/file_collection/merge_request_diff.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/diff/file_collection/merge_request_diff.rb b/lib/gitlab/diff/file_collection/merge_request_diff.rb index fe7adb7bed6..26bb0bc16f5 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.text? end def cache_key -- cgit v1.2.1 From 6a08de7386fd41c9bbe27c32338328c6e6b40027 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Fri, 25 Nov 2016 15:41:28 +0100 Subject: Add issue search slash command One of many requested in: gitlab-org/gitlab-ce#24768 --- lib/gitlab/chat_commands/base_command.rb | 4 +--- lib/gitlab/chat_commands/command.rb | 1 + lib/gitlab/chat_commands/issue_command.rb | 6 +----- lib/gitlab/chat_commands/issue_search.rb | 17 +++++++++++++++++ 4 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 lib/gitlab/chat_commands/issue_search.rb (limited to 'lib') 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_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+(?.*)/.match(text) + end + + def self.help_message + "issue search " + end + + def execute(match) + collection.search(match[:query]).limit(QUERY_LIMIT) + end + end + end +end -- cgit v1.2.1 From 40e8185b64a04f719c85a793d0fdd5438a129975 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Tue, 22 Nov 2016 17:28:58 +0100 Subject: Expose coverage on GET pipelines/:id The coverage wasn't exposed yet, now it is but only for detailed requests to save queries on the database. --- lib/api/entities.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 33cb6fd3704..7dfaf2e8eb7 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -606,6 +606,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 -- cgit v1.2.1 From 7c607a55ab339293b0e67eeb33439d5407e22aad Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Wed, 9 Nov 2016 15:51:27 +0100 Subject: Grapify the projects API --- lib/api/helpers.rb | 26 +-- lib/api/projects.rb | 578 ++++++++++++++++++++++++---------------------------- 2 files changed, 267 insertions(+), 337 deletions(-) (limited to 'lib') diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 0d3ddb89dc3..2fd50143e91 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -324,11 +324,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 @@ -337,25 +332,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/projects.rb b/lib/api/projects.rb index 2ea3c433ae2..8975b1a751c 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -1,293 +1,287 @@ module API # Projects API class Projects < Grape::API + include PaginationParams + before { authenticate! } - resource :projects, requirements: { id: /[^\/]+/ } do + 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 + + 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 + + resource :projects 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 + 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 + + 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 projects list for authenticated user - # - # Example Request: - # GET /projects + 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 projects = current_user.authorized_projects projects = filter_projects(projects) - projects = paginate 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 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] ? 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 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 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 + present user_project, with: Entities::ProjectWithAccess, 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 +291,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 +302,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 +313,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 +327,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 +341,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 + 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 +395,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 @@ -451,40 +417,26 @@ module API no_content! end - # Upload a file - # - # Parameters: - # id: (required) - The ID of the project - # file: (required) - The file to be uploaded + 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 - # 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) - - present paginate(projects), with: Entities::Project + 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 a users list - # - # Example Request: - # GET /users 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.where(id: user_project.team.users.map(&:id)) + users = users.search(params[:search]) if params[:search].present? + + present paginate(users), with: Entities::UserBasic end end end -- cgit v1.2.1 From b62e2bedbfa49aacfc4847049aa589f045af15ce Mon Sep 17 00:00:00 2001 From: Ruben Davila Date: Mon, 28 Nov 2016 17:00:03 -0500 Subject: Add new configuration setting to enable/disable HTML emails. This new global setting will allow admins to specify if HTML emails should be sent or not, this is basically useful when system administrators want to save some disk space by avoiding emails in HTML format and using only the Plain Text version. --- lib/email_template_interceptor.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 lib/email_template_interceptor.rb (limited to 'lib') 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 -- cgit v1.2.1 From 3d7704ae5f62446b8b399c796c64d1f527666376 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 10 Nov 2016 10:23:44 +0000 Subject: Merge branch 'zj-fix-label-creation-non-members' into 'security' Fix label creation non members Fixes https://gitlab.com/gitlab-org/gitlab-ce/issues/23416 See merge request !2006 --- lib/api/helpers.rb | 14 --------- lib/api/issues.rb | 74 +++++++++++++++++++++++------------------------ lib/api/merge_requests.rb | 10 ------- 3 files changed, 37 insertions(+), 61 deletions(-) (limited to 'lib') diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 0d3ddb89dc3..79a83496eee 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -198,20 +198,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. # diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 2fea71870b8..029be7519f5 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 @@ -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 # @@ -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/merge_requests.rb b/lib/api/merge_requests.rb index e82651a1578..90fa588b455 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -77,11 +77,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? @@ -157,11 +152,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? -- cgit v1.2.1 From 3bf34face4cacf07ca973705c261369b1f596626 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 22 Nov 2016 10:25:04 +0000 Subject: Merge branch 'jej-use-issuable-finder-instead-of-access-check' into 'security' Replace issue access checks with use of IssuableFinder Split from !2024 to partially solve https://gitlab.com/gitlab-org/gitlab-ce/issues/23867 ## Which fixes are in this MR? :warning: - Potentially untested :bomb: - No test coverage :traffic_light: - Test coverage of some sort exists (a test failed when error raised) :vertical_traffic_light: - Test coverage of return value (a test failed when nil used) :white_check_mark: - Permissions check tested ### Issue lookup with access check Using `visible_to_user` likely makes these security issues too. See [Code smells](#code-smells). - [x] :vertical_traffic_light: app/finders/notes_finder.rb:15 [`visible_to_user`] - [x] :traffic_light: app/views/layouts/nav/_project.html.haml:73 [`visible_to_user`] [`.count`] - [x] :white_check_mark: app/services/merge_requests/build_service.rb:84 [`issue.try(:confidential?)`] - [x] :white_check_mark: lib/api/issues.rb:112 [`visible_to_user`] - CHANGELOG: Prevented API returning issues set to 'Only team members' to everyone - [x] :white_check_mark: lib/api/helpers.rb:126 [`can?(current_user, :read_issue, issue)`] Maybe here too? - [x] :white_check_mark: lib/gitlab/search_results.rb:53 [`visible_to_user`] ### Previous discussions - [ ] https://dev.gitlab.org/gitlab/gitlabhq/merge_requests/2024/diffs#b2ff264eddf9819d7693c14ae213d941494fe2b3_128_126 - [ ] https://dev.gitlab.org/gitlab/gitlabhq/merge_requests/2024/diffs#7b6375270d22f880bdcb085e47b519b426a5c6c7_87_87 See merge request !2031 --- lib/api/helpers.rb | 4 +--- lib/api/issues.rb | 2 +- lib/gitlab/search_results.rb | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 79a83496eee..34d9c3c6932 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -128,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) diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 029be7519f5..049b4fb214c 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -122,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? 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) -- cgit v1.2.1 From a49e9949c6bc474c8bfd4016d9c6c3b59776772f Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 28 Nov 2016 11:13:32 +0100 Subject: Rename `MergeRequest#pipeline` to `head_pipeline` --- lib/api/merge_requests.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 90fa588b455..97baebc1d27 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -192,7 +192,7 @@ module API should_remove_source_branch: params[:should_remove_source_branch] } - if params[:merge_when_build_succeeds] && merge_request.pipeline && merge_request.pipeline.active? + if params[:merge_when_build_succeeds] && merge_request.head_pipeline && merge_request.head_pipeline.active? ::MergeRequests::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user, merge_params). execute(merge_request) else -- cgit v1.2.1 From fbbf177e3b604bebce3b10f8eea8920ff5606fca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 28 Sep 2016 12:45:46 +0200 Subject: New `gitlab:workhorse:install` rake task MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/tasks/gitlab/shell.rake | 42 ++++++-------------------------------- lib/tasks/gitlab/task_helpers.rake | 35 ++++++++++++++++++++++++++++++- lib/tasks/gitlab/workhorse.rake | 23 +++++++++++++++++++++ 3 files changed, 63 insertions(+), 37 deletions(-) create mode 100644 lib/tasks/gitlab/workhorse.rake (limited to 'lib') 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 index 74be413423a..85c3a3a9b0f 100644 --- a/lib/tasks/gitlab/task_helpers.rake +++ b/lib/tasks/gitlab/task_helpers.rake @@ -70,7 +70,7 @@ namespace :gitlab do # Runs the given command # - # Returns nil if the command was not found + # Returns '' if the command was not found # Returns the output of the command otherwise # # see also #run_and_match @@ -137,4 +137,37 @@ namespace :gitlab do 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) + Dir.chdir(target_dir) do + run_command(%W[#{Gitlab.config.git.bin_path} fetch --tags --quiet]) + run_command(%W[#{Gitlab.config.git.bin_path} checkout --quiet #{tag}]) + end + else + run_command(%W[#{Gitlab.config.git.bin_path} clone -- #{repo} #{target_dir}]) + end + + # 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. + reset_to_tag(tag) + end + end + + def reset_to_tag(tag_wanted) + tag, status = Gitlab::Popen.popen(%W[#{Gitlab.config.git.bin_path} describe -- #{tag_wanted}]) + + unless status.zero? + run_command(%W(#{Gitlab.config.git.bin_path} fetch origin)) + tag = run_command(%W[#{Gitlab.config.git.bin_path} describe -- origin/#{tag_wanted}]) + end + + run_command(%W[#{Gitlab.config.git.bin_path} reset --hard #{tag.strip}]) + end end diff --git a/lib/tasks/gitlab/workhorse.rake b/lib/tasks/gitlab/workhorse.rake new file mode 100644 index 00000000000..5964cb83b89 --- /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#{ENV['GITLAB_WORKHORSE_VERSION'] || Gitlab::Workhorse.version}" + repo = ENV['GITLAB_WORKHORSE_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 -- cgit v1.2.1 From a9c250eaddf758f99ac8c868dc86f4df0cc157f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 24 Nov 2016 14:19:09 +0100 Subject: Add #run_command! to task helpers to raise a TaskFailedError if status is not 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/tasks/gitlab/task_helpers.rake | 36 +++++++++++++++++++++++++++--------- lib/tasks/gitlab/workhorse.rake | 2 +- 2 files changed, 28 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/tasks/gitlab/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake index 85c3a3a9b0f..c0759b96602 100644 --- a/lib/tasks/gitlab/task_helpers.rake +++ b/lib/tasks/gitlab/task_helpers.rake @@ -1,4 +1,5 @@ module Gitlab + class TaskFailedError < StandardError; end class TaskAbortedByUserError < StandardError; end end @@ -81,6 +82,18 @@ namespace :gitlab do '' # if the command does not exist, return an empty string end + # Runs the given command and raise 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 @@ -145,11 +158,11 @@ namespace :gitlab do def checkout_or_clone_tag(tag:, repo:, target_dir:) if Dir.exist?(target_dir) Dir.chdir(target_dir) do - run_command(%W[#{Gitlab.config.git.bin_path} fetch --tags --quiet]) - run_command(%W[#{Gitlab.config.git.bin_path} checkout --quiet #{tag}]) + run_command!(%W[#{Gitlab.config.git.bin_path} fetch --tags --quiet]) + run_command!(%W[#{Gitlab.config.git.bin_path} checkout --quiet #{tag}]) end else - run_command(%W[#{Gitlab.config.git.bin_path} clone -- #{repo} #{target_dir}]) + run_command!(%W[#{Gitlab.config.git.bin_path} clone -- #{repo} #{target_dir}]) end # Make sure we're on the right tag @@ -161,13 +174,18 @@ namespace :gitlab do end def reset_to_tag(tag_wanted) - tag, status = Gitlab::Popen.popen(%W[#{Gitlab.config.git.bin_path} describe -- #{tag_wanted}]) + tag = + begin + run_command!(%W[#{Gitlab.config.git.bin_path} describe -- #{tag_wanted}]) + rescue Gitlab::TaskFailedError + run_command!(%W[#{Gitlab.config.git.bin_path} fetch origin]) + run_command!(%W[#{Gitlab.config.git.bin_path} describe -- origin/#{tag_wanted}]) + end - unless status.zero? - run_command(%W(#{Gitlab.config.git.bin_path} fetch origin)) - tag = run_command(%W[#{Gitlab.config.git.bin_path} describe -- origin/#{tag_wanted}]) + if tag + run_command!(%W[#{Gitlab.config.git.bin_path} reset --hard #{tag.strip}]) + else + raise Gitlab::TaskFailedError end - - run_command(%W[#{Gitlab.config.git.bin_path} reset --hard #{tag.strip}]) end end diff --git a/lib/tasks/gitlab/workhorse.rake b/lib/tasks/gitlab/workhorse.rake index 5964cb83b89..563744dd0c7 100644 --- a/lib/tasks/gitlab/workhorse.rake +++ b/lib/tasks/gitlab/workhorse.rake @@ -16,7 +16,7 @@ namespace :gitlab do command = status.zero? ? 'gmake' : 'make' Dir.chdir(args.dir) do - run_command([command]) + run_command!([command]) end end end -- cgit v1.2.1 From 0fbb5a86dbe054af91c20d36697fda273445dd2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Ger=C5=91?= Date: Sat, 29 Oct 2016 12:51:01 +0200 Subject: Add Human Readable Timestamp to backup tar file --- lib/backup/manager.rb | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'lib') 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!" -- cgit v1.2.1 From b193e8497444a19e4ea541f73f82eb7e21aa8879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 29 Nov 2016 19:21:25 +0100 Subject: Move task helpers to a module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/tasks/gitlab/helpers.rake | 8 ++ lib/tasks/gitlab/task_helpers.rake | 191 ------------------------------------- lib/tasks/gitlab/task_helpers.rb | 190 ++++++++++++++++++++++++++++++++++++ lib/tasks/gitlab/workhorse.rake | 2 +- 4 files changed, 199 insertions(+), 192 deletions(-) create mode 100644 lib/tasks/gitlab/helpers.rake delete mode 100644 lib/tasks/gitlab/task_helpers.rake create mode 100644 lib/tasks/gitlab/task_helpers.rb (limited to 'lib') 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/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake deleted file mode 100644 index c0759b96602..00000000000 --- a/lib/tasks/gitlab/task_helpers.rake +++ /dev/null @@ -1,191 +0,0 @@ -module Gitlab - class TaskFailedError < StandardError; end - 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 '' 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 raise 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) - Dir.chdir(target_dir) do - run_command!(%W[#{Gitlab.config.git.bin_path} fetch --tags --quiet]) - run_command!(%W[#{Gitlab.config.git.bin_path} checkout --quiet #{tag}]) - end - else - run_command!(%W[#{Gitlab.config.git.bin_path} clone -- #{repo} #{target_dir}]) - end - - # 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. - reset_to_tag(tag) - end - end - - def reset_to_tag(tag_wanted) - tag = - begin - run_command!(%W[#{Gitlab.config.git.bin_path} describe -- #{tag_wanted}]) - rescue Gitlab::TaskFailedError - run_command!(%W[#{Gitlab.config.git.bin_path} fetch origin]) - run_command!(%W[#{Gitlab.config.git.bin_path} describe -- origin/#{tag_wanted}]) - end - - if tag - run_command!(%W[#{Gitlab.config.git.bin_path} reset --hard #{tag.strip}]) - else - raise Gitlab::TaskFailedError - end - 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 index 563744dd0c7..46bd0bf2e7b 100644 --- a/lib/tasks/gitlab/workhorse.rake +++ b/lib/tasks/gitlab/workhorse.rake @@ -4,7 +4,7 @@ namespace :gitlab do 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]" + abort %(Please specify the directory where you want to install gitlab-workhorse:\n rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]") end tag = "v#{ENV['GITLAB_WORKHORSE_VERSION'] || Gitlab::Workhorse.version}" -- cgit v1.2.1 From dd5f71138ce98522b1324319fbd60f665b3d1337 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Mon, 28 Nov 2016 22:15:12 +0100 Subject: Grapify the files API --- lib/api/files.rb | 153 ++++++++++++++++++++++--------------------------------- 1 file changed, 60 insertions(+), 93 deletions(-) (limited to 'lib') 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 -- cgit v1.2.1 From 2ce66c071fc7ab2b8ca881223321a3927ec7d61e Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Mon, 28 Nov 2016 19:16:15 +0100 Subject: API: Expose branch status --- lib/api/entities.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index fdb19558c1c..d5dfb8d00be 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| -- cgit v1.2.1 From 374033fe26013c685157ac0a3cd2a2b40f992ef5 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Wed, 30 Nov 2016 11:23:04 -0600 Subject: Improve the `Gitlab::OAuth::User` error message The error saving the user is logged to application.log. Previously, the entry had no context and was unusable - 'Error saving user: [Email address already taken]'. Adding the auth hash UID and email makes the error more helpful. --- lib/gitlab/o_auth/user.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') 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 -- cgit v1.2.1 From d6b9b21e6db3c32e0f272ab96486876fa8b54d1b Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 29 Nov 2016 16:59:25 -0200 Subject: Allow access to the wiki with git when repository feature disabled --- lib/gitlab/git_access.rb | 6 +++++- lib/gitlab/git_access_wiki.rb | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index bcbf6455998..db07b7c5fcc 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -46,7 +46,7 @@ module Gitlab def download_access_check if user user_download_access_check - elsif deploy_key.nil? && !Guest.can?(:download_code, project) + elsif deploy_key.nil? && !guest_can_downlod_code? raise UnauthorizedError, ERROR_MESSAGES[:download] end end @@ -59,6 +59,10 @@ module Gitlab end end + def guest_can_downlod_code? + Guest.can?(:download_code, project) + end + def user_download_access_check unless user_can_download_code? || build_can_download_code? raise UnauthorizedError, ERROR_MESSAGES[:download] diff --git a/lib/gitlab/git_access_wiki.rb b/lib/gitlab/git_access_wiki.rb index f71d3575909..2c06c4ff1ef 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 change_access_check(change) if user_access.can_do_action?(:create_wiki) build_status_object(true) -- cgit v1.2.1 From ff2026f40ec0bc162dc4281b067ed4716b2ad248 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Mon, 21 Nov 2016 12:02:19 -0600 Subject: add transparent namespace to all user-generated anchors in GitLab flavored markdown --- lib/banzai/filter/table_of_contents_filter.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/banzai/filter/table_of_contents_filter.rb b/lib/banzai/filter/table_of_contents_filter.rb index a4eda6fdf76..80669953723 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{} + def anchor_tag(id, href) + %Q{} end def push_toc(href, text) -- cgit v1.2.1 From 131a04d7962b01daa58b8e5efe3f1359a3e73fe1 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 29 Nov 2016 13:07:38 -0600 Subject: remove underscore from user-content id namespace --- lib/banzai/filter/table_of_contents_filter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/banzai/filter/table_of_contents_filter.rb b/lib/banzai/filter/table_of_contents_filter.rb index 80669953723..8e7084f2543 100644 --- a/lib/banzai/filter/table_of_contents_filter.rb +++ b/lib/banzai/filter/table_of_contents_filter.rb @@ -36,7 +36,7 @@ module Banzai if header_content = node.children.first # namespace detection will be automatically handled via javascript (see issue #22781) - namespace = "user-content_" + namespace = "user-content-" href = "#{id}#{uniq}" push_toc(href, text) header_content.add_previous_sibling(anchor_tag("#{namespace}#{href}", href)) -- cgit v1.2.1 From 8ca040d8ae66be461f61f52673a87022e6c84204 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 1 Dec 2016 00:03:12 -0200 Subject: Fix branch validation for GitHub PR where repo/fork was renamed/deleted --- lib/gitlab/github_import/branch_formatter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') 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 -- cgit v1.2.1 From 79d99d470faf5cf088a0b76ae6bb2ec280c4c4a8 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Wed, 30 Nov 2016 18:02:58 +0100 Subject: API: Expose committer details for a commit --- lib/api/entities.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index d5dfb8d00be..899d68bc6c7 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -174,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 -- cgit v1.2.1 From d757247247ea6015d560eacd29ec7be564e332bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 30 Nov 2016 15:48:19 +0100 Subject: Allow public access to some Project API endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/api/helpers.rb | 5 +++++ lib/api/projects.rb | 28 ++++++++++++++++++---------- 2 files changed, 23 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index cbafa952ef6..7f94ede7940 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -141,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) @@ -149,6 +153,7 @@ module API end def authenticated_as_admin! + authenticate! forbidden! unless current_user.is_admin? end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 8975b1a751c..2929d2157dc 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -3,7 +3,7 @@ module API class Projects < Grape::API include PaginationParams - before { authenticate! } + before { authenticate_non_get! } helpers do params :optional_params do @@ -61,7 +61,7 @@ module API end end - desc 'Get a projects list for authenticated user' do + desc 'Get a list of visible projects for authenticated user' do success Entities::BasicProjectDetails end params do @@ -70,15 +70,15 @@ module API use :filter_params use :pagination end - get do - projects = current_user.authorized_projects + get '/visible' do + projects = ProjectsFinder.new.execute(current_user) projects = filter_projects(projects) - entity = params[:simple] ? Entities::BasicProjectDetails : Entities::ProjectWithAccess + entity = params[:simple] || !current_user ? Entities::BasicProjectDetails : Entities::ProjectWithAccess present paginate(projects), with: entity, user: current_user end - desc 'Get a list of visible projects for authenticated user' do + desc 'Get a projects list for authenticated user' do success Entities::BasicProjectDetails end params do @@ -87,8 +87,10 @@ module API use :filter_params use :pagination end - get '/visible' do - projects = ProjectsFinder.new.execute(current_user) + get do + authenticate! + + projects = current_user.authorized_projects projects = filter_projects(projects) entity = params[:simple] ? Entities::BasicProjectDetails : Entities::ProjectWithAccess @@ -103,6 +105,8 @@ module API use :pagination end get '/owned' do + authenticate! + projects = current_user.owned_projects projects = filter_projects(projects) @@ -117,6 +121,8 @@ module API use :pagination end get '/starred' do + authenticate! + projects = current_user.viewable_starred_projects projects = filter_projects(projects) @@ -132,6 +138,7 @@ module API end get '/all' do authenticated_as_admin! + projects = Project.all projects = filter_projects(projects) @@ -213,7 +220,8 @@ module API success Entities::ProjectWithAccess end get ":id" do - present user_project, with: Entities::ProjectWithAccess, user: current_user, + 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 @@ -433,7 +441,7 @@ module API use :pagination end get ':id/users' do - users = User.where(id: user_project.team.users.map(&:id)) + users = user_project.team.users users = users.search(params[:search]) if params[:search].present? present paginate(users), with: Entities::UserBasic -- cgit v1.2.1 From fde754e2676e40dcf2600190983ef54030c5d5a5 Mon Sep 17 00:00:00 2001 From: Guyzmo Date: Sat, 26 Nov 2016 16:37:26 +0100 Subject: API: Endpoint to expose personal snippets as /snippets Adding the necessary API for the new /snippets Restful resource added with this commit. Added a new Grape class `Snippets`, as well as a `PersonalSnippet` entity. Issue: #20042 Merge-Request: !6373 Signed-off-by: Guyzmo --- lib/api/api.rb | 1 + lib/api/entities.rb | 13 +++++ lib/api/snippets.rb | 137 ++++++++++++++++++++++++++++++++++++++++++++++ lib/gitlab/url_builder.rb | 2 + 4 files changed, 153 insertions(+) create mode 100644 lib/api/snippets.rb (limited to 'lib') diff --git a/lib/api/api.rb b/lib/api/api.rb index 67109ceeef9..cec2702e44d 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -64,6 +64,7 @@ module API mount ::API::Session mount ::API::Settings mount ::API::SidekiqMetrics + mount ::API::Snippets mount ::API::Subscriptions mount ::API::SystemHooks mount ::API::Tags diff --git a/lib/api/entities.rb b/lib/api/entities.rb index d5dfb8d00be..dc6d085925d 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -200,6 +200,19 @@ module API end end + class PersonalSnippet < Grape::Entity + expose :id, :title, :file_name + expose :author, using: Entities::UserBasic + expose :updated_at, :created_at + + expose :web_url do |snippet| + Gitlab::UrlBuilder.build(snippet) + end + expose :raw_url do |snippet| + Gitlab::UrlBuilder.build(snippet) + "/raw" + end + end + class ProjectEntity < Grape::Entity expose :id, :iid expose(:project_id) { |entity| entity.project.id } diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb new file mode 100644 index 00000000000..e096e636806 --- /dev/null +++ b/lib/api/snippets.rb @@ -0,0 +1,137 @@ +module API + # Snippets API + class Snippets < Grape::API + include PaginationParams + + before { authenticate! } + + resource :snippets do + helpers do + def snippets_for_current_user + SnippetsFinder.new.execute(current_user, filter: :by_user, user: current_user) + end + + def public_snippets + SnippetsFinder.new.execute(current_user, filter: :public) + end + end + + desc 'Get a snippets list for authenticated user' do + detail 'This feature was introduced in GitLab 8.15.' + success Entities::PersonalSnippet + end + params do + use :pagination + end + get do + present paginate(snippets_for_current_user), with: Entities::PersonalSnippet + end + + desc 'List all public snippets current_user has access to' do + detail 'This feature was introduced in GitLab 8.15.' + success Entities::PersonalSnippet + end + params do + use :pagination + end + get 'public' do + present paginate(public_snippets), with: Entities::PersonalSnippet + end + + desc 'Get a single snippet' do + detail 'This feature was introduced in GitLab 8.15.' + success Entities::PersonalSnippet + end + params do + requires :id, type: Integer, desc: 'The ID of a snippet' + end + get ':id' do + snippet = snippets_for_current_user.find(params[:id]) + present snippet, with: Entities::PersonalSnippet + end + + desc 'Create new snippet' do + detail 'This feature was introduced in GitLab 8.15.' + success Entities::PersonalSnippet + end + params do + requires :title, type: String, desc: 'The title of a snippet' + requires :file_name, type: String, desc: 'The name of a snippet file' + requires :content, type: String, desc: 'The content of a snippet' + optional :visibility_level, type: Integer, + values: Gitlab::VisibilityLevel.values, + default: Gitlab::VisibilityLevel::INTERNAL, + desc: 'The visibility level of the snippet' + end + post do + attrs = declared_params(include_missing: false) + snippet = CreateSnippetService.new(nil, current_user, attrs).execute + + if snippet.persisted? + present snippet, with: Entities::PersonalSnippet + else + render_validation_error!(snippet) + end + end + + desc 'Update an existing snippet' do + detail 'This feature was introduced in GitLab 8.15.' + success Entities::PersonalSnippet + end + params do + requires :id, type: Integer, desc: 'The ID of a snippet' + optional :title, type: String, desc: 'The title of a snippet' + optional :file_name, type: String, desc: 'The name of a snippet file' + optional :content, type: String, desc: 'The content of a snippet' + optional :visibility_level, type: Integer, + values: Gitlab::VisibilityLevel.values, + desc: 'The visibility level of the snippet' + at_least_one_of :title, :file_name, :content, :visibility_level + end + put ':id' do + snippet = snippets_for_current_user.find_by(id: params.delete(:id)) + return not_found!('Snippet') unless snippet + authorize! :update_personal_snippet, snippet + + attrs = declared_params(include_missing: false) + + UpdateSnippetService.new(nil, current_user, snippet, attrs).execute + if snippet.persisted? + present snippet, with: Entities::PersonalSnippet + else + render_validation_error!(snippet) + end + end + + desc 'Remove snippet' do + detail 'This feature was introduced in GitLab 8.15.' + success Entities::PersonalSnippet + end + params do + requires :id, type: Integer, desc: 'The ID of a snippet' + end + delete ':id' do + snippet = snippets_for_current_user.find_by(id: params.delete(:id)) + return not_found!('Snippet') unless snippet + authorize! :destroy_personal_snippet, snippet + snippet.destroy + no_content! + end + + desc 'Get a raw snippet' do + detail 'This feature was introduced in GitLab 8.15.' + end + params do + requires :id, type: Integer, desc: 'The ID of a snippet' + end + get ":id/raw" do + snippet = snippets_for_current_user.find_by(id: params.delete(:id)) + return not_found!('Snippet') unless snippet + + env['api.format'] = :txt + content_type 'text/plain' + present snippet.content + end + end + end +end diff --git a/lib/gitlab/url_builder.rb b/lib/gitlab/url_builder.rb index 99d0c28e749..ccb456bcc94 100644 --- a/lib/gitlab/url_builder.rb +++ b/lib/gitlab/url_builder.rb @@ -24,6 +24,8 @@ module Gitlab wiki_page_url when ProjectSnippet project_snippet_url(object) + when Snippet + personal_snippet_url(object) else raise NotImplementedError.new("No URL builder defined for #{object.class}") end -- cgit v1.2.1 From 2c0bcefdc6af442f67f74c6124fc1a9cbacd2831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 1 Dec 2016 16:24:35 +0100 Subject: Don't allow to specify a repo or version when installing Workhorse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The task will use the canonical repo and the required version. Signed-off-by: Rémy Coutable --- lib/tasks/gitlab/workhorse.rake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/tasks/gitlab/workhorse.rake b/lib/tasks/gitlab/workhorse.rake index 46bd0bf2e7b..baea94bf8ca 100644 --- a/lib/tasks/gitlab/workhorse.rake +++ b/lib/tasks/gitlab/workhorse.rake @@ -7,8 +7,8 @@ namespace :gitlab do abort %(Please specify the directory where you want to install gitlab-workhorse:\n rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]") end - tag = "v#{ENV['GITLAB_WORKHORSE_VERSION'] || Gitlab::Workhorse.version}" - repo = ENV['GITLAB_WORKHORSE_REPO'] || 'https://gitlab.com/gitlab-org/gitlab-workhorse.git' + 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) -- cgit v1.2.1 From be797097df258f306dca1cf821b7ed9b46e7919f Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 2 Dec 2016 11:17:48 +0100 Subject: Add scaffold for each class of core CI status --- lib/gitlab/ci/status/core/canceled.rb | 8 ++++++++ lib/gitlab/ci/status/core/created.rb | 8 ++++++++ lib/gitlab/ci/status/core/failed.rb | 8 ++++++++ lib/gitlab/ci/status/core/pending.rb | 8 ++++++++ lib/gitlab/ci/status/core/running.rb | 8 ++++++++ lib/gitlab/ci/status/core/skipped.rb | 8 ++++++++ lib/gitlab/ci/status/core/success.rb | 8 ++++++++ 7 files changed, 56 insertions(+) create mode 100644 lib/gitlab/ci/status/core/canceled.rb create mode 100644 lib/gitlab/ci/status/core/created.rb create mode 100644 lib/gitlab/ci/status/core/failed.rb create mode 100644 lib/gitlab/ci/status/core/pending.rb create mode 100644 lib/gitlab/ci/status/core/running.rb create mode 100644 lib/gitlab/ci/status/core/skipped.rb create mode 100644 lib/gitlab/ci/status/core/success.rb (limited to 'lib') diff --git a/lib/gitlab/ci/status/core/canceled.rb b/lib/gitlab/ci/status/core/canceled.rb new file mode 100644 index 00000000000..3dcddd6e3ef --- /dev/null +++ b/lib/gitlab/ci/status/core/canceled.rb @@ -0,0 +1,8 @@ +module Gitlab::Ci + module Status + module Core + class Canceled + end + end + end +end diff --git a/lib/gitlab/ci/status/core/created.rb b/lib/gitlab/ci/status/core/created.rb new file mode 100644 index 00000000000..590f14d6b57 --- /dev/null +++ b/lib/gitlab/ci/status/core/created.rb @@ -0,0 +1,8 @@ +module Gitlab::Ci + module Status + module Core + class Created + end + end + end +end diff --git a/lib/gitlab/ci/status/core/failed.rb b/lib/gitlab/ci/status/core/failed.rb new file mode 100644 index 00000000000..d5af40b53cb --- /dev/null +++ b/lib/gitlab/ci/status/core/failed.rb @@ -0,0 +1,8 @@ +module Gitlab::Ci + module Status + module Core + class Failed + end + end + end +end diff --git a/lib/gitlab/ci/status/core/pending.rb b/lib/gitlab/ci/status/core/pending.rb new file mode 100644 index 00000000000..ef57886234e --- /dev/null +++ b/lib/gitlab/ci/status/core/pending.rb @@ -0,0 +1,8 @@ +module Gitlab::Ci + module Status + module Core + class Pending + end + end + end +end diff --git a/lib/gitlab/ci/status/core/running.rb b/lib/gitlab/ci/status/core/running.rb new file mode 100644 index 00000000000..0b027f4dc9c --- /dev/null +++ b/lib/gitlab/ci/status/core/running.rb @@ -0,0 +1,8 @@ +module Gitlab::Ci + module Status + module Core + class Running + end + end + end +end diff --git a/lib/gitlab/ci/status/core/skipped.rb b/lib/gitlab/ci/status/core/skipped.rb new file mode 100644 index 00000000000..b8b07a69156 --- /dev/null +++ b/lib/gitlab/ci/status/core/skipped.rb @@ -0,0 +1,8 @@ +module Gitlab::Ci + module Status + module Core + class Skipped + end + end + end +end diff --git a/lib/gitlab/ci/status/core/success.rb b/lib/gitlab/ci/status/core/success.rb new file mode 100644 index 00000000000..511a34901f8 --- /dev/null +++ b/lib/gitlab/ci/status/core/success.rb @@ -0,0 +1,8 @@ +module Gitlab::Ci + module Status + module Core + class Success + end + end + end +end -- cgit v1.2.1 From 5df3e8b81b4115f44b1ee56a3531dca6d912756c Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 2 Dec 2016 12:14:10 +0100 Subject: Add initial implmentation for core success status --- lib/gitlab/ci/status/core/success.rb | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'lib') diff --git a/lib/gitlab/ci/status/core/success.rb b/lib/gitlab/ci/status/core/success.rb index 511a34901f8..7bae4f612cc 100644 --- a/lib/gitlab/ci/status/core/success.rb +++ b/lib/gitlab/ci/status/core/success.rb @@ -2,6 +2,13 @@ module Gitlab::Ci module Status module Core class Success + def label + 'passed' + end + + def icon + 'success' + end end end end -- cgit v1.2.1 From d4ed5b2e0c7fc94309499a0a268c543a82e00e9b Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 2 Dec 2016 12:38:07 +0100 Subject: Add abstract base class for core CI status --- lib/gitlab/ci/status/core/base.rb | 41 ++++++++++++++++++++++++++++++++++++ lib/gitlab/ci/status/core/success.rb | 2 +- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 lib/gitlab/ci/status/core/base.rb (limited to 'lib') diff --git a/lib/gitlab/ci/status/core/base.rb b/lib/gitlab/ci/status/core/base.rb new file mode 100644 index 00000000000..96d09dcbfc5 --- /dev/null +++ b/lib/gitlab/ci/status/core/base.rb @@ -0,0 +1,41 @@ +module Gitlab::Ci + module Status + module Core + # Base abstract class fore core status + # + class Base + def initialize(subject) + @subject = subject + end + + def icon + raise NotImplementedError + end + + def label + raise NotImplementedError + end + + def has_details? + raise NotImplementedError + end + + def details_path + raise NotImplementedError + end + + def has_action? + raise NotImplementedError + end + + def action_icon + raise NotImplementedError + end + + def action_path + raise NotImplementedError + end + end + end + end +end diff --git a/lib/gitlab/ci/status/core/success.rb b/lib/gitlab/ci/status/core/success.rb index 7bae4f612cc..e32a5228619 100644 --- a/lib/gitlab/ci/status/core/success.rb +++ b/lib/gitlab/ci/status/core/success.rb @@ -1,7 +1,7 @@ module Gitlab::Ci module Status module Core - class Success + class Success < Core::Base def label 'passed' end -- cgit v1.2.1 From f5d7a61760d8f60c27b8838db826468768154733 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 2 Dec 2016 14:03:30 +0200 Subject: Fixes ActionView::Template::Error: undefined method `text?` for nil:NilClass --- lib/gitlab/diff/file_collection/merge_request_diff.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/gitlab/diff/file_collection/merge_request_diff.rb b/lib/gitlab/diff/file_collection/merge_request_diff.rb index 26bb0bc16f5..56530448f36 100644 --- a/lib/gitlab/diff/file_collection/merge_request_diff.rb +++ b/lib/gitlab/diff/file_collection/merge_request_diff.rb @@ -61,7 +61,7 @@ module Gitlab end def cacheable?(diff_file) - @merge_request_diff.present? && diff_file.blob.text? + @merge_request_diff.present? && diff_file.blob && diff_file.blob.text? end def cache_key -- cgit v1.2.1 From 943b3d0e0007d2f48a64ffdef6bf0ff0fcb774f2 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 2 Dec 2016 13:08:21 +0100 Subject: Implement the rest of core CI statuses with specs --- lib/gitlab/ci/status/core/base.rb | 4 ++++ lib/gitlab/ci/status/core/canceled.rb | 9 ++++++++- lib/gitlab/ci/status/core/created.rb | 9 ++++++++- lib/gitlab/ci/status/core/failed.rb | 9 ++++++++- lib/gitlab/ci/status/core/pending.rb | 9 ++++++++- lib/gitlab/ci/status/core/running.rb | 9 ++++++++- lib/gitlab/ci/status/core/skipped.rb | 9 ++++++++- lib/gitlab/ci/status/core/success.rb | 2 +- 8 files changed, 53 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/ci/status/core/base.rb b/lib/gitlab/ci/status/core/base.rb index 96d09dcbfc5..f7687c49ffd 100644 --- a/lib/gitlab/ci/status/core/base.rb +++ b/lib/gitlab/ci/status/core/base.rb @@ -16,6 +16,10 @@ module Gitlab::Ci raise NotImplementedError end + def title + "#{@subject.class.name.demodulize}: #{label}" + end + def has_details? raise NotImplementedError end diff --git a/lib/gitlab/ci/status/core/canceled.rb b/lib/gitlab/ci/status/core/canceled.rb index 3dcddd6e3ef..5e06b946a99 100644 --- a/lib/gitlab/ci/status/core/canceled.rb +++ b/lib/gitlab/ci/status/core/canceled.rb @@ -1,7 +1,14 @@ module Gitlab::Ci module Status module Core - class Canceled + class Canceled < Core::Base + def label + 'canceled' + end + + def icon + 'icon_status_canceled' + end end end end diff --git a/lib/gitlab/ci/status/core/created.rb b/lib/gitlab/ci/status/core/created.rb index 590f14d6b57..c116f9a97f1 100644 --- a/lib/gitlab/ci/status/core/created.rb +++ b/lib/gitlab/ci/status/core/created.rb @@ -1,7 +1,14 @@ module Gitlab::Ci module Status module Core - class Created + class Created < Core::Base + def label + 'created' + end + + def icon + 'icon_status_created' + end end end end diff --git a/lib/gitlab/ci/status/core/failed.rb b/lib/gitlab/ci/status/core/failed.rb index d5af40b53cb..467ef71e819 100644 --- a/lib/gitlab/ci/status/core/failed.rb +++ b/lib/gitlab/ci/status/core/failed.rb @@ -1,7 +1,14 @@ module Gitlab::Ci module Status module Core - class Failed + class Failed < Core::Base + def label + 'failed' + end + + def icon + 'icon_status_failed' + end end end end diff --git a/lib/gitlab/ci/status/core/pending.rb b/lib/gitlab/ci/status/core/pending.rb index ef57886234e..05c9e41091b 100644 --- a/lib/gitlab/ci/status/core/pending.rb +++ b/lib/gitlab/ci/status/core/pending.rb @@ -1,7 +1,14 @@ module Gitlab::Ci module Status module Core - class Pending + class Pending < Core::Base + def label + 'pending' + end + + def icon + 'icon_status_pending' + end end end end diff --git a/lib/gitlab/ci/status/core/running.rb b/lib/gitlab/ci/status/core/running.rb index 0b027f4dc9c..01f0c57ef5f 100644 --- a/lib/gitlab/ci/status/core/running.rb +++ b/lib/gitlab/ci/status/core/running.rb @@ -1,7 +1,14 @@ module Gitlab::Ci module Status module Core - class Running + class Running < Core::Base + def label + 'running' + end + + def icon + 'icon_status_running' + end end end end diff --git a/lib/gitlab/ci/status/core/skipped.rb b/lib/gitlab/ci/status/core/skipped.rb index b8b07a69156..e791341b7e0 100644 --- a/lib/gitlab/ci/status/core/skipped.rb +++ b/lib/gitlab/ci/status/core/skipped.rb @@ -1,7 +1,14 @@ module Gitlab::Ci module Status module Core - class Skipped + class Skipped < Core::Base + def label + 'skipped' + end + + def icon + 'icon_status_skipped' + end end end end diff --git a/lib/gitlab/ci/status/core/success.rb b/lib/gitlab/ci/status/core/success.rb index e32a5228619..bcfe7e63a6c 100644 --- a/lib/gitlab/ci/status/core/success.rb +++ b/lib/gitlab/ci/status/core/success.rb @@ -7,7 +7,7 @@ module Gitlab::Ci end def icon - 'success' + 'icon_status_success' end end end -- cgit v1.2.1 From 119757ac9c2e968a4e97bd3a7f7d7a783456da83 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 2 Dec 2016 13:27:26 +0100 Subject: Add scaffold for remaining statuses-related classes --- lib/gitlab/ci/status/extended/base.rb | 11 ++++++++++ lib/gitlab/ci/status/extended/pipeline/common.rb | 24 ++++++++++++++++++++++ .../extended/pipeline/success_with_warnings.rb | 23 +++++++++++++++++++++ lib/gitlab/ci/status/factory.rb | 6 ++++++ 4 files changed, 64 insertions(+) create mode 100644 lib/gitlab/ci/status/extended/base.rb create mode 100644 lib/gitlab/ci/status/extended/pipeline/common.rb create mode 100644 lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb create mode 100644 lib/gitlab/ci/status/factory.rb (limited to 'lib') diff --git a/lib/gitlab/ci/status/extended/base.rb b/lib/gitlab/ci/status/extended/base.rb new file mode 100644 index 00000000000..1d7819c6891 --- /dev/null +++ b/lib/gitlab/ci/status/extended/base.rb @@ -0,0 +1,11 @@ +module Gitlab::Ci + module Status + module Extended + module Base + def matches?(_subject) + raise NotImplementedError + end + end + end + end +end diff --git a/lib/gitlab/ci/status/extended/pipeline/common.rb b/lib/gitlab/ci/status/extended/pipeline/common.rb new file mode 100644 index 00000000000..75d392fab6c --- /dev/null +++ b/lib/gitlab/ci/status/extended/pipeline/common.rb @@ -0,0 +1,24 @@ +module Gitlab::Ci + module Status + module Extended + module Pipeline + module Common + def initialize(pipeline) + @pipeline = pipeline + end + + def has_details? + true + end + + def details_path + end + + def has_action? + false + end + end + end + end + end +end diff --git a/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb b/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb new file mode 100644 index 00000000000..5e92bb97eec --- /dev/null +++ b/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb @@ -0,0 +1,23 @@ +module Gitlab::Ci + module Status + module Extended + module Pipeline + class SuccessWithWarnings < SimpleDelegator + extend Status::Extended::Base + + def label + 'passed with warnings' + end + + def icon + 'icon_status_warning' + end + + def self.matches?(pipeline) + pipeline.success? && pipeline.has_warnings? + end + end + end + end + end +end diff --git a/lib/gitlab/ci/status/factory.rb b/lib/gitlab/ci/status/factory.rb new file mode 100644 index 00000000000..212cd1a1687 --- /dev/null +++ b/lib/gitlab/ci/status/factory.rb @@ -0,0 +1,6 @@ +module Gitlab::Ci + module Status + class Factory + end + end +end -- cgit v1.2.1 From 0c7168b98d69dc4071873a2787e4974a4c24ea20 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 2 Dec 2016 13:56:33 +0100 Subject: Add information about badge test to core statuses --- lib/gitlab/ci/status/core/canceled.rb | 4 ++++ lib/gitlab/ci/status/core/created.rb | 4 ++++ lib/gitlab/ci/status/core/failed.rb | 4 ++++ lib/gitlab/ci/status/core/pending.rb | 4 ++++ lib/gitlab/ci/status/core/running.rb | 4 ++++ lib/gitlab/ci/status/core/skipped.rb | 4 ++++ lib/gitlab/ci/status/core/success.rb | 4 ++++ lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb | 4 ++++ 8 files changed, 32 insertions(+) (limited to 'lib') diff --git a/lib/gitlab/ci/status/core/canceled.rb b/lib/gitlab/ci/status/core/canceled.rb index 5e06b946a99..a05ac8ee3cc 100644 --- a/lib/gitlab/ci/status/core/canceled.rb +++ b/lib/gitlab/ci/status/core/canceled.rb @@ -2,6 +2,10 @@ module Gitlab::Ci module Status module Core class Canceled < Core::Base + def text + 'canceled' + end + def label 'canceled' end diff --git a/lib/gitlab/ci/status/core/created.rb b/lib/gitlab/ci/status/core/created.rb index c116f9a97f1..ee8bf2e8dac 100644 --- a/lib/gitlab/ci/status/core/created.rb +++ b/lib/gitlab/ci/status/core/created.rb @@ -2,6 +2,10 @@ module Gitlab::Ci module Status module Core class Created < Core::Base + def text + 'created' + end + def label 'created' end diff --git a/lib/gitlab/ci/status/core/failed.rb b/lib/gitlab/ci/status/core/failed.rb index 467ef71e819..ea1615853c0 100644 --- a/lib/gitlab/ci/status/core/failed.rb +++ b/lib/gitlab/ci/status/core/failed.rb @@ -2,6 +2,10 @@ module Gitlab::Ci module Status module Core class Failed < Core::Base + def text + 'failed' + end + def label 'failed' end diff --git a/lib/gitlab/ci/status/core/pending.rb b/lib/gitlab/ci/status/core/pending.rb index 05c9e41091b..95fbb710735 100644 --- a/lib/gitlab/ci/status/core/pending.rb +++ b/lib/gitlab/ci/status/core/pending.rb @@ -2,6 +2,10 @@ module Gitlab::Ci module Status module Core class Pending < Core::Base + def text + 'pending' + end + def label 'pending' end diff --git a/lib/gitlab/ci/status/core/running.rb b/lib/gitlab/ci/status/core/running.rb index 01f0c57ef5f..5580c1a5154 100644 --- a/lib/gitlab/ci/status/core/running.rb +++ b/lib/gitlab/ci/status/core/running.rb @@ -2,6 +2,10 @@ module Gitlab::Ci module Status module Core class Running < Core::Base + def text + 'running' + end + def label 'running' end diff --git a/lib/gitlab/ci/status/core/skipped.rb b/lib/gitlab/ci/status/core/skipped.rb index e791341b7e0..0e8e42f525b 100644 --- a/lib/gitlab/ci/status/core/skipped.rb +++ b/lib/gitlab/ci/status/core/skipped.rb @@ -2,6 +2,10 @@ module Gitlab::Ci module Status module Core class Skipped < Core::Base + def text + 'skipped' + end + def label 'skipped' end diff --git a/lib/gitlab/ci/status/core/success.rb b/lib/gitlab/ci/status/core/success.rb index bcfe7e63a6c..7efafdb615f 100644 --- a/lib/gitlab/ci/status/core/success.rb +++ b/lib/gitlab/ci/status/core/success.rb @@ -2,6 +2,10 @@ module Gitlab::Ci module Status module Core class Success < Core::Base + def text + 'passed' + end + def label 'passed' end diff --git a/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb b/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb index 5e92bb97eec..8f1d9cf87c7 100644 --- a/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb +++ b/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb @@ -5,6 +5,10 @@ module Gitlab::Ci class SuccessWithWarnings < SimpleDelegator extend Status::Extended::Base + def text + 'passed' + end + def label 'passed with warnings' end -- cgit v1.2.1 From c7c249407e98bf5fc099cd89901e67b000fdf69d Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 2 Dec 2016 14:21:04 +0100 Subject: Add implementation of common pipeline extended status --- lib/gitlab/ci/status/core/base.rb | 2 ++ lib/gitlab/ci/status/extended/pipeline/common.rb | 7 +++---- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/ci/status/core/base.rb b/lib/gitlab/ci/status/core/base.rb index f7687c49ffd..d1896a610b7 100644 --- a/lib/gitlab/ci/status/core/base.rb +++ b/lib/gitlab/ci/status/core/base.rb @@ -4,6 +4,8 @@ module Gitlab::Ci # Base abstract class fore core status # class Base + include Gitlab::Routing.url_helpers + def initialize(subject) @subject = subject end diff --git a/lib/gitlab/ci/status/extended/pipeline/common.rb b/lib/gitlab/ci/status/extended/pipeline/common.rb index 75d392fab6c..1b70ba303dc 100644 --- a/lib/gitlab/ci/status/extended/pipeline/common.rb +++ b/lib/gitlab/ci/status/extended/pipeline/common.rb @@ -3,15 +3,14 @@ module Gitlab::Ci module Extended module Pipeline module Common - def initialize(pipeline) - @pipeline = pipeline - end - def has_details? true end def details_path + namespace_project_pipeline_path(@subject.project.namespace, + @subject.project, + @subject) end def has_action? -- cgit v1.2.1 From f272ee6eba37548cbd8919139d583a71ffdac8dc Mon Sep 17 00:00:00 2001 From: Oswaldo Ferreira Date: Wed, 2 Nov 2016 21:49:13 -0200 Subject: Add shorthand support to gitlab markdown references --- lib/banzai/filter/abstract_reference_filter.rb | 23 ++++++--- lib/banzai/filter/commit_range_reference_filter.rb | 2 +- lib/banzai/filter/commit_reference_filter.rb | 2 +- lib/banzai/filter/label_reference_filter.rb | 54 ++++------------------ lib/banzai/filter/milestone_reference_filter.rb | 20 ++++---- 5 files changed, 41 insertions(+), 60 deletions(-) (limited to 'lib') 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 = " in #{reference}" 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} in #{reference}".html_safe else - "#{escape_once(super)} in #{escape_once(object.project.path_with_namespace)}". - html_safe + milestone_link end end -- cgit v1.2.1 From 8b5c16e4b1d54745ba6ca65ecfbf14c1683db3b4 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Mon, 28 Nov 2016 21:33:12 +0100 Subject: API: Ability to remove source branch --- lib/api/merge_requests.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 97baebc1d27..5535c807d21 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -28,6 +28,7 @@ module API optional :assignee_id, type: Integer, desc: 'The ID of a user to assign the merge request' optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign the merge request' optional :labels, type: String, desc: 'Comma-separated list of label names' + optional :remove_source_branch, type: Boolean, desc: 'Remove source branch when merging' end end @@ -75,7 +76,8 @@ module API post ":id/merge_requests" do authorize! :create_merge_request, user_project - mr_params = declared_params + mr_params = declared_params(include_missing: false) + mr_params[:force_remove_source_branch] = mr_params.delete(:remove_source_branch) if mr_params[:remove_source_branch].present? merge_request = ::MergeRequests::CreateService.new(user_project, current_user, mr_params).execute @@ -144,13 +146,15 @@ module API desc: 'Status of the merge request' use :optional_params at_least_one_of :title, :target_branch, :description, :assignee_id, - :milestone_id, :labels, :state_event + :milestone_id, :labels, :state_event, + :remove_source_branch end put path do merge_request = user_project.merge_requests.find(params.delete(:merge_request_id)) authorize! :update_merge_request, merge_request mr_params = declared_params(include_missing: false) + mr_params[:force_remove_source_branch] = mr_params.delete(:remove_source_branch) if mr_params[:remove_source_branch].present? merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, mr_params).execute(merge_request) -- cgit v1.2.1 From 74c8669b0a96b6afcb41ce5e09b147c7309ecbeb Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sun, 4 Dec 2016 18:11:19 +0100 Subject: Use the pagination helper in the API --- lib/api/access_requests.rb | 5 +++++ lib/api/award_emoji.rb | 5 +++++ lib/api/builds.rb | 7 +++++-- lib/api/commit_statuses.rb | 4 +++- lib/api/groups.rb | 12 ++++++++++-- lib/api/members.rb | 6 ++++-- lib/api/merge_requests.rb | 9 +++++++++ lib/api/milestones.rb | 5 ++++- lib/api/namespaces.rb | 4 +++- lib/api/notes.rb | 4 +++- lib/api/project_hooks.rb | 12 ++++++++---- lib/api/project_snippets.rb | 6 +++++- lib/api/runners.rb | 5 +++++ lib/api/todos.rb | 10 ++++++---- lib/api/triggers.rb | 5 +++++ lib/api/users.rb | 5 ++++- 16 files changed, 84 insertions(+), 20 deletions(-) (limited to 'lib') 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/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 492884d162b..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) diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 5315c22e1e4..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 @@ -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 @@ -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]) 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/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 97baebc1d27..752c105ff7c 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! } @@ -42,6 +44,7 @@ module API optional :sort, type: String, values: %w[asc desc], default: 'desc', desc: 'Return merge requests sorted in `asc` or `desc` order.' 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 @@ -218,6 +221,9 @@ module API 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]) @@ -255,6 +261,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 50d6109be3d..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 @@ -30,6 +31,7 @@ module API optional :state, type: String, values: %w[active closed all], default: 'all', desc: 'Return "active", "closed", or "all" milestones' optional :iid, type: Array[Integer], desc: 'The IID of the milestone' + use :pagination end get ":id/milestones" do authorize! :read_milestone, user_project @@ -103,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/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/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/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 bb4de39def1..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 @@ -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 a73650dc361..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) @@ -330,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]) -- cgit v1.2.1 From bb447383c527d28bf4f884df18462e22be05f3b4 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Tue, 22 Nov 2016 18:11:34 +0100 Subject: Add issue events filter and make sure "All" really shows everything Currently, the EventFilter whitelists event types for the "All" filter. This has gotten outdated, which causes the confusing behaviour of the "All" tab not really showing all events. To make matters worse, by default no tab at all is selected, which does show all events, so selecting the "All" tab actually hides some events. Fix this by: - Making sure All always includes all activity, by abolishing the whitelist and just returning all events instead. - Make the All tab selected by default. - Add Issue events tab to include the specific events around opening and closing issues, since there was no specific filter to see them yet. Fixes #24826 --- lib/event_filter.rb | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) (limited to 'lib') diff --git a/lib/event_filter.rb b/lib/event_filter.rb index 21f6a9a762b..515095af1c2 100644 --- a/lib/event_filter.rb +++ b/lib/event_filter.rb @@ -14,6 +14,10 @@ class EventFilter 'merged' end + def issue + 'issue' + end + def comments 'comments' end @@ -32,32 +36,20 @@ class EventFilter end def apply_filter(events) - return events unless params.present? - - filter = params.dup - actions = [] + return events if params.blank? || params == EventFilter.all - case filter + case params when EventFilter.push - actions = [Event::PUSHED] + events.where(action: Event::PUSHED) when EventFilter.merged - actions = [Event::MERGED] + events.where(action: Event::MERGED) when EventFilter.comments - actions = [Event::COMMENTED] + events.where(action: Event::COMMENTED) when EventFilter.team - actions = [Event::JOINED, Event::LEFT, Event::EXPIRED] - when EventFilter.all - actions = [ - Event::PUSHED, - Event::MERGED, - Event::COMMENTED, - Event::JOINED, - Event::LEFT, - Event::EXPIRED - ] + events.where(action: [Event::JOINED, Event::LEFT, Event::EXPIRED]) + when EventFilter.issue + events.where(action: [Event::CREATED, Event::UPDATED, Event::CLOSED, Event::REOPENED]) end - - events.where(action: actions) end def options(key) @@ -73,6 +65,10 @@ class EventFilter end def active?(key) - params.include? key + if params.present? + params.include? key + else + key == EventFilter.all + end end end -- cgit v1.2.1 From 13858d6873afb1168247f79c183ad8260bf4ccf4 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Mon, 28 Nov 2016 13:18:06 +0100 Subject: Accept `issue new` as command to create an issue Now only `/trigger issue create` is a valid command, but our UI shows Issue new everywhere. The default now will be `/trigger issue new`. The help message is adjusted to reflect this. Fixes: gitlab-org/gitlab-ce#25025 --- lib/gitlab/chat_commands/issue_create.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/chat_commands/issue_create.rb b/lib/gitlab/chat_commands/issue_create.rb index 99c1382af44..1dba85c1b51 100644 --- a/lib/gitlab/chat_commands/issue_create.rb +++ b/lib/gitlab/chat_commands/issue_create.rb @@ -4,11 +4,11 @@ module Gitlab def self.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+(?[^\n]*)\n*(?<description>(.|\n)*)/.match(text) + /\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) -- cgit v1.2.1 From b86d8afe23524d10956cfbc1b87337fd2ce75e8c Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon <grzesiek.bizon@gmail.com> Date: Mon, 5 Dec 2016 11:35:27 +0100 Subject: Fold core/extended status modules to reduce nesting --- lib/gitlab/ci/status/canceled.rb | 19 +++++++++ lib/gitlab/ci/status/core.rb | 47 ++++++++++++++++++++++ lib/gitlab/ci/status/core/base.rb | 47 ---------------------- lib/gitlab/ci/status/core/canceled.rb | 19 --------- lib/gitlab/ci/status/core/created.rb | 19 --------- lib/gitlab/ci/status/core/failed.rb | 19 --------- lib/gitlab/ci/status/core/pending.rb | 19 --------- lib/gitlab/ci/status/core/running.rb | 19 --------- lib/gitlab/ci/status/core/skipped.rb | 19 --------- lib/gitlab/ci/status/core/success.rb | 19 --------- lib/gitlab/ci/status/created.rb | 19 +++++++++ lib/gitlab/ci/status/extended.rb | 11 +++++ lib/gitlab/ci/status/extended/base.rb | 11 ----- lib/gitlab/ci/status/extended/pipeline/common.rb | 23 ----------- .../extended/pipeline/success_with_warnings.rb | 27 ------------- lib/gitlab/ci/status/factory.rb | 6 --- lib/gitlab/ci/status/failed.rb | 19 +++++++++ lib/gitlab/ci/status/pending.rb | 19 +++++++++ lib/gitlab/ci/status/pipeline/common.rb | 23 +++++++++++ .../ci/status/pipeline/success_with_warnings.rb | 27 +++++++++++++ lib/gitlab/ci/status/running.rb | 19 +++++++++ lib/gitlab/ci/status/skipped.rb | 19 +++++++++ lib/gitlab/ci/status/success.rb | 19 +++++++++ 23 files changed, 241 insertions(+), 247 deletions(-) create mode 100644 lib/gitlab/ci/status/canceled.rb create mode 100644 lib/gitlab/ci/status/core.rb delete mode 100644 lib/gitlab/ci/status/core/base.rb delete mode 100644 lib/gitlab/ci/status/core/canceled.rb delete mode 100644 lib/gitlab/ci/status/core/created.rb delete mode 100644 lib/gitlab/ci/status/core/failed.rb delete mode 100644 lib/gitlab/ci/status/core/pending.rb delete mode 100644 lib/gitlab/ci/status/core/running.rb delete mode 100644 lib/gitlab/ci/status/core/skipped.rb delete mode 100644 lib/gitlab/ci/status/core/success.rb create mode 100644 lib/gitlab/ci/status/created.rb create mode 100644 lib/gitlab/ci/status/extended.rb delete mode 100644 lib/gitlab/ci/status/extended/base.rb delete mode 100644 lib/gitlab/ci/status/extended/pipeline/common.rb delete mode 100644 lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb delete mode 100644 lib/gitlab/ci/status/factory.rb create mode 100644 lib/gitlab/ci/status/failed.rb create mode 100644 lib/gitlab/ci/status/pending.rb create mode 100644 lib/gitlab/ci/status/pipeline/common.rb create mode 100644 lib/gitlab/ci/status/pipeline/success_with_warnings.rb create mode 100644 lib/gitlab/ci/status/running.rb create mode 100644 lib/gitlab/ci/status/skipped.rb create mode 100644 lib/gitlab/ci/status/success.rb (limited to 'lib') diff --git a/lib/gitlab/ci/status/canceled.rb b/lib/gitlab/ci/status/canceled.rb new file mode 100644 index 00000000000..dd6d99e9075 --- /dev/null +++ b/lib/gitlab/ci/status/canceled.rb @@ -0,0 +1,19 @@ +module Gitlab + module Ci + module Status + class Canceled < Status::Core + def text + 'canceled' + end + + def label + 'canceled' + end + + def icon + 'icon_status_canceled' + end + end + end + end +end diff --git a/lib/gitlab/ci/status/core.rb b/lib/gitlab/ci/status/core.rb new file mode 100644 index 00000000000..fbfe257eeca --- /dev/null +++ b/lib/gitlab/ci/status/core.rb @@ -0,0 +1,47 @@ +module Gitlab + module Ci + module Status + # Base abstract class fore core status + # + class Core + include Gitlab::Routing.url_helpers + + def initialize(subject) + @subject = subject + end + + def icon + raise NotImplementedError + end + + def label + raise NotImplementedError + end + + def title + "#{@subject.class.name.demodulize}: #{label}" + end + + def has_details? + raise NotImplementedError + end + + def details_path + raise NotImplementedError + end + + def has_action? + raise NotImplementedError + end + + def action_icon + raise NotImplementedError + end + + def action_path + raise NotImplementedError + end + end + end + end +end diff --git a/lib/gitlab/ci/status/core/base.rb b/lib/gitlab/ci/status/core/base.rb deleted file mode 100644 index d1896a610b7..00000000000 --- a/lib/gitlab/ci/status/core/base.rb +++ /dev/null @@ -1,47 +0,0 @@ -module Gitlab::Ci - module Status - module Core - # Base abstract class fore core status - # - class Base - include Gitlab::Routing.url_helpers - - def initialize(subject) - @subject = subject - end - - def icon - raise NotImplementedError - end - - def label - raise NotImplementedError - end - - def title - "#{@subject.class.name.demodulize}: #{label}" - end - - def has_details? - raise NotImplementedError - end - - def details_path - raise NotImplementedError - end - - def has_action? - raise NotImplementedError - end - - def action_icon - raise NotImplementedError - end - - def action_path - raise NotImplementedError - end - end - end - end -end diff --git a/lib/gitlab/ci/status/core/canceled.rb b/lib/gitlab/ci/status/core/canceled.rb deleted file mode 100644 index a05ac8ee3cc..00000000000 --- a/lib/gitlab/ci/status/core/canceled.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Gitlab::Ci - module Status - module Core - class Canceled < Core::Base - def text - 'canceled' - end - - def label - 'canceled' - end - - def icon - 'icon_status_canceled' - end - end - end - end -end diff --git a/lib/gitlab/ci/status/core/created.rb b/lib/gitlab/ci/status/core/created.rb deleted file mode 100644 index ee8bf2e8dac..00000000000 --- a/lib/gitlab/ci/status/core/created.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Gitlab::Ci - module Status - module Core - class Created < Core::Base - def text - 'created' - end - - def label - 'created' - end - - def icon - 'icon_status_created' - end - end - end - end -end diff --git a/lib/gitlab/ci/status/core/failed.rb b/lib/gitlab/ci/status/core/failed.rb deleted file mode 100644 index ea1615853c0..00000000000 --- a/lib/gitlab/ci/status/core/failed.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Gitlab::Ci - module Status - module Core - class Failed < Core::Base - def text - 'failed' - end - - def label - 'failed' - end - - def icon - 'icon_status_failed' - end - end - end - end -end diff --git a/lib/gitlab/ci/status/core/pending.rb b/lib/gitlab/ci/status/core/pending.rb deleted file mode 100644 index 95fbb710735..00000000000 --- a/lib/gitlab/ci/status/core/pending.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Gitlab::Ci - module Status - module Core - class Pending < Core::Base - def text - 'pending' - end - - def label - 'pending' - end - - def icon - 'icon_status_pending' - end - end - end - end -end diff --git a/lib/gitlab/ci/status/core/running.rb b/lib/gitlab/ci/status/core/running.rb deleted file mode 100644 index 5580c1a5154..00000000000 --- a/lib/gitlab/ci/status/core/running.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Gitlab::Ci - module Status - module Core - class Running < Core::Base - def text - 'running' - end - - def label - 'running' - end - - def icon - 'icon_status_running' - end - end - end - end -end diff --git a/lib/gitlab/ci/status/core/skipped.rb b/lib/gitlab/ci/status/core/skipped.rb deleted file mode 100644 index 0e8e42f525b..00000000000 --- a/lib/gitlab/ci/status/core/skipped.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Gitlab::Ci - module Status - module Core - class Skipped < Core::Base - def text - 'skipped' - end - - def label - 'skipped' - end - - def icon - 'icon_status_skipped' - end - end - end - end -end diff --git a/lib/gitlab/ci/status/core/success.rb b/lib/gitlab/ci/status/core/success.rb deleted file mode 100644 index 7efafdb615f..00000000000 --- a/lib/gitlab/ci/status/core/success.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Gitlab::Ci - module Status - module Core - class Success < Core::Base - def text - 'passed' - end - - def label - 'passed' - end - - def icon - 'icon_status_success' - end - end - end - end -end diff --git a/lib/gitlab/ci/status/created.rb b/lib/gitlab/ci/status/created.rb new file mode 100644 index 00000000000..6596d7e01ca --- /dev/null +++ b/lib/gitlab/ci/status/created.rb @@ -0,0 +1,19 @@ +module Gitlab + module Ci + module Status + class Created < Status::Core + def text + 'created' + end + + def label + 'created' + end + + def icon + 'icon_status_created' + end + end + end + end +end diff --git a/lib/gitlab/ci/status/extended.rb b/lib/gitlab/ci/status/extended.rb new file mode 100644 index 00000000000..6bfb5d38c1f --- /dev/null +++ b/lib/gitlab/ci/status/extended.rb @@ -0,0 +1,11 @@ +module Gitlab + module Ci + module Status + module Extended + def matches?(_subject) + raise NotImplementedError + end + end + end + end +end diff --git a/lib/gitlab/ci/status/extended/base.rb b/lib/gitlab/ci/status/extended/base.rb deleted file mode 100644 index 1d7819c6891..00000000000 --- a/lib/gitlab/ci/status/extended/base.rb +++ /dev/null @@ -1,11 +0,0 @@ -module Gitlab::Ci - module Status - module Extended - module Base - def matches?(_subject) - raise NotImplementedError - end - end - end - end -end diff --git a/lib/gitlab/ci/status/extended/pipeline/common.rb b/lib/gitlab/ci/status/extended/pipeline/common.rb deleted file mode 100644 index 1b70ba303dc..00000000000 --- a/lib/gitlab/ci/status/extended/pipeline/common.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Gitlab::Ci - module Status - module Extended - module Pipeline - module Common - def has_details? - true - end - - def details_path - namespace_project_pipeline_path(@subject.project.namespace, - @subject.project, - @subject) - end - - def has_action? - false - end - end - end - end - end -end diff --git a/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb b/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb deleted file mode 100644 index 8f1d9cf87c7..00000000000 --- a/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Gitlab::Ci - module Status - module Extended - module Pipeline - class SuccessWithWarnings < SimpleDelegator - extend Status::Extended::Base - - def text - 'passed' - end - - def label - 'passed with warnings' - end - - def icon - 'icon_status_warning' - end - - def self.matches?(pipeline) - pipeline.success? && pipeline.has_warnings? - end - end - end - end - end -end diff --git a/lib/gitlab/ci/status/factory.rb b/lib/gitlab/ci/status/factory.rb deleted file mode 100644 index 212cd1a1687..00000000000 --- a/lib/gitlab/ci/status/factory.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Gitlab::Ci - module Status - class Factory - end - end -end diff --git a/lib/gitlab/ci/status/failed.rb b/lib/gitlab/ci/status/failed.rb new file mode 100644 index 00000000000..c5b5e3203ad --- /dev/null +++ b/lib/gitlab/ci/status/failed.rb @@ -0,0 +1,19 @@ +module Gitlab + module Ci + module Status + class Failed < Status::Core + def text + 'failed' + end + + def label + 'failed' + end + + def icon + 'icon_status_failed' + end + end + end + end +end diff --git a/lib/gitlab/ci/status/pending.rb b/lib/gitlab/ci/status/pending.rb new file mode 100644 index 00000000000..d30f35a59a2 --- /dev/null +++ b/lib/gitlab/ci/status/pending.rb @@ -0,0 +1,19 @@ +module Gitlab + module Ci + module Status + class Pending < Status::Core + def text + 'pending' + end + + def label + 'pending' + end + + def icon + 'icon_status_pending' + end + end + end + end +end diff --git a/lib/gitlab/ci/status/pipeline/common.rb b/lib/gitlab/ci/status/pipeline/common.rb new file mode 100644 index 00000000000..25e52bec3da --- /dev/null +++ b/lib/gitlab/ci/status/pipeline/common.rb @@ -0,0 +1,23 @@ +module Gitlab + module Ci + module Status + module Pipeline + module Common + def has_details? + true + end + + def details_path + namespace_project_pipeline_path(@subject.project.namespace, + @subject.project, + @subject) + end + + def has_action? + false + end + end + end + end + end +end diff --git a/lib/gitlab/ci/status/pipeline/success_with_warnings.rb b/lib/gitlab/ci/status/pipeline/success_with_warnings.rb new file mode 100644 index 00000000000..97dfba81ff5 --- /dev/null +++ b/lib/gitlab/ci/status/pipeline/success_with_warnings.rb @@ -0,0 +1,27 @@ +module Gitlab + module Ci + module Status + module Pipeline + class SuccessWithWarnings < SimpleDelegator + extend Status::Extended + + def text + 'passed' + end + + def label + 'passed with warnings' + end + + def icon + 'icon_status_warning' + end + + def self.matches?(pipeline) + pipeline.success? && pipeline.has_warnings? + end + end + end + end + end +end diff --git a/lib/gitlab/ci/status/running.rb b/lib/gitlab/ci/status/running.rb new file mode 100644 index 00000000000..2aba3c373c7 --- /dev/null +++ b/lib/gitlab/ci/status/running.rb @@ -0,0 +1,19 @@ +module Gitlab + module Ci + module Status + class Running < Status::Core + def text + 'running' + end + + def label + 'running' + end + + def icon + 'icon_status_running' + end + end + end + end +end diff --git a/lib/gitlab/ci/status/skipped.rb b/lib/gitlab/ci/status/skipped.rb new file mode 100644 index 00000000000..16282aefd03 --- /dev/null +++ b/lib/gitlab/ci/status/skipped.rb @@ -0,0 +1,19 @@ +module Gitlab + module Ci + module Status + class Skipped < Status::Core + def text + 'skipped' + end + + def label + 'skipped' + end + + def icon + 'icon_status_skipped' + end + end + end + end +end diff --git a/lib/gitlab/ci/status/success.rb b/lib/gitlab/ci/status/success.rb new file mode 100644 index 00000000000..c09c5f006e3 --- /dev/null +++ b/lib/gitlab/ci/status/success.rb @@ -0,0 +1,19 @@ +module Gitlab + module Ci + module Status + class Success < Status::Core + def text + 'passed' + end + + def label + 'passed' + end + + def icon + 'icon_status_success' + end + end + end + end +end -- cgit v1.2.1 From d28f5e776b90a648a83246beac94518cd8183af4 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon <grzesiek.bizon@gmail.com> Date: Mon, 5 Dec 2016 12:07:14 +0100 Subject: Implement pipeline status factory with extended status --- lib/gitlab/ci/status/pipeline/factory.rb | 39 ++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 lib/gitlab/ci/status/pipeline/factory.rb (limited to 'lib') diff --git a/lib/gitlab/ci/status/pipeline/factory.rb b/lib/gitlab/ci/status/pipeline/factory.rb new file mode 100644 index 00000000000..71d27bf7cf5 --- /dev/null +++ b/lib/gitlab/ci/status/pipeline/factory.rb @@ -0,0 +1,39 @@ +module Gitlab + module Ci + module Status + module Pipeline + class Factory + EXTENDED_STATUSES = [Pipeline::SuccessWithWarnings] + + def initialize(pipeline) + @pipeline = pipeline + @status = pipeline.status || :created + end + + def fabricate! + if extended_status + extended_status.new(core_status) + else + core_status + end + end + + private + + def core_status + Gitlab::Ci::Status + .const_get(@status.capitalize) + .new(@pipeline) + .extend(Status::Pipeline::Common) + end + + def extended_status + @extended ||= EXTENDED_STATUSES.find do |status| + status.matches?(@pipeline) + end + end + end + end + end + end +end -- cgit v1.2.1 From 1123057ab792ac73b1611f4d3a9faf79369dd6da Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt <bob@vanlanduyt.co> Date: Wed, 26 Oct 2016 23:21:50 +0200 Subject: Feature: delegate all open discussions to Issue When a merge request can only be merged when all discussions are resolved. This feature allows to easily delegate those discussions to a new issue, while marking them as resolved in the merge request. The user is presented with a new issue, prepared with mentions of all unresolved discussions, including the first unresolved note of the discussion, time and link to the note. When the issue is created, the discussions in the merge request will get a system note directing the user to the newly created issue. --- lib/api/issues.rb | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 049b4fb214c..cfb7c45de8e 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -28,6 +28,14 @@ module API new_params end + + def merge_request_for_resolving_discussions + return unless merge_request_iid = params[:merge_request_for_resolving_discussions] + + @merge_request_for_resolving_discussions ||= MergeRequestsFinder.new(current_user, project_id: user_project.id). + execute. + find_by(iid: merge_request_iid) + end end resource :issues do @@ -151,24 +159,28 @@ module API # Create a new project issue # # Parameters: - # id (required) - The ID of a project - # title (required) - 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 - # created_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 + # id (required) - The ID of a project + # title (required) - 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 + # created_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 + # merge_request_for_resolving_discussions (optional) - The IID of a merge request for which to resolve discussions # Example Request: # POST /projects/:id/issues post ':id/issues' do required_attributes! [:title] - keys = [:title, :description, :assignee_id, :milestone_id, :due_date, :confidential, :labels] + keys = [:title, :description, :assignee_id, :milestone_id, :due_date, :confidential, :labels, :merge_request_for_resolving_discussions] keys << :created_at if current_user.admin? || user_project.owner == current_user attrs = attributes_for_keys(keys) + attrs[:labels] = params[:labels] if params[:labels] + attrs[:merge_request_for_resolving_discussions] = merge_request_for_resolving_discussions if params[:merge_request_for_resolving_discussions] + # Convert and filter out invalid confidential flags attrs['confidential'] = to_boolean(attrs['confidential']) attrs.delete('confidential') if attrs['confidential'].nil? -- cgit v1.2.1 From bdc13c3142507650e9170cc7b9b63232bb1cfdad Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon <grzesiek.bizon@gmail.com> Date: Tue, 6 Dec 2016 11:22:31 +0100 Subject: Untangle status label and text in ci status helper --- lib/gitlab/ci/status/core.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'lib') diff --git a/lib/gitlab/ci/status/core.rb b/lib/gitlab/ci/status/core.rb index fbfe257eeca..10e04d5be03 100644 --- a/lib/gitlab/ci/status/core.rb +++ b/lib/gitlab/ci/status/core.rb @@ -22,6 +22,17 @@ module Gitlab "#{@subject.class.name.demodulize}: #{label}" end + # Deprecation warning: this method is here because we need to maintain + # backwards compatibility with legacy statuses. We often do something + # like "ci-status ci-status-#{status}" to set CSS class. + # + # `to_s` method should be renamed to `group` at some point, after + # phasing legacy satuses out. + # + def to_s + self.class.name.demodulize.downcase + end + def has_details? raise NotImplementedError end -- cgit v1.2.1 From e94f378b619849c46bf6b736ea9c61c19956b57f Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon <grzesiek.bizon@gmail.com> Date: Tue, 6 Dec 2016 12:16:52 +0100 Subject: Improve support for icons in new detailed statuses --- lib/gitlab/ci/status/core.rb | 2 +- lib/gitlab/ci/status/pipeline/success_with_warnings.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/gitlab/ci/status/core.rb b/lib/gitlab/ci/status/core.rb index 10e04d5be03..ce4108fdcf2 100644 --- a/lib/gitlab/ci/status/core.rb +++ b/lib/gitlab/ci/status/core.rb @@ -30,7 +30,7 @@ module Gitlab # phasing legacy satuses out. # def to_s - self.class.name.demodulize.downcase + self.class.name.demodulize.downcase.underscore end def has_details? diff --git a/lib/gitlab/ci/status/pipeline/success_with_warnings.rb b/lib/gitlab/ci/status/pipeline/success_with_warnings.rb index 97dfba81ff5..4b040d60df8 100644 --- a/lib/gitlab/ci/status/pipeline/success_with_warnings.rb +++ b/lib/gitlab/ci/status/pipeline/success_with_warnings.rb @@ -17,6 +17,10 @@ module Gitlab 'icon_status_warning' end + def to_s + 'success_with_warnings' + end + def self.matches?(pipeline) pipeline.success? && pipeline.has_warnings? end -- cgit v1.2.1 From d865aedafc2282f898b4bd2fdfd3660c47203c37 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski <ayufan@ayufan.eu> Date: Mon, 5 Dec 2016 14:17:42 +0100 Subject: Introduce `Ci::Stage`, right now this is artificial object that is build dynamically. --- lib/gitlab/data_builder/pipeline.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/gitlab/data_builder/pipeline.rb b/lib/gitlab/data_builder/pipeline.rb index 06a783ebc1c..e0284d55da8 100644 --- a/lib/gitlab/data_builder/pipeline.rb +++ b/lib/gitlab/data_builder/pipeline.rb @@ -22,7 +22,7 @@ module Gitlab sha: pipeline.sha, before_sha: pipeline.before_sha, status: pipeline.status, - stages: pipeline.stages, + stages: pipeline.stages.map(&:name), created_at: pipeline.created_at, finished_at: pipeline.finished_at, duration: pipeline.duration -- cgit v1.2.1 From d47aef58cd88fb813390c904bd24525e24d41483 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski <ayufan@ayufan.eu> Date: Mon, 5 Dec 2016 14:28:02 +0100 Subject: Add Ci::Status::Stage --- lib/gitlab/ci/status/stage/common.rb | 24 +++++++++++++++++++++ lib/gitlab/ci/status/stage/factory.rb | 39 +++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 lib/gitlab/ci/status/stage/common.rb create mode 100644 lib/gitlab/ci/status/stage/factory.rb (limited to 'lib') diff --git a/lib/gitlab/ci/status/stage/common.rb b/lib/gitlab/ci/status/stage/common.rb new file mode 100644 index 00000000000..a1513024c6c --- /dev/null +++ b/lib/gitlab/ci/status/stage/common.rb @@ -0,0 +1,24 @@ +module Gitlab + module Ci + module Status + module Stage + module Common + def has_details? + true + end + + def details_path + namespace_project_pipeline_path(@subject.project.namespace, + @subject.project, + @subject, + anchor: subject.name) + end + + def has_action? + false + end + end + end + end + end +end diff --git a/lib/gitlab/ci/status/stage/factory.rb b/lib/gitlab/ci/status/stage/factory.rb new file mode 100644 index 00000000000..2e485ee22a9 --- /dev/null +++ b/lib/gitlab/ci/status/stage/factory.rb @@ -0,0 +1,39 @@ +module Gitlab + module Ci + module Status + module Stage + class Factory + EXTENDED_STATUSES = [] + + def initialize(stage) + @stage = stage + @status = stage.status || :created + end + + def fabricate! + if extended_status + extended_status.new(core_status) + else + core_status + end + end + + private + + def core_status + Gitlab::Ci::Status + .const_get(@status.capitalize) + .new(@stage) + .extend(Status::Pipeline::Common) + end + + def extended_status + @extended ||= EXTENDED_STATUSES.find do |status| + status.matches?(@stage) + end + end + end + end + end + end +end -- cgit v1.2.1 From 10499677e2cbabc6331837d20afc0e0fe68ddc37 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski <ayufan@ayufan.eu> Date: Mon, 5 Dec 2016 14:38:01 +0100 Subject: Added Stage tests --- lib/gitlab/ci/status/stage/factory.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/gitlab/ci/status/stage/factory.rb b/lib/gitlab/ci/status/stage/factory.rb index 2e485ee22a9..a2f7ad81d39 100644 --- a/lib/gitlab/ci/status/stage/factory.rb +++ b/lib/gitlab/ci/status/stage/factory.rb @@ -24,7 +24,7 @@ module Gitlab Gitlab::Ci::Status .const_get(@status.capitalize) .new(@stage) - .extend(Status::Pipeline::Common) + .extend(Status::Stage::Common) end def extended_status -- cgit v1.2.1 From 13cee6d7fc568f42a43dd78cc86c033d06faf2b3 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski <ayufan@ayufan.eu> Date: Mon, 5 Dec 2016 14:47:35 +0100 Subject: Fix test failures --- lib/gitlab/ci/status/stage/common.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/ci/status/stage/common.rb b/lib/gitlab/ci/status/stage/common.rb index a1513024c6c..14c437d2b98 100644 --- a/lib/gitlab/ci/status/stage/common.rb +++ b/lib/gitlab/ci/status/stage/common.rb @@ -10,8 +10,8 @@ module Gitlab def details_path namespace_project_pipeline_path(@subject.project.namespace, @subject.project, - @subject, - anchor: subject.name) + @subject.pipeline, + anchor: @subject.name) end def has_action? -- cgit v1.2.1 From 953a10947bea8ff75290cd50fae4ae1f7636a71d Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski <ayufan@ayufan.eu> Date: Tue, 6 Dec 2016 14:49:37 +0100 Subject: Added Ci::Stage specs --- lib/gitlab/data_builder/pipeline.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/gitlab/data_builder/pipeline.rb b/lib/gitlab/data_builder/pipeline.rb index e0284d55da8..e50e54b6e99 100644 --- a/lib/gitlab/data_builder/pipeline.rb +++ b/lib/gitlab/data_builder/pipeline.rb @@ -22,7 +22,7 @@ module Gitlab sha: pipeline.sha, before_sha: pipeline.before_sha, status: pipeline.status, - stages: pipeline.stages.map(&:name), + stages: pipeline.stages_name, created_at: pipeline.created_at, finished_at: pipeline.finished_at, duration: pipeline.duration -- cgit v1.2.1 From 51a921baf90be2a6654990b9b7d062f4c613a64b Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt <bob@vanlanduyt.co> Date: Tue, 6 Dec 2016 17:13:14 +0100 Subject: A simpler implementation of finding a merge request Following a discussion in !7180 --- lib/api/issues.rb | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/api/issues.rb b/lib/api/issues.rb index cfb7c45de8e..26c8f2fecd0 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -28,14 +28,6 @@ module API new_params end - - def merge_request_for_resolving_discussions - return unless merge_request_iid = params[:merge_request_for_resolving_discussions] - - @merge_request_for_resolving_discussions ||= MergeRequestsFinder.new(current_user, project_id: user_project.id). - execute. - find_by(iid: merge_request_iid) - end end resource :issues do @@ -179,7 +171,12 @@ module API attrs = attributes_for_keys(keys) attrs[:labels] = params[:labels] if params[:labels] - attrs[:merge_request_for_resolving_discussions] = merge_request_for_resolving_discussions if params[:merge_request_for_resolving_discussions] + + if merge_request_iid = params[:merge_request_for_resolving_discussions] + attrs[:merge_request_for_resolving_discussions] = MergeRequestsFinder.new(current_user, project_id: user_project.id). + execute. + find_by(iid: merge_request_iid) + end # Convert and filter out invalid confidential flags attrs['confidential'] = to_boolean(attrs['confidential']) -- cgit v1.2.1 From 3e7818e93a69d2f628c864e40b00ab0871bff3dc Mon Sep 17 00:00:00 2001 From: Robert Schilling <rschilling@student.tugraz.at> Date: Mon, 7 Nov 2016 15:15:14 +0100 Subject: Grapify the issues API --- lib/api/helpers.rb | 16 ---- lib/api/issues.rb | 263 +++++++++++++++++++++++------------------------------ 2 files changed, 112 insertions(+), 167 deletions(-) (limited to 'lib') diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 7f94ede7940..898ca470a30 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -217,22 +217,6 @@ module API end end - def issuable_order_by - if params["order_by"] == 'updated_at' - 'updated_at' - else - 'created_at' - end - end - - def issuable_sort - if params["sort"] == 'asc' - :asc - else - :desc - end - end - def filter_by_iid(items, iid) items.where(iid: iid) end diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 26c8f2fecd0..c9124649cbb 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -1,6 +1,7 @@ module API - # Issues API class Issues < Grape::API + include PaginationParams + before { authenticate! } helpers do @@ -20,77 +21,68 @@ module API 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) + params :issues_params do + optional :labels, type: String, desc: 'Comma-separated list of label names' + optional :order_by, type: String, values: %w[created_at updated_at], default: 'created_at', + desc: 'Return issues ordered by `created_at` or `updated_at` fields.' + optional :sort, type: String, values: %w[asc desc], default: 'desc', + desc: 'Return issues sorted in `asc` or `desc` order.' + use :pagination + end - new_params + params :issue_params do + 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: 'Comma-separated list of label names' + optional :due_date, type: String, desc: 'Date time string in the format YEAR-MONTH-DAY' + optional :confidential, type: Boolean, desc: 'Boolean parameter if the issue should be confidential' + optional :state_event, type: String, values: %w[open close], + desc: 'State of the issue' end end resource :issues do - # Get currently authenticated user's issues - # - # Parameters: - # state (optional) - Return "opened" or "closed" issues - # labels (optional) - Comma-separated list of label names - # order_by (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` - # sort (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc` - # - # Example Requests: - # GET /issues - # GET /issues?state=opened - # GET /issues?state=closed - # GET /issues?labels=foo - # GET /issues?labels=foo,bar - # GET /issues?labels=foo,bar&state=opened + desc "Get currently authenticated user's issues" do + success Entities::Issue + end + params do + optional :state, type: String, values: %w[opened closed all], default: 'all', + desc: 'Return opened, closed, or all issues' + use :issues_params + end get do issues = current_user.issues.inc_notes_with_associations - issues = filter_issues_state(issues, params[:state]) unless params[:state].nil? + issues = filter_issues_state(issues, params[:state]) issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil? - issues = issues.reorder(issuable_order_by => issuable_sort) + issues = issues.reorder(params[:order_by] => params[:sort]) present paginate(issues), with: Entities::Issue, current_user: current_user end end + params do + requires :id, type: String, desc: 'The ID of a group' + end resource :groups do - # Get a list of group issues - # - # Parameters: - # id (required) - The ID of a group - # state (optional) - Return "opened" or "closed" issues - # labels (optional) - Comma-separated list of label names - # milestone (optional) - Milestone title - # order_by (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` - # sort (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc` - # - # Example Requests: - # GET /groups/:id/issues - # GET /groups/:id/issues?state=opened - # GET /groups/:id/issues?state=closed - # GET /groups/:id/issues?labels=foo - # GET /groups/:id/issues?labels=foo,bar - # GET /groups/:id/issues?labels=foo,bar&state=opened - # GET /groups/:id/issues?milestone=1.0.0 - # GET /groups/:id/issues?milestone=1.0.0&state=closed + desc 'Get a list of group issues' do + success Entities::Issue + end + params do + optional :state, type: String, values: %w[opened closed all], default: 'opened', + desc: 'Return opened, closed, or all issues' + use :issues_params + end get ":id/issues" do - group = find_group!(params[:id]) + group = find_group!(params.delete(:id)) - params[:state] ||= 'opened' params[:group_id] = group.id params[:milestone_title] = params.delete(:milestone) params[:label_name] = params.delete(:labels) - if params[:order_by] || params[:sort] - # The Sortable concern takes 'created_desc', not 'created_at_desc' (for example) - params[:sort] = "#{issuable_order_by.sub('_at', '')}_#{issuable_sort}" - end - issues = IssuesFinder.new(current_user, params).execute + issues = issues.reorder(params[:order_by] => params[:sort]) present paginate(issues), with: Entities::Issue, current_user: current_user end end @@ -98,32 +90,19 @@ module API params do requires :id, type: String, desc: 'The ID of a project' end - resource :projects do - # Get a list of project issues - # - # Parameters: - # id (required) - The ID of a project - # iid (optional) - Return the project issue having the given `iid` - # state (optional) - Return "opened" or "closed" issues - # labels (optional) - Comma-separated list of label names - # milestone (optional) - Milestone title - # order_by (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` - # sort (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc` - # - # Example Requests: - # GET /projects/:id/issues - # GET /projects/:id/issues?state=opened - # GET /projects/:id/issues?state=closed - # GET /projects/:id/issues?labels=foo - # GET /projects/:id/issues?labels=foo,bar - # GET /projects/:id/issues?labels=foo,bar&state=opened - # GET /projects/:id/issues?milestone=1.0.0 - # GET /projects/:id/issues?milestone=1.0.0&state=closed - # GET /issues?iid=42 + desc 'Get a list of project issues' do + success Entities::Issue + end + params do + optional :state, type: String, values: %w[opened closed all], default: 'all', + desc: 'Return opened, closed, or all issues' + optional :iid, type: Integer, desc: 'The IID of the issue' + use :issues_params + end get ":id/issues" do 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_state(issues, params[:state]) issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil? issues = filter_by_iid(issues, params[:iid]) unless params[:iid].nil? @@ -131,59 +110,49 @@ module API issues = filter_issues_milestone(issues, params[:milestone]) end - issues = issues.reorder(issuable_order_by => issuable_sort) - + issues = issues.reorder(params[:order_by] => params[:sort]) present paginate(issues), with: Entities::Issue, current_user: current_user, project: user_project end - # Get a single project issue - # - # Parameters: - # id (required) - The ID of a project - # issue_id (required) - The ID of a project issue - # Example Request: - # GET /projects/:id/issues/:issue_id + desc 'Get a single project issue' do + success Entities::Issue + end + params do + requires :issue_id, type: Integer, desc: 'The ID of a project issue' + end get ":id/issues/:issue_id" do - @issue = find_project_issue(params[:issue_id]) - present @issue, with: Entities::Issue, current_user: current_user, project: user_project + issue = find_project_issue(params[:issue_id]) + present issue, with: Entities::Issue, current_user: current_user, project: user_project end - # Create a new project issue - # - # Parameters: - # id (required) - The ID of a project - # title (required) - 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 - # created_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 - # merge_request_for_resolving_discussions (optional) - The IID of a merge request for which to resolve discussions - # Example Request: - # POST /projects/:id/issues + desc 'Create a new project issue' do + success Entities::Issue + end + params do + requires :title, type: String, desc: 'The title of an issue' + optional :created_at, type: DateTime, + desc: 'Date time when the issue was created. Available only for admins and project owners.' + optional :merge_request_for_resolving_discussions, type: Integer, + desc: 'The IID of a merge request for which to resolve discussions' + use :issue_params + end post ':id/issues' do - required_attributes! [:title] - - keys = [:title, :description, :assignee_id, :milestone_id, :due_date, :confidential, :labels, :merge_request_for_resolving_discussions] - keys << :created_at if current_user.admin? || user_project.owner == current_user - attrs = attributes_for_keys(keys) + # Setting created_at time only allowed for admins and project owners + unless current_user.admin? || user_project.owner == current_user + params.delete(:created_at) + end - attrs[:labels] = params[:labels] if params[:labels] + issue_params = declared_params(include_missing: false) if merge_request_iid = params[:merge_request_for_resolving_discussions] - attrs[:merge_request_for_resolving_discussions] = MergeRequestsFinder.new(current_user, project_id: user_project.id). + issue_params[:merge_request_for_resolving_discussions] = MergeRequestsFinder.new(current_user, project_id: user_project.id). execute. find_by(iid: merge_request_iid) end - # Convert and filter out invalid confidential flags - attrs['confidential'] = to_boolean(attrs['confidential']) - attrs.delete('confidential') if attrs['confidential'].nil? - - issue = ::Issues::CreateService.new(user_project, current_user, attrs.merge(request: request, api: true)).execute - + issue = ::Issues::CreateService.new(user_project, + current_user, + issue_params.merge(request: request, api: true)).execute if issue.spam? render_api_error!({ error: 'Spam detected' }, 400) end @@ -199,31 +168,26 @@ module API 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' + requires :issue_id, type: Integer, desc: 'The ID of a project issue' + optional :title, type: String, desc: 'The title of an issue' + optional :updated_at, type: DateTime, + desc: 'Date time when the issue was updated. Available only for admins and project owners.' + use :issue_params + at_least_one_of :title, :description, :assignee_id, :milestone_id, + :labels, :created_at, :due_date, :confidential, :state_event end put ':id/issues/:issue_id' do - issue = user_project.issues.find(params[:issue_id]) + issue = user_project.issues.find(params.delete(:issue_id)) authorize! :update_issue, issue - # Convert and filter out invalid confidential flags - 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 + # Setting created_at time only allowed for admins and project owners + unless current_user.admin? || user_project.owner == current_user + params.delete(:updated_at) + end - issue = ::Issues::UpdateService.new(user_project, current_user, issue_params).execute(issue) + issue = ::Issues::UpdateService.new(user_project, + current_user, + declared_params(include_missing: false)).execute(issue) if issue.valid? present issue, with: Entities::Issue, current_user: current_user, project: user_project @@ -232,19 +196,19 @@ module API end end - # Move an existing issue - # - # Parameters: - # id (required) - The ID of a project - # issue_id (required) - The ID of a project issue - # to_project_id (required) - The ID of the new project - # Example Request: - # POST /projects/:id/issues/:issue_id/move + desc 'Move an existing issue' do + success Entities::Issue + end + params do + requires :issue_id, type: Integer, desc: 'The ID of a project issue' + requires :to_project_id, type: Integer, desc: 'The ID of the new project' + end post ':id/issues/:issue_id/move' do - required_attributes! [:to_project_id] + issue = user_project.issues.find_by(id: params[:issue_id]) + not_found!('Issue') unless issue - issue = user_project.issues.find(params[:issue_id]) - new_project = Project.find(params[:to_project_id]) + new_project = Project.find_by(id: params[:to_project_id]) + not_found!('Project') unless new_project begin issue = ::Issues::MoveService.new(user_project, current_user).execute(issue, new_project) @@ -254,16 +218,13 @@ module API end end - # - # Delete a project issue - # - # Parameters: - # id (required) - The ID of a project - # issue_id (required) - The ID of a project issue - # Example Request: - # DELETE /projects/:id/issues/:issue_id + desc 'Delete a project issue' + params do + requires :issue_id, type: Integer, desc: 'The ID of a project issue' + end delete ":id/issues/:issue_id" do issue = user_project.issues.find_by(id: params[:issue_id]) + not_found!('Issue') unless issue authorize!(:destroy_issue, issue) issue.destroy -- cgit v1.2.1 From 9e307b93b7c57a14de6e425566f88511024859a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= <remy@rymai.me> Date: Fri, 2 Dec 2016 14:33:29 +0100 Subject: Allow public access to some Tag API endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable <remy@rymai.me> --- lib/api/tags.rb | 1 - 1 file changed, 1 deletion(-) (limited to 'lib') diff --git a/lib/api/tags.rb b/lib/api/tags.rb index cd33f9a9903..5b345db3a41 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -1,7 +1,6 @@ module API # Git Tags API class Tags < Grape::API - before { authenticate! } before { authorize! :download_code, user_project } params do -- cgit v1.2.1 From 93c72e0f71919968972e874519e01370edcce022 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski <ayufan@ayufan.eu> Date: Wed, 7 Dec 2016 13:14:45 +0100 Subject: Add Ci::Status::Factory --- lib/gitlab/ci/status/factory.rb | 43 ++++++++++++++++++++++++++++++++ lib/gitlab/ci/status/pipeline/factory.rb | 30 ++++------------------ lib/gitlab/ci/status/stage/factory.rb | 28 ++------------------- 3 files changed, 50 insertions(+), 51 deletions(-) create mode 100644 lib/gitlab/ci/status/factory.rb (limited to 'lib') diff --git a/lib/gitlab/ci/status/factory.rb b/lib/gitlab/ci/status/factory.rb new file mode 100644 index 00000000000..b2f896f2211 --- /dev/null +++ b/lib/gitlab/ci/status/factory.rb @@ -0,0 +1,43 @@ +module Gitlab + module Ci + module Status + class Factory + attr_reader :subject + + def initialize(subject) + @subject = subject + end + + def fabricate! + if extended_status + extended_status.new(core_status) + else + core_status + end + end + + private + + def subject_status + @subject_status ||= subject.status + end + + def core_status + Gitlab::Ci::Status + .const_get(subject_status.capitalize) + .new(subject) + end + + def extended_status + @extended ||= extended_statuses.find do |status| + status.matches?(subject) + end + end + + def extended_statuses + [] + end + end + end + end +end diff --git a/lib/gitlab/ci/status/pipeline/factory.rb b/lib/gitlab/ci/status/pipeline/factory.rb index 71d27bf7cf5..4ac4ec671d0 100644 --- a/lib/gitlab/ci/status/pipeline/factory.rb +++ b/lib/gitlab/ci/status/pipeline/factory.rb @@ -2,35 +2,15 @@ module Gitlab module Ci module Status module Pipeline - class Factory - EXTENDED_STATUSES = [Pipeline::SuccessWithWarnings] - - def initialize(pipeline) - @pipeline = pipeline - @status = pipeline.status || :created - end - - def fabricate! - if extended_status - extended_status.new(core_status) - else - core_status - end - end - + class Factory < Status::Factory private - def core_status - Gitlab::Ci::Status - .const_get(@status.capitalize) - .new(@pipeline) - .extend(Status::Pipeline::Common) + def extended_statuses + [Pipeline::SuccessWithWarnings] end - def extended_status - @extended ||= EXTENDED_STATUSES.find do |status| - status.matches?(@pipeline) - end + def core_status + super.extend(Status::Pipeline::Common) end end end diff --git a/lib/gitlab/ci/status/stage/factory.rb b/lib/gitlab/ci/status/stage/factory.rb index a2f7ad81d39..c6522d5ada1 100644 --- a/lib/gitlab/ci/status/stage/factory.rb +++ b/lib/gitlab/ci/status/stage/factory.rb @@ -2,35 +2,11 @@ module Gitlab module Ci module Status module Stage - class Factory - EXTENDED_STATUSES = [] - - def initialize(stage) - @stage = stage - @status = stage.status || :created - end - - def fabricate! - if extended_status - extended_status.new(core_status) - else - core_status - end - end - + class Factory < Status::Factory private def core_status - Gitlab::Ci::Status - .const_get(@status.capitalize) - .new(@stage) - .extend(Status::Stage::Common) - end - - def extended_status - @extended ||= EXTENDED_STATUSES.find do |status| - status.matches?(@stage) - end + super.extend(Status::Stage::Common) end end end -- cgit v1.2.1 From 2fc1e643a7d8fb2fb4f9df49a2347d4f82776e99 Mon Sep 17 00:00:00 2001 From: Sean McGivern <sean@gitlab.com> Date: Wed, 7 Dec 2016 12:39:19 +0000 Subject: Fix Backup::Manager#remove_old --- lib/backup/manager.rb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index 96c20100541..7e6537e3d9e 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -82,16 +82,17 @@ module Backup removed = 0 Dir.chdir(Gitlab.config.backup.path) do - file_list = Dir.glob('*_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]})) + Dir.glob('*_gitlab_backup.tar').each do |file| + next unless file =~ /(\d+)(?:_\d{4}_\d{2}_\d{2})?_gitlab_backup\.tar/ + + timestamp = $1.to_i + + if Time.at(timestamp) < (Time.now - keep_time) + begin + FileUtils.rm(file) removed += 1 + rescue => e + $progress.puts "Deleting #{file} failed: #{e.message}".color(:red) end end end -- cgit v1.2.1 From 8b379465a5be48c8062379a3dea8e58110c52d87 Mon Sep 17 00:00:00 2001 From: tiagonbotelho <tiagonbotelho@hotmail.com> Date: Mon, 21 Nov 2016 11:42:10 +0000 Subject: Reenables /user API request to return private-token if user is admin and requested with sudo --- lib/api/entities.rb | 1 + lib/api/users.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 899d68bc6c7..1dd191161ef 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -32,6 +32,7 @@ module API expose :can_create_project?, as: :can_create_project expose :two_factor_enabled?, as: :two_factor_enabled expose :external + expose :private_token, if: lambda { |user, options| user.is_admin? && options[:sudo_identifier] } end class UserLogin < UserFull diff --git a/lib/api/users.rb b/lib/api/users.rb index bc2362aa72e..b3870e0c7c8 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -353,7 +353,7 @@ module API success Entities::UserFull end get do - present current_user, with: Entities::UserFull + present current_user, with: Entities::UserFull, sudo_identifier: sudo_identifier end desc "Get the currently authenticated user's SSH keys" do -- cgit v1.2.1 From 3ed96afc47c481db4f8c0a6581602abaee920808 Mon Sep 17 00:00:00 2001 From: tiagonbotelho <tiagonbotelho@hotmail.com> Date: Mon, 21 Nov 2016 12:59:37 +0000 Subject: adds impersonator variable and makes sudo usage overall more clear --- lib/api/entities.rb | 7 +++---- lib/api/helpers.rb | 13 ++++++++++--- lib/api/session.rb | 4 ++-- lib/api/users.rb | 16 ++++++++-------- 4 files changed, 23 insertions(+), 17 deletions(-) (limited to 'lib') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 1dd191161ef..006d5f9f44e 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -22,7 +22,7 @@ module API expose :provider, :extern_uid end - class UserFull < User + class UserPublic < User expose :last_sign_in_at expose :confirmed_at expose :email @@ -32,10 +32,9 @@ module API expose :can_create_project?, as: :can_create_project expose :two_factor_enabled?, as: :two_factor_enabled expose :external - expose :private_token, if: lambda { |user, options| user.is_admin? && options[:sudo_identifier] } end - class UserLogin < UserFull + class UserWithPrivateToken < UserPublic expose :private_token end @@ -290,7 +289,7 @@ module API end class SSHKeyWithUser < SSHKey - expose :user, using: Entities::UserFull + expose :user, using: Entities::UserPublic end class Note < Grape::Entity diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 7f94ede7940..6a47efc74f0 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -44,11 +44,14 @@ module API return nil end - identifier = sudo_identifier() + identifier = sudo_identifier - # If the sudo is the current user do nothing - if identifier && !(@current_user.id == identifier || @current_user.username == identifier) + if identifier + # We check for private_token because we cannot allow PAT to be used forbidden!('Must be admin to use sudo') unless @current_user.is_admin? + forbidden!('Private token must be specified in order to use sudo') unless private_token_used? + + @impersonator = @current_user @current_user = User.by_username_or_id(identifier) not_found!("No user id or username for: #{identifier}") if @current_user.nil? end @@ -399,6 +402,10 @@ module API links.join(', ') end + def private_token_used? + private_token == @current_user.private_token + end + def secret_token Gitlab::Shell.secret_token end diff --git a/lib/api/session.rb b/lib/api/session.rb index d09400b81f5..002ffd1d154 100644 --- a/lib/api/session.rb +++ b/lib/api/session.rb @@ -1,7 +1,7 @@ module API class Session < Grape::API desc 'Login to get token' do - success Entities::UserLogin + success Entities::UserWithPrivateToken end params do optional :login, type: String, desc: 'The username' @@ -14,7 +14,7 @@ module API return unauthorized! unless user return render_api_error!('401 Unauthorized. You have 2FA enabled. Please use a personal access token to access the API', 401) if user.two_factor_enabled? - present user, with: Entities::UserLogin + present user, with: Entities::UserWithPrivateToken end end end diff --git a/lib/api/users.rb b/lib/api/users.rb index b3870e0c7c8..1dab799dd61 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -51,7 +51,7 @@ module API users = users.external if params[:external] && current_user.is_admin? end - entity = current_user.is_admin? ? Entities::UserFull : Entities::UserBasic + entity = current_user.is_admin? ? Entities::UserPublic : Entities::UserBasic present paginate(users), with: entity end @@ -66,7 +66,7 @@ module API not_found!('User') unless user if current_user && current_user.is_admin? - present user, with: Entities::UserFull + present user, with: Entities::UserPublic elsif can?(current_user, :read_user, user) present user, with: Entities::User else @@ -75,7 +75,7 @@ module API end desc 'Create a user. Available only for admins.' do - success Entities::UserFull + success Entities::UserPublic end params do requires :email, type: String, desc: 'The email of the user' @@ -99,7 +99,7 @@ module API end if user.save - present user, with: Entities::UserFull + present user, with: Entities::UserPublic else conflict!('Email has already been taken') if User. where(email: user.email). @@ -114,7 +114,7 @@ module API end desc 'Update a user. Available only for admins.' do - success Entities::UserFull + success Entities::UserPublic end params do requires :id, type: Integer, desc: 'The ID of the user' @@ -161,7 +161,7 @@ module API user_params.delete(:provider) if user.update_attributes(user_params) - present user, with: Entities::UserFull + present user, with: Entities::UserPublic else render_validation_error!(user) end @@ -350,10 +350,10 @@ module API resource :user do desc 'Get the currently authenticated user' do - success Entities::UserFull + success Entities::UserPublic end get do - present current_user, with: Entities::UserFull, sudo_identifier: sudo_identifier + present current_user, with: @impersonator ? Entities::UserWithPrivateToken : Entities::UserPublic end desc "Get the currently authenticated user's SSH keys" do -- cgit v1.2.1 From f4f9af06c9e6f468d9f696dbb5438dd8825fe773 Mon Sep 17 00:00:00 2001 From: Horacio Sanson <horacio@allm.net> Date: Tue, 29 Nov 2016 13:18:13 +0900 Subject: Enable display of admonition icons in Asciidoc. When rendering Asciidoc documents this merge request enables the diplay of admonition blocks using font icons. This improves the looks of Asciidoc redered files. This uses the font-awesome fonts already present in Gitlab so no large dependencies are required for this to work. --- lib/gitlab/asciidoc.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb index 1a22ad9acf5..9667df4ffb8 100644 --- a/lib/gitlab/asciidoc.rb +++ b/lib/gitlab/asciidoc.rb @@ -6,7 +6,7 @@ module Gitlab module Asciidoc DEFAULT_ADOC_ATTRS = [ 'showtitle', 'idprefix=user-content-', 'idseparator=-', 'env=gitlab', - 'env-gitlab', 'source-highlighter=html-pipeline' + 'env-gitlab', 'source-highlighter=html-pipeline', 'icons=font' ].freeze # Public: Converts the provided Asciidoc markup into HTML. -- cgit v1.2.1 From 83232be0e14cc8b35bf74532203a6e4371c15e70 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> Date: Mon, 31 Oct 2016 13:00:53 +0200 Subject: Add nested groups support on data level * add parent_id field to namespaces table to store relation with nested groups * create routes table to keep information about full path of every group and project * project/group lookup by full path from routes table Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> --- lib/api/helpers.rb | 2 +- lib/constraints/group_url_constrainer.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 7f94ede7940..96ccde760db 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -108,7 +108,7 @@ module API if id =~ /^\d+$/ Group.find_by(id: id) else - Group.find_by(path: id) + Group.find_by_full_path(id) end end diff --git a/lib/constraints/group_url_constrainer.rb b/lib/constraints/group_url_constrainer.rb index 5711d96a586..bae4db1ca4d 100644 --- a/lib/constraints/group_url_constrainer.rb +++ b/lib/constraints/group_url_constrainer.rb @@ -4,7 +4,7 @@ class GroupUrlConstrainer return false unless valid?(id) - Group.find_by(path: id).present? + Group.find_by_full_path(id).present? end private -- cgit v1.2.1 From 4a0ca85834b6cf7decb58857986e535ba4e37c53 Mon Sep 17 00:00:00 2001 From: Felipe Artur <felipefac@gmail.com> Date: Tue, 6 Dec 2016 18:56:59 -0200 Subject: Allow branch names with dots on API endpoint --- lib/api/branches.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/api/branches.rb b/lib/api/branches.rb index 73aed624ea7..0950c3d2e88 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -23,9 +23,9 @@ module API success Entities::RepoBranch end params do - requires :branch, type: String, regexp: /.+/, desc: 'The name of the branch' + requires :branch, type: String, desc: 'The name of the branch' end - get ':id/repository/branches/:branch' do + get ':id/repository/branches/:branch', requirements: { branch: /.+/ } do branch = user_project.repository.find_branch(params[:branch]) not_found!("Branch") unless branch @@ -39,11 +39,11 @@ module API success Entities::RepoBranch end params do - requires :branch, type: String, regexp: /.+/, desc: 'The name of the branch' + requires :branch, type: String, desc: 'The name of the branch' optional :developers_can_push, type: Boolean, desc: 'Flag if developers can push to that branch' optional :developers_can_merge, type: Boolean, desc: 'Flag if developers can merge to that branch' end - put ':id/repository/branches/:branch/protect' do + put ':id/repository/branches/:branch/protect', requirements: { branch: /.+/ } do authorize_admin_project branch = user_project.repository.find_branch(params[:branch]) @@ -76,9 +76,9 @@ module API success Entities::RepoBranch end params do - requires :branch, type: String, regexp: /.+/, desc: 'The name of the branch' + requires :branch, type: String, desc: 'The name of the branch' end - put ':id/repository/branches/:branch/unprotect' do + put ':id/repository/branches/:branch/unprotect', requirements: { branch: /.+/ } do authorize_admin_project branch = user_project.repository.find_branch(params[:branch]) @@ -112,9 +112,9 @@ module API desc 'Delete a branch' params do - requires :branch, type: String, regexp: /.+/, desc: 'The name of the branch' + requires :branch, type: String, desc: 'The name of the branch' end - delete ":id/repository/branches/:branch" do + delete ":id/repository/branches/:branch", requirements: { branch: /.+/ } do authorize_push_project result = DeleteBranchService.new(user_project, current_user). -- cgit v1.2.1 From f23b1cb453deea2659c0cb9e9047c72d859bbf9d Mon Sep 17 00:00:00 2001 From: Douwe Maan <douwe@gitlab.com> Date: Tue, 29 Nov 2016 13:47:43 +0000 Subject: Merge branch 'jej-23867-use-mr-finder-instead-of-access-check' into 'security' Replace MR access checks with use of MergeRequestsFinder Split from !2024 to partially solve https://gitlab.com/gitlab-org/gitlab-ce/issues/23867 :warning: - Potentially untested :bomb: - No test coverage :traffic_light: - Test coverage of some sort exists (a test failed when error raised) :vertical_traffic_light: - Test coverage of return value (a test failed when nil used) :white_check_mark: - Permissions check tested - [x] :bomb: app/finders/notes_finder.rb:17 - [x] :warning: app/views/layouts/nav/_project.html.haml:80 [`.count`] - [x] :bomb: app/controllers/concerns/creates_commit.rb:84 - [x] :traffic_light: app/controllers/projects/commits_controller.rb:24 - [x] :traffic_light: app/controllers/projects/compare_controller.rb:56 - [x] :vertical_traffic_light: app/controllers/projects/discussions_controller.rb:29 - [x] :white_check_mark: app/controllers/projects/todos_controller.rb:27 - [x] :vertical_traffic_light: app/models/commit.rb:268 - [x] :white_check_mark: lib/gitlab/search_results.rb:71 - [x] https://dev.gitlab.org/gitlab/gitlabhq/merge_requests/2024/diffs#d1c10892daedb4d4dd3d4b12b6d071091eea83df_267_266 Memoize ` merged_merge_request(current_user)` - [x] https://dev.gitlab.org/gitlab/gitlabhq/merge_requests/2024/diffs#d1c10892daedb4d4dd3d4b12b6d071091eea83df_248_247 Expected side effect for `merged_merge_request!`, consider `skip_authorization: true`. - [x] https://dev.gitlab.org/gitlab/gitlabhq/merge_requests/2024/diffs#d1c10892daedb4d4dd3d4b12b6d071091eea83df_269_269 Scary use of unchecked `merged_merge_request?` See merge request !2033 --- lib/gitlab/search_results.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb index 47d8599e298..35212992698 100644 --- a/lib/gitlab/search_results.rb +++ b/lib/gitlab/search_results.rb @@ -68,7 +68,7 @@ module Gitlab end def merge_requests - merge_requests = MergeRequest.in_projects(project_ids_relation) + merge_requests = MergeRequestsFinder.new(current_user).execute.in_projects(project_ids_relation) if query =~ /[#!](\d+)\z/ merge_requests = merge_requests.where(iid: $1) else -- cgit v1.2.1 From 593c912151eaef865d31fc2a8307ef6d337c2349 Mon Sep 17 00:00:00 2001 From: Robert Schilling <rschilling@student.tugraz.at> Date: Mon, 5 Dec 2016 15:40:53 +0100 Subject: Grapify the service API --- lib/api/helpers.rb | 11 - lib/api/services.rb | 626 +++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 572 insertions(+), 65 deletions(-) (limited to 'lib') diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 7f94ede7940..16ac40b7142 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -93,17 +93,6 @@ module API end end - def project_service(project = user_project) - @project_service ||= project.find_or_initialize_service(params[:service_slug].underscore) - @project_service || not_found!("Service") - end - - def service_attributes - @service_attributes ||= project_service.fields.inject([]) do |arr, hash| - arr << hash[:name].to_sym - end - end - def find_group(id) if id =~ /^\d+$/ Group.find_by(id: id) diff --git a/lib/api/services.rb b/lib/api/services.rb index bc427705777..fde2e2746f1 100644 --- a/lib/api/services.rb +++ b/lib/api/services.rb @@ -1,84 +1,602 @@ module API - # Projects API class Services < Grape::API + services = { + 'asana' => [ + { + required: true, + name: :api_key, + type: String, + desc: 'User API token' + }, + { + required: false, + name: :restrict_to_branch, + type: String, + desc: 'Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches' + } + ], + 'assembla' => [ + { + required: true, + name: :token, + type: String, + desc: 'The authentication token' + }, + { + required: false, + name: :subdomain, + type: String, + desc: 'Subdomain setting' + } + ], + 'bamboo' => [ + { + required: true, + name: :bamboo_url, + type: String, + desc: 'Bamboo root URL like https://bamboo.example.com' + }, + { + required: true, + name: :build_key, + type: String, + desc: 'Bamboo build plan key like' + }, + { + required: true, + name: :username, + type: String, + desc: 'A user with API access, if applicable' + }, + { + required: true, + name: :password, + type: String, + desc: 'Passord of the user' + } + ], + 'bugzilla' => [ + { + required: true, + name: :new_issue_url, + type: String, + desc: 'New issue URL' + }, + { + required: true, + name: :issues_url, + type: String, + desc: 'Issues URL' + }, + { + required: true, + name: :project_url, + type: String, + desc: 'Project URL' + }, + { + required: false, + name: :description, + type: String, + desc: 'Description' + }, + { + required: false, + name: :title, + type: String, + desc: 'Title' + } + ], + 'buildkite' => [ + { + required: true, + name: :token, + type: String, + desc: 'Buildkite project GitLab token' + }, + { + required: true, + name: :project_url, + type: String, + desc: 'The buildkite project URL' + }, + { + required: false, + name: :enable_ssl_verification, + type: Boolean, + desc: 'Enable SSL verification for communication' + } + ], + 'builds-email' => [ + { + required: true, + name: :recipients, + type: String, + desc: 'Comma-separated list of recipient email addresses' + }, + { + required: false, + name: :add_pusher, + type: Boolean, + desc: 'Add pusher to recipients list' + }, + { + required: false, + name: :notify_only_broken_builds, + type: Boolean, + desc: 'Notify only broken builds' + } + ], + 'campfire' => [ + { + required: true, + name: :token, + type: String, + desc: 'Campfire token' + }, + { + required: false, + name: :subdomain, + type: String, + desc: 'Campfire subdomain' + }, + { + required: false, + name: :room, + type: String, + desc: 'Campfire room' + }, + ], + 'custom-issue-tracker' => [ + { + required: true, + name: :new_issue_url, + type: String, + desc: 'New issue URL' + }, + { + required: true, + name: :issues_url, + type: String, + desc: 'Issues URL' + }, + { + required: true, + name: :project_url, + type: String, + desc: 'Project URL' + }, + { + required: false, + name: :description, + type: String, + desc: 'Description' + }, + { + required: false, + name: :title, + type: String, + desc: 'Title' + } + ], + 'drone-ci' => [ + { + required: true, + name: :token, + type: String, + desc: 'Drone CI token' + }, + { + required: true, + name: :drone_url, + type: String, + desc: 'Drone CI URL' + }, + { + required: false, + name: :enable_ssl_verification, + type: Boolean, + desc: 'Enable SSL verification for communication' + } + ], + 'emails-on-push' => [ + { + required: true, + name: :recipients, + type: String, + desc: 'Comma-separated list of recipient email addresses' + }, + { + required: false, + name: :disable_diffs, + type: Boolean, + desc: 'Disable code diffs' + }, + { + required: false, + name: :send_from_committer_email, + type: Boolean, + desc: 'Send from committer' + } + ], + 'external-wiki' => [ + { + required: true, + name: :external_wiki_url, + type: String, + desc: 'The URL of the external Wiki' + } + ], + 'flowdock' => [ + { + required: true, + name: :token, + type: String, + desc: 'Flowdock token' + } + ], + 'gemnasium' => [ + { + required: true, + name: :api_key, + type: String, + desc: 'Your personal API key on gemnasium.com' + }, + { + required: true, + name: :token, + type: String, + desc: "The project's slug on gemnasium.com" + } + ], + 'hipchat' => [ + { + required: true, + name: :token, + type: String, + desc: 'The room token' + }, + { + required: false, + name: :room, + type: String, + desc: 'The room name or ID' + }, + { + required: false, + name: :color, + type: String, + desc: 'The room color' + }, + { + required: false, + name: :notify, + type: Boolean, + desc: 'Enable notifications' + }, + { + required: false, + name: :api_version, + type: String, + desc: 'Leave blank for default (v2)' + }, + { + required: false, + name: :server, + type: String, + desc: 'Leave blank for default. https://hipchat.example.com' + } + ], + 'irker' => [ + { + required: true, + name: :recipients, + type: String, + desc: 'Recipients/channels separated by whitespaces' + }, + { + required: false, + name: :default_irc_uri, + type: String, + desc: 'Default: irc://irc.network.net:6697' + }, + { + required: false, + name: :server_host, + type: String, + desc: 'Server host. Default localhost' + }, + { + required: false, + name: :server_port, + type: Integer, + desc: 'Server port. Default 6659' + }, + { + required: false, + name: :colorize_messages, + type: Boolean, + desc: 'Colorize messages' + } + ], + 'jira' => [ + { + required: true, + name: :url, + type: String, + desc: 'The URL to the JIRA project which is being linked to this GitLab project, e.g., https://jira.example.com' + }, + { + required: true, + name: :project_key, + type: String, + desc: 'The short identifier for your JIRA project, all uppercase, e.g., PROJ' + }, + { + required: false, + name: :username, + type: String, + desc: 'The username of the user created to be used with GitLab/JIRA' + }, + { + required: false, + name: :password, + type: String, + desc: 'The password of the user created to be used with GitLab/JIRA' + }, + { + required: false, + name: :jira_issue_transition_id, + type: Integer, + desc: 'The ID of a transition that moves issues to a closed state. You can find this number under the JIRA workflow administration (**Administration > Issues > Workflows**) by selecting **View** under **Operations** of the desired workflow of your project. The ID of each state can be found inside the parenthesis of each transition name under the **Transitions (id)** column ([see screenshot][trans]). By default, this ID is set to `2`' + } + ], + 'mattermost-slash-commands' => [ + { + required: true, + name: :token, + type: String, + desc: 'The Mattermost token' + } + ], + 'pipelines-email' => [ + { + required: true, + name: :recipients, + type: String, + desc: 'Comma-separated list of recipient email addresses' + }, + { + required: false, + name: :notify_only_broken_builds, + type: Boolean, + desc: 'Notify only broken builds' + } + ], + 'pivotaltracker' => [ + { + required: true, + name: :token, + type: String, + desc: 'The Pivotaltracker token' + }, + { + required: false, + name: :restrict_to_branch, + type: String, + desc: 'Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches.' + } + ], + 'pushover' => [ + { + required: true, + name: :api_key, + type: String, + desc: 'The application key' + }, + { + required: true, + name: :user_key, + type: String, + desc: 'The user key' + }, + { + required: true, + name: :priority, + type: String, + desc: 'The priority' + }, + { + required: true, + name: :device, + type: String, + desc: 'Leave blank for all active devices' + }, + { + required: true, + name: :sound, + type: String, + desc: 'The sound of the notification' + } + ], + 'redmine' => [ + { + required: true, + name: :new_issue_url, + type: String, + desc: 'The new issue URL' + }, + { + required: true, + name: :project_url, + type: String, + desc: 'The project URL' + }, + { + required: true, + name: :issues_url, + type: String, + desc: 'The issues URL' + }, + { + required: false, + name: :description, + type: String, + desc: 'The description of the tracker' + } + ], + 'slack' => [ + { + required: true, + name: :webhook, + type: String, + desc: 'The Slack webhook. e.g. https://hooks.slack.com/services/...' + }, + { + required: false, + name: :new_issue_url, + type: String, + desc: 'The user name' + }, + { + required: false, + name: :channel, + type: String, + desc: 'The channel name' + } + ], + 'teamcity' => [ + { + required: true, + name: :teamcity_url, + type: String, + desc: 'TeamCity root URL like https://teamcity.example.com' + }, + { + required: true, + name: :build_type, + type: String, + desc: 'Build configuration ID' + }, + { + required: true, + name: :username, + type: String, + desc: 'A user with permissions to trigger a manual build' + }, + { + required: true, + name: :password, + type: String, + desc: 'The password of the user' + } + ] + }.freeze + + trigger_services = { + 'mattermost-slash-commands' => [ + { + name: :token, + type: String, + desc: 'The Mattermost token' + } + ] + }.freeze + resource :projects do before { authenticate! } before { authorize_admin_project } - # Set <service_slug> service for project - # - # Example Request: - # - # PUT /projects/:id/services/gitlab-ci - # - put ':id/services/:service_slug' do - if project_service - validators = project_service.class.validators.select do |s| - s.class == ActiveRecord::Validations::PresenceValidator && - s.attributes != [:project_id] + helpers do + def service_attributes(service) + service.fields.inject([]) do |arr, hash| + arr << hash[:name].to_sym end + end + end - required_attributes! validators.map(&:attributes).flatten.uniq - attrs = attributes_for_keys service_attributes + services.each do |service_slug, settings| + desc "Set #{service_slug} service for project" + params do + settings.each do |setting| + if setting[:required] + requires setting[:name], type: setting[:type], desc: setting[:desc] + else + optional setting[:name], type: setting[:type], desc: setting[:desc] + end + end + end + put ":id/services/#{service_slug}" do + service = user_project.find_or_initialize_service(service_slug.underscore) + service_params = declared_params(include_missing: false).merge(active: true) - if project_service.update_attributes(attrs.merge(active: true)) + if service.update_attributes(service_params) true else - not_found! + render_api_error!('400 Bad Request', 400) end end end - # Delete <service_slug> service for project - # - # Example Request: - # - # DELETE /project/:id/services/gitlab-ci - # - delete ':id/services/:service_slug' do - if project_service - attrs = service_attributes.inject({}) do |hash, key| - hash.merge!(key => nil) - end + desc "Delete a service for project" + params do + requires :service_slug, type: String, values: services.keys, desc: 'The name of the service' + end + delete ":id/services/:service_slug" do + service = user_project.find_or_initialize_service(params[:service_slug].underscore) - if project_service.update_attributes(attrs.merge(active: false)) - true - else - not_found! - end + attrs = service_attributes(service).inject({}) do |hash, key| + hash.merge!(key => nil) + end + + if service.update_attributes(attrs.merge(active: false)) + true + else + render_api_error!('400 Bad Request', 400) end end - # Get <service_slug> service settings for project - # - # Example Request: - # - # GET /project/:id/services/gitlab-ci - # - get ':id/services/:service_slug' do - present project_service, with: Entities::ProjectService, include_passwords: current_user.is_admin? + desc 'Get the service settings for project' do + success Entities::ProjectService + end + params do + requires :service_slug, type: String, values: services.keys, desc: 'The name of the service' + end + get ":id/services/:service_slug" do + service = user_project.find_or_initialize_service(params[:service_slug].underscore) + present service, with: Entities::ProjectService, include_passwords: current_user.is_admin? end end - resource :projects do - desc 'Trigger a slash command' do - detail 'Added in GitLab 8.13' + trigger_services.each do |service_slug, settings| + params do + requires :id, type: String, desc: 'The ID of a project' end - post ':id/services/:service_slug/trigger' do - project = find_project(params[:id]) + resource :projects do + desc "Trigger a slash command for #{service_slug}" do + detail 'Added in GitLab 8.13' + end + params do + settings.each do |setting| + requires setting[:name], type: setting[:type], desc: setting[:desc] + end + end + post ":id/services/#{service_slug.underscore}/trigger" do + project = find_project(params[:id]) - # This is not accurate, but done to prevent leakage of the project names - not_found!('Service') unless project + # This is not accurate, but done to prevent leakage of the project names + not_found!('Service') unless project - service = project_service(project) + service = project.find_or_initialize_service(service_slug.underscore) - result = service.try(:active?) && service.try(:trigger, params) + result = service.try(:active?) && service.try(:trigger, params) - if result - status result[:status] || 200 - present result - else - not_found!('Service') + if result + status result[:status] || 200 + present result + else + not_found!('Service') + end end end end -- cgit v1.2.1 From 3e0b8e000f088c24493e993139f8af1be2e14de1 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> Date: Fri, 9 Dec 2016 17:27:11 +0200 Subject: Rename Routable.where_paths_in to Routable.where_full_path_in Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> --- lib/banzai/filter/abstract_reference_filter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb index d904a8bd4ae..fd74eeaebe7 100644 --- a/lib/banzai/filter/abstract_reference_filter.rb +++ b/lib/banzai/filter/abstract_reference_filter.rb @@ -248,7 +248,7 @@ module Banzai end def projects_relation_for_paths(paths) - Project.where_paths_in(paths).includes(:namespace) + Project.where_full_path_in(paths).includes(:namespace) end # Returns projects for the given paths. -- cgit v1.2.1 From 81a12c10fe7ba6f58ab4d1ae57861aa8899d545f Mon Sep 17 00:00:00 2001 From: Robert Schilling <rschilling@student.tugraz.at> Date: Mon, 12 Dec 2016 09:55:29 +0100 Subject: API: Fix groups filter --- lib/api/groups.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/api/groups.rb b/lib/api/groups.rb index fbf7513302b..105d3ee342e 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -1,7 +1,7 @@ module API class Groups < Grape::API include PaginationParams - + before { authenticate! } helpers do @@ -117,11 +117,20 @@ module API success Entities::Project end 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' + 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' use :pagination end get ":id/projects" do group = find_group!(params[:id]) projects = GroupProjectsFinder.new(group).execute(current_user) + projects = filter_projects(projects) present paginate(projects), with: Entities::Project, user: current_user end -- cgit v1.2.1 From 2f45d3bcf0f28d4cd4124b4c9722edc1d3085201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= <remy@rymai.me> Date: Fri, 9 Dec 2016 18:48:20 +0100 Subject: API: Memoize the current_user so that the sudo can work properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The issue was arising when `#current_user` was called a second time after a user was impersonated: the `User#is_admin?` check would be performed on it and it would fail. Signed-off-by: Rémy Coutable <remy@rymai.me> --- lib/api/helpers.rb | 131 +++++++++++++++++++++++++++++++---------------------- lib/api/users.rb | 2 +- 2 files changed, 79 insertions(+), 54 deletions(-) (limited to 'lib') diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 8b0f8deadfa..2041f0dac6b 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -7,67 +7,23 @@ module API SUDO_HEADER = "HTTP_SUDO" SUDO_PARAM = :sudo - def private_token - params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER] - end - - def warden - env['warden'] - end - - # Check the Rails session for valid authentication details - # - # Until CSRF protection is added to the API, disallow this method for - # state-changing endpoints - def find_user_from_warden - warden.try(:authenticate) if %w[GET HEAD].include?(env['REQUEST_METHOD']) - end - def declared_params(options = {}) options = { include_parent_namespaces: false }.merge(options) declared(params, options).to_h.symbolize_keys end - def find_user_by_private_token - token = private_token - return nil unless token.present? - - User.find_by_authentication_token(token) || User.find_by_personal_access_token(token) - end - def current_user - @current_user ||= find_user_by_private_token - @current_user ||= doorkeeper_guard - @current_user ||= find_user_from_warden - - unless @current_user && Gitlab::UserAccess.new(@current_user).allowed? - return nil - end - - identifier = sudo_identifier + return @current_user if defined?(@current_user) - if identifier - # We check for private_token because we cannot allow PAT to be used - forbidden!('Must be admin to use sudo') unless @current_user.is_admin? - forbidden!('Private token must be specified in order to use sudo') unless private_token_used? + @current_user = initial_current_user - @impersonator = @current_user - @current_user = User.by_username_or_id(identifier) - not_found!("No user id or username for: #{identifier}") if @current_user.nil? - end + sudo! @current_user end - def sudo_identifier - identifier ||= params[SUDO_PARAM] || env[SUDO_HEADER] - - # Regex for integers - if !!(identifier =~ /\A[0-9]+\z/) - identifier.to_i - else - identifier - end + def sudo? + initial_current_user != current_user end def user_project @@ -354,6 +310,79 @@ module API private + def private_token + params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER] + end + + def warden + env['warden'] + end + + # Check the Rails session for valid authentication details + # + # Until CSRF protection is added to the API, disallow this method for + # state-changing endpoints + def find_user_from_warden + warden.try(:authenticate) if %w[GET HEAD].include?(env['REQUEST_METHOD']) + end + + def find_user_by_private_token + token = private_token + return nil unless token.present? + + User.find_by_authentication_token(token) || User.find_by_personal_access_token(token) + end + + def initial_current_user + return @initial_current_user if defined?(@initial_current_user) + + @initial_current_user ||= find_user_by_private_token + @initial_current_user ||= doorkeeper_guard + @initial_current_user ||= find_user_from_warden + + unless @initial_current_user && Gitlab::UserAccess.new(@initial_current_user).allowed? + @initial_current_user = nil + end + + @initial_current_user + end + + def sudo! + return unless sudo_identifier + return unless initial_current_user.is_a?(User) + + unless initial_current_user.is_admin? + forbidden!('Must be admin to use sudo') + end + + # Only private tokens should be used for the SUDO feature + unless private_token == initial_current_user.private_token + forbidden!('Private token must be specified in order to use sudo') + end + + sudoed_user = User.by_username_or_id(sudo_identifier) + + if sudoed_user + @current_user = sudoed_user + else + not_found!("No user id or username for: #{sudo_identifier}") + end + end + + def sudo_identifier + return @sudo_identifier if defined?(@sudo_identifier) + + identifier ||= params[SUDO_PARAM] || env[SUDO_HEADER] + + # Regex for integers + @sudo_identifier = + if !!(identifier =~ /\A[0-9]+\z/) + identifier.to_i + else + identifier + end + end + def add_pagination_headers(paginated_data) header 'X-Total', paginated_data.total_count.to_s header 'X-Total-Pages', paginated_data.total_pages.to_s @@ -386,10 +415,6 @@ module API links.join(', ') end - def private_token_used? - private_token == @current_user.private_token - end - def secret_token Gitlab::Shell.secret_token end diff --git a/lib/api/users.rb b/lib/api/users.rb index 1dab799dd61..c7db2d71017 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -353,7 +353,7 @@ module API success Entities::UserPublic end get do - present current_user, with: @impersonator ? Entities::UserWithPrivateToken : Entities::UserPublic + present current_user, with: sudo? ? Entities::UserWithPrivateToken : Entities::UserPublic end desc "Get the currently authenticated user's SSH keys" do -- cgit v1.2.1 From 17e3d3fde8c9a83f58d797c5f62f36b59eedd870 Mon Sep 17 00:00:00 2001 From: winniehell <git@winniehell.de> Date: Tue, 6 Dec 2016 01:29:43 +0100 Subject: Avoid escaping relative links in Markdown twice (!7940) --- lib/banzai/filter/relative_link_filter.rb | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb index f09d78be0ce..9e23c8f8c55 100644 --- a/lib/banzai/filter/relative_link_filter.rb +++ b/lib/banzai/filter/relative_link_filter.rb @@ -46,7 +46,7 @@ module Banzai end def rebuild_relative_uri(uri) - file_path = relative_file_path(uri.path) + file_path = relative_file_path(uri) uri.path = [ relative_url_root, @@ -59,8 +59,10 @@ module Banzai uri end - def relative_file_path(path) - nested_path = build_relative_path(path, context[:requested_path]) + def relative_file_path(uri) + path = Addressable::URI.unescape(uri.path) + request_path = Addressable::URI.unescape(context[:requested_path]) + nested_path = build_relative_path(path, request_path) file_exists?(nested_path) ? nested_path : path end @@ -108,11 +110,7 @@ module Banzai end def uri_type(path) - @uri_types[path] ||= begin - unescaped_path = Addressable::URI.unescape(path) - - current_commit.uri_type(unescaped_path) - end + @uri_types[path] ||= current_commit.uri_type(path) end def current_commit -- cgit v1.2.1 From 2cbd07e69c647726cfb927bf35a594850d4f22fa Mon Sep 17 00:00:00 2001 From: Robert Schilling <rschilling@student.tugraz.at> Date: Wed, 30 Nov 2016 17:12:43 +0100 Subject: Don't allow blank MR titles in API --- lib/api/merge_requests.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 55bdbc6a47c..5d1fe22f2df 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -143,8 +143,8 @@ module API success Entities::MergeRequest end params do - optional :title, type: String, desc: 'The title of the merge request' - optional :target_branch, type: String, desc: 'The target branch' + optional :title, type: String, allow_blank: false, desc: 'The title of the merge request' + optional :target_branch, type: String, allow_blank: false, desc: 'The target branch' optional :state_event, type: String, values: %w[close reopen merge], desc: 'Status of the merge request' use :optional_params -- cgit v1.2.1 From 61aa90ef2089c9d840b88f14a553ef0dd49b779f Mon Sep 17 00:00:00 2001 From: winniehell <git@winniehell.de> Date: Thu, 8 Dec 2016 22:53:15 +0100 Subject: Allow all alphanumeric characters in file names (!8002) --- lib/gitlab/regex.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb index a06cf6a989c..d9d1e3cccca 100644 --- a/lib/gitlab/regex.rb +++ b/lib/gitlab/regex.rb @@ -61,7 +61,7 @@ module Gitlab end def file_name_regex - @file_name_regex ||= /\A[a-zA-Z0-9_\-\.\@]*\z/.freeze + @file_name_regex ||= /\A[[[:alnum:]]_\-\.\@]*\z/.freeze end def file_name_regex_message @@ -69,7 +69,7 @@ module Gitlab end def file_path_regex - @file_path_regex ||= /\A[a-zA-Z0-9_\-\.\/\@]*\z/.freeze + @file_path_regex ||= /\A[[[:alnum:]]_\-\.\/\@]*\z/.freeze end def file_path_regex_message -- cgit v1.2.1 From ec25abe6255a1cc0a4c68192894cc7ac1cb90f54 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> Date: Tue, 13 Dec 2016 14:48:02 +0200 Subject: Add AddLowerPathIndexToRoutes to setup_postgresql.rake Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> --- lib/tasks/migrate/setup_postgresql.rake | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib') diff --git a/lib/tasks/migrate/setup_postgresql.rake b/lib/tasks/migrate/setup_postgresql.rake index 141a0b74ec0..f5caca3ddbf 100644 --- a/lib/tasks/migrate/setup_postgresql.rake +++ b/lib/tasks/migrate/setup_postgresql.rake @@ -1,8 +1,12 @@ +require Rails.root.join('lib/gitlab/database') +require Rails.root.join('lib/gitlab/database/migration_helpers') require Rails.root.join('db/migrate/20151007120511_namespaces_projects_path_lower_indexes') require Rails.root.join('db/migrate/20151008110232_add_users_lower_username_email_indexes') +require Rails.root.join('db/migrate/20161212142807_add_lower_path_index_to_routes') desc 'GitLab | Sets up PostgreSQL' task setup_postgresql: :environment do NamespacesProjectsPathLowerIndexes.new.up AddUsersLowerUsernameEmailIndexes.new.up + AddLowerPathIndexToRoutes.new.up end -- cgit v1.2.1 From 7841be243e5f28828cea95500b554d7f5cc039eb Mon Sep 17 00:00:00 2001 From: Robert Schilling <rschilling@student.tugraz.at> Date: Tue, 13 Dec 2016 13:51:30 +0100 Subject: API: Ability to get group's project in simple representation --- lib/api/groups.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 105d3ee342e..9b9d3df7435 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -125,13 +125,16 @@ module API 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' + optional :simple, type: Boolean, default: false, + desc: 'Return only the ID, URL, name, and path of each project' use :pagination end get ":id/projects" do group = find_group!(params[:id]) projects = GroupProjectsFinder.new(group).execute(current_user) projects = filter_projects(projects) - present paginate(projects), with: Entities::Project, user: current_user + entity = params[:simple] ? Entities::BasicProjectDetails : Entities::Project + present paginate(projects), with: entity, user: current_user end desc 'Transfer a project to the group namespace. Available only for admin.' do -- cgit v1.2.1 From d95b709a66a5597dced25a2b9df9a1e24fc6d49a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= <remy@rymai.me> Date: Tue, 13 Dec 2016 15:53:00 +0100 Subject: Be smarter when finding a sudoed user in API::Helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable <remy@rymai.me> --- lib/api/helpers.rb | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 2041f0dac6b..8260fcab80a 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -34,6 +34,14 @@ module API @available_labels ||= LabelsFinder.new(current_user, project_id: user_project.id).execute end + def find_user(id) + if id =~ /^\d+$/ + User.find_by(id: id) + else + User.find_by(username: id) + end + end + def find_project(id) if id =~ /^\d+$/ Project.find_by(id: id) @@ -349,7 +357,7 @@ module API def sudo! return unless sudo_identifier - return unless initial_current_user.is_a?(User) + return unless initial_current_user unless initial_current_user.is_admin? forbidden!('Must be admin to use sudo') @@ -360,7 +368,7 @@ module API forbidden!('Private token must be specified in order to use sudo') end - sudoed_user = User.by_username_or_id(sudo_identifier) + sudoed_user = find_user(sudo_identifier) if sudoed_user @current_user = sudoed_user @@ -370,17 +378,7 @@ module API end def sudo_identifier - return @sudo_identifier if defined?(@sudo_identifier) - - identifier ||= params[SUDO_PARAM] || env[SUDO_HEADER] - - # Regex for integers - @sudo_identifier = - if !!(identifier =~ /\A[0-9]+\z/) - identifier.to_i - else - identifier - end + @sudo_identifier ||= params[SUDO_PARAM] || env[SUDO_HEADER] end def add_pagination_headers(paginated_data) -- cgit v1.2.1 From 1a81fcfbd8261862c9614f0ea317af02ae20b24a Mon Sep 17 00:00:00 2001 From: Robert Schilling <rschilling@student.tugraz.at> Date: Mon, 12 Dec 2016 13:04:13 +0100 Subject: API: Ability to cherry-pick a commit --- lib/api/commits.rb | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/api/commits.rb b/lib/api/commits.rb index 2670a2d413a..cf2489dbb67 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -1,7 +1,6 @@ require 'mime/types' module API - # Projects commits API class Commits < Grape::API include PaginationParams @@ -121,6 +120,41 @@ module API present paginate(notes), with: Entities::CommitNote end + desc 'Cherry pick commit into a branch' do + detail 'This feature was introduced in GitLab 8.15' + success Entities::RepoCommit + end + params do + requires :sha, type: String, desc: 'A commit sha to be cherry picked' + requires :branch, type: String, desc: 'The name of the branch' + end + post ':id/repository/commits/:sha/cherry_pick' do + authorize! :push_code, user_project + + commit = user_project.commit(params[:sha]) + not_found!('Commit') unless commit + + branch = user_project.repository.find_branch(params[:branch]) + not_found!('Branch') unless branch + + commit_params = { + commit: commit, + create_merge_request: false, + source_project: user_project, + source_branch: commit.cherry_pick_branch_name, + target_branch: params[:branch] + } + + result = ::Commits::CherryPickService.new(user_project, current_user, commit_params).execute + + if result[:status] == :success + branch = user_project.repository.find_branch(params[:branch]) + present user_project.repository.commit(branch.dereferenced_target), with: Entities::RepoCommit + else + render_api_error!(result[:message], 400) + end + end + desc 'Post comment to commit' do success Entities::CommitNote end -- cgit v1.2.1