diff options
author | Lin Jen-Shin <godfat@godfat.org> | 2016-11-17 23:28:58 +0800 |
---|---|---|
committer | Lin Jen-Shin <godfat@godfat.org> | 2016-11-17 23:28:58 +0800 |
commit | a8a879eebc805f27de9eb27fa05bce291ee931ff (patch) | |
tree | bc7054b990df703cbfa1fc4aac77e87be2359fe8 /lib | |
parent | 48090a9188e13e3ddaffb5957a7b5a264024f060 (diff) | |
parent | aea8baed3093c513560e9ac5ac0c5c99508d3001 (diff) | |
download | gitlab-ce-a8a879eebc805f27de9eb27fa05bce291ee931ff.tar.gz |
Merge remote-tracking branch 'upstream/master' into feature/1376-allow-write-access-deploy-keys
* upstream/master: (236 commits)
Mention Git strategy none
Remove ToC since it's now supported in the docs portal itself
Add 8.14 to versions with further additions to review apps
Add Limitations sections to environments and review apps docs
Add link to environments docs
Fix URL to review apps docs
Add a prerequisites section, add some links
Link to NGINX example project for the time being
Get rid most of the irrelevant sections
Add note about current limitation in $CI_BUILD_REF_NAME
Add an intro and an Overview section for Review Apps
WIP review apps
Add Review apps link to CI README
Add stop environment permissions and remove delete
Add note about auto-stopping of environments
Update CHANGELOG.md for 8.13.6
Finish "Stopping envs" and "Grouping similar envs" sections
Highlight first user autocomplete option
Remove ToC and fix headings in Markdown docs
Revert "Merge branch '22680-unlabel-limit-autocomplete-to-selected-items' into 'master'"
...
Diffstat (limited to 'lib')
-rw-r--r-- | lib/api/access_requests.rb | 2 | ||||
-rw-r--r-- | lib/api/branches.rb | 12 | ||||
-rw-r--r-- | lib/api/broadcast_messages.rb | 6 | ||||
-rw-r--r-- | lib/api/commits.rb | 2 | ||||
-rw-r--r-- | lib/api/deploy_keys.rb | 2 | ||||
-rw-r--r-- | lib/api/entities.rb | 15 | ||||
-rw-r--r-- | lib/api/environments.rb | 7 | ||||
-rw-r--r-- | lib/api/groups.rb | 156 | ||||
-rw-r--r-- | lib/api/helpers.rb | 22 | ||||
-rw-r--r-- | lib/api/helpers/internal_helpers.rb | 57 | ||||
-rw-r--r-- | lib/api/internal.rb | 38 | ||||
-rw-r--r-- | lib/api/labels.rb | 11 | ||||
-rw-r--r-- | lib/api/members.rb | 2 | ||||
-rw-r--r-- | lib/api/merge_requests.rb | 272 | ||||
-rw-r--r-- | lib/api/milestones.rb | 7 | ||||
-rw-r--r-- | lib/api/notes.rb | 124 | ||||
-rw-r--r-- | lib/api/notification_settings.rb | 7 | ||||
-rw-r--r-- | lib/api/project_hooks.rb | 10 | ||||
-rw-r--r-- | lib/api/repositories.rb | 4 | ||||
-rw-r--r-- | lib/api/runners.rb | 4 | ||||
-rw-r--r-- | lib/api/subscriptions.rb | 37 | ||||
-rw-r--r-- | lib/api/system_hooks.rb | 2 | ||||
-rw-r--r-- | lib/api/tags.rb | 3 | ||||
-rw-r--r-- | lib/api/triggers.rb | 2 | ||||
-rw-r--r-- | lib/api/users.rb | 2 | ||||
-rw-r--r-- | lib/ci/gitlab_ci_yaml_processor.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/ci/config.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/artifacts.rb (renamed from lib/gitlab/ci/config/node/artifacts.rb) | 4 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/attributable.rb (renamed from lib/gitlab/ci/config/node/attributable.rb) | 2 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/boolean.rb (renamed from lib/gitlab/ci/config/node/boolean.rb) | 4 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/cache.rb (renamed from lib/gitlab/ci/config/node/cache.rb) | 10 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/commands.rb (renamed from lib/gitlab/ci/config/node/commands.rb) | 4 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/configurable.rb (renamed from lib/gitlab/ci/config/node/configurable.rb) | 6 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/environment.rb (renamed from lib/gitlab/ci/config/node/environment.rb) | 4 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/factory.rb (renamed from lib/gitlab/ci/config/node/factory.rb) | 30 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/global.rb (renamed from lib/gitlab/ci/config/node/global.rb) | 24 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/hidden.rb (renamed from lib/gitlab/ci/config/node/hidden.rb) | 6 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/image.rb (renamed from lib/gitlab/ci/config/node/image.rb) | 4 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/job.rb (renamed from lib/gitlab/ci/config/node/job.rb) | 30 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/jobs.rb (renamed from lib/gitlab/ci/config/node/jobs.rb) | 8 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/key.rb (renamed from lib/gitlab/ci/config/node/key.rb) | 4 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/legacy_validation_helpers.rb (renamed from lib/gitlab/ci/config/node/legacy_validation_helpers.rb) | 2 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/node.rb (renamed from lib/gitlab/ci/config/node/entry.rb) | 6 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/paths.rb (renamed from lib/gitlab/ci/config/node/paths.rb) | 4 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/script.rb (renamed from lib/gitlab/ci/config/node/script.rb) | 4 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/services.rb (renamed from lib/gitlab/ci/config/node/services.rb) | 4 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/stage.rb (renamed from lib/gitlab/ci/config/node/stage.rb) | 4 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/stages.rb (renamed from lib/gitlab/ci/config/node/stages.rb) | 4 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/trigger.rb (renamed from lib/gitlab/ci/config/node/trigger.rb) | 4 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/undefined.rb (renamed from lib/gitlab/ci/config/node/undefined.rb) | 8 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/unspecified.rb (renamed from lib/gitlab/ci/config/node/unspecified.rb) | 4 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/validatable.rb (renamed from lib/gitlab/ci/config/node/validatable.rb) | 4 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/validator.rb (renamed from lib/gitlab/ci/config/node/validator.rb) | 12 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/validators.rb (renamed from lib/gitlab/ci/config/node/validators.rb) | 2 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/variables.rb (renamed from lib/gitlab/ci/config/node/variables.rb) | 4 | ||||
-rw-r--r-- | lib/gitlab/current_settings.rb | 5 | ||||
-rw-r--r-- | lib/gitlab/diff/file.rb | 15 | ||||
-rw-r--r-- | lib/gitlab/diff/file_collection/merge_request_diff.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/ldap/adapter.rb | 4 | ||||
-rw-r--r-- | lib/gitlab/ldap/authentication.rb | 6 | ||||
-rw-r--r-- | lib/gitlab/ldap/config.rb | 65 | ||||
-rw-r--r-- | lib/gitlab/mail_room.rb | 1 | ||||
-rw-r--r-- | lib/gitlab/o_auth/user.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/project_search_results.rb | 51 | ||||
-rw-r--r-- | lib/gitlab/regex.rb | 6 | ||||
-rw-r--r-- | lib/gitlab/sidekiq_throttler.rb | 23 |
66 files changed, 631 insertions, 565 deletions
diff --git a/lib/api/access_requests.rb b/lib/api/access_requests.rb index 87915b19480..ed723b94cfd 100644 --- a/lib/api/access_requests.rb +++ b/lib/api/access_requests.rb @@ -48,7 +48,7 @@ module API put ':id/access_requests/:user_id/approve' do source = find_source(source_type, params[:id]) - member = ::Members::ApproveAccessRequestService.new(source, current_user, declared(params)).execute + member = ::Members::ApproveAccessRequestService.new(source, current_user, declared_params).execute status :created present member.user, with: Entities::Member, member: member diff --git a/lib/api/branches.rb b/lib/api/branches.rb index 21a106387f0..73aed624ea7 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -128,6 +128,18 @@ module API render_api_error!(result[:message], result[:return_code]) end end + + # Delete all merged branches + # + # Parameters: + # id (required) - The ID of a project + # Example Request: + # DELETE /projects/:id/repository/branches/delete_merged + delete ":id/repository/merged_branches" do + DeleteMergedBranchesService.new(user_project, current_user).async_execute + + status(200) + end end end end diff --git a/lib/api/broadcast_messages.rb b/lib/api/broadcast_messages.rb index fb2a4148011..b6281a7f0ac 100644 --- a/lib/api/broadcast_messages.rb +++ b/lib/api/broadcast_messages.rb @@ -36,8 +36,7 @@ module API optional :font, type: String, desc: 'Foreground color' end post do - create_params = declared(params, include_missing: false).to_h - message = BroadcastMessage.create(create_params) + message = BroadcastMessage.create(declared_params(include_missing: false)) if message.persisted? present message, with: Entities::BroadcastMessage @@ -73,9 +72,8 @@ module API end put ':id' do message = find_message - update_params = declared(params, include_missing: false).to_h - if message.update(update_params) + if message.update(declared_params(include_missing: false)) present message, with: Entities::BroadcastMessage else render_validation_error!(message) diff --git a/lib/api/commits.rb b/lib/api/commits.rb index 2f2cf769481..f412e1da1bf 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -53,7 +53,7 @@ module API post ":id/repository/commits" do authorize! :push_code, user_project - attrs = declared(params) + attrs = declared_params attrs[:source_branch] = attrs[:branch_name] attrs[:target_branch] = attrs[:branch_name] attrs[:actions].map! do |action| diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb index 425df2c176a..85360730841 100644 --- a/lib/api/deploy_keys.rb +++ b/lib/api/deploy_keys.rb @@ -82,7 +82,7 @@ module API end post ":id/#{path}/:key_id/enable" do key = ::Projects::EnableDeployKeyService.new(user_project, - current_user, declared(params)).execute + current_user, declared_params).execute if key present key, with: Entities::SSHKey diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 147aaf06b18..54c35d21b0b 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -159,7 +159,7 @@ module API end class RepoTreeObject < Grape::Entity - expose :id, :name, :type + expose :id, :name, :type, :path expose :mode do |obj, options| filemode = obj.mode.to_s(8) @@ -437,7 +437,18 @@ module API end class Label < LabelBasic - expose :open_issues_count, :closed_issues_count, :open_merge_requests_count + expose :open_issues_count do |label, options| + label.open_issues_count(options[:current_user]) + end + + expose :closed_issues_count do |label, options| + label.closed_issues_count(options[:current_user]) + end + + expose :open_merge_requests_count do |label, options| + label.open_merge_requests_count(options[:current_user]) + end + expose :priority do |label, options| label.priority(options[:project]) end diff --git a/lib/api/environments.rb b/lib/api/environments.rb index 819f80d8365..00c901937b1 100644 --- a/lib/api/environments.rb +++ b/lib/api/environments.rb @@ -32,8 +32,7 @@ module API post ':id/environments' do authorize! :create_environment, user_project - create_params = declared(params, include_parent_namespaces: false).to_h - environment = user_project.environments.create(create_params) + environment = user_project.environments.create(declared_params) if environment.persisted? present environment, with: Entities::Environment @@ -55,8 +54,8 @@ module API authorize! :update_environment, user_project environment = user_project.environments.find(params[:environment_id]) - - update_params = declared(params, include_missing: false).extract!(:name, :external_url).to_h + + update_params = declared_params(include_missing: false).extract!(:name, :external_url) if environment.update(update_params) present environment, with: Entities::Environment else diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 40644fc2adf..3f57b9ab5bc 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -1,118 +1,111 @@ module API - # groups API class Groups < Grape::API before { authenticate! } + helpers do + params :optional_params do + optional :description, type: String, desc: 'The description of the group' + optional :visibility_level, type: Integer, desc: 'The visibility level of the group' + optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group' + optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access' + end + end + resource :groups do - # Get a groups list - # - # Parameters: - # skip_groups (optional) - Array of group ids to exclude from list - # all_available (optional, boolean) - Show all group that you have access to - # Example Request: - # GET /groups + desc 'Get a groups list' do + success Entities::Group + end + params do + optional :skip_groups, type: Array[Integer], desc: 'Array of group ids to exclude from list' + optional :all_available, type: Boolean, desc: 'Show all group that you have access to' + optional :search, type: String, desc: 'Search for a specific group' + end get do - @groups = if current_user.admin - Group.all - elsif params[:all_available] - GroupsFinder.new.execute(current_user) - else - current_user.groups - end + groups = if current_user.admin + Group.all + elsif params[:all_available] + GroupsFinder.new.execute(current_user) + else + current_user.groups + end - @groups = @groups.search(params[:search]) if params[:search].present? - @groups = @groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present? - @groups = paginate @groups - present @groups, with: Entities::Group + groups = groups.search(params[:search]) if params[:search].present? + groups = groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present? + present paginate(groups), with: Entities::Group end - # Get list of owned groups for authenticated user - # - # Example Request: - # GET /groups/owned + desc 'Get list of owned groups for authenticated user' do + success Entities::Group + end get '/owned' do - @groups = current_user.owned_groups - @groups = paginate @groups - present @groups, with: Entities::Group, user: current_user + groups = current_user.owned_groups + present paginate(groups), with: Entities::Group, user: current_user end - # Create group. Available only for users who can create groups. - # - # Parameters: - # name (required) - The name of the group - # path (required) - The path of the group - # description (optional) - The description of the group - # visibility_level (optional) - The visibility level of the group - # lfs_enabled (optional) - Enable/disable LFS for the projects in this group - # request_access_enabled (optional) - Allow users to request member access - # Example Request: - # POST /groups + desc 'Create a group. Available only for users who can create groups.' do + success Entities::Group + end + params do + requires :name, type: String, desc: 'The name of the group' + requires :path, type: String, desc: 'The path of the group' + use :optional_params + end post do authorize! :create_group - required_attributes! [:name, :path] - attrs = attributes_for_keys [:name, :path, :description, :visibility_level, :lfs_enabled, :request_access_enabled] - @group = Group.new(attrs) + group = ::Groups::CreateService.new(current_user, declared_params(include_missing: false)).execute - if @group.save - @group.add_owner(current_user) - present @group, with: Entities::Group + if group.persisted? + present group, with: Entities::Group else - render_api_error!("Failed to save group #{@group.errors.messages}", 400) + render_api_error!("Failed to save group #{group.errors.messages}", 400) end end + end - # Update group. Available only for users who can administrate groups. - # - # Parameters: - # id (required) - The ID of a group - # path (optional) - The path of the group - # description (optional) - The description of the group - # visibility_level (optional) - The visibility level of the group - # lfs_enabled (optional) - Enable/disable LFS for the projects in this group - # request_access_enabled (optional) - Allow users to request member access - # Example Request: - # PUT /groups/:id + params do + requires :id, type: String, desc: 'The ID of a group' + end + resource :groups do + desc 'Update a group. Available only for users who can administrate groups.' do + success Entities::Group + end + params do + optional :name, type: String, desc: 'The name of the group' + optional :path, type: String, desc: 'The path of the group' + use :optional_params + at_least_one_of :name, :path, :description, :visibility_level, + :lfs_enabled, :request_access_enabled + end put ':id' do group = find_group(params[:id]) authorize! :admin_group, group - attrs = attributes_for_keys [:name, :path, :description, :visibility_level, :lfs_enabled, :request_access_enabled] - - if ::Groups::UpdateService.new(group, current_user, attrs).execute + if ::Groups::UpdateService.new(group, current_user, declared_params(include_missing: false)).execute present group, with: Entities::GroupDetail else render_validation_error!(group) end end - # Get a single group, with containing projects - # - # Parameters: - # id (required) - The ID of a group - # Example Request: - # GET /groups/:id + desc 'Get a single group, with containing projects.' do + success Entities::GroupDetail + end get ":id" do group = find_group(params[:id]) present group, with: Entities::GroupDetail end - # Remove group - # - # Parameters: - # id (required) - The ID of a group - # Example Request: - # DELETE /groups/:id + desc 'Remove a group.' delete ":id" do group = find_group(params[:id]) authorize! :admin_group, group DestroyGroupService.new(group, current_user).execute end - # Get a list of projects in this group - # - # Example Request: - # GET /groups/:id/projects + desc 'Get a list of projects in this group.' do + success Entities::Project + end get ":id/projects" do group = find_group(params[:id]) projects = GroupProjectsFinder.new(group).execute(current_user) @@ -120,13 +113,12 @@ module API present projects, with: Entities::Project, user: current_user end - # Transfer a project to the Group namespace - # - # Parameters: - # id - group id - # project_id - project id - # Example Request: - # POST /groups/:id/projects/:project_id + desc 'Transfer a project to the group namespace. Available only for admin.' do + success Entities::GroupDetail + end + params do + requires :project_id, type: String, desc: 'The ID of the project' + end post ":id/projects/:project_id" do authenticated_as_admin! group = Group.find_by(id: params[:id]) @@ -134,7 +126,7 @@ module API result = ::Projects::TransferService.new(project, current_user).execute(group) if result - present group + present group, with: Entities::GroupDetail else render_api_error!("Failed to transfer project #{project.errors.messages}", 400) end diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 3c9d7b1aaef..84cc9200d1b 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -23,6 +23,11 @@ module API 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? @@ -81,25 +86,10 @@ module API end def project_service - @project_service ||= begin - underscored_service = params[:service_slug].underscore - - if Service.available_services_names.include?(underscored_service) - user_project.build_missing_services - - service_method = "#{underscored_service}_service" - - send_service(service_method) - end - end - + @project_service ||= user_project.find_or_initialize_service(params[:service_slug].underscore) @project_service || not_found!("Service") end - def send_service(service_method) - user_project.send(service_method) - end - def service_attributes @service_attributes ||= project_service.fields.inject([]) do |arr, hash| arr << hash[:name].to_sym diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb new file mode 100644 index 00000000000..eb223c1101d --- /dev/null +++ b/lib/api/helpers/internal_helpers.rb @@ -0,0 +1,57 @@ +module API + module Helpers + module InternalHelpers + # Project paths may be any of the following: + # * /repository/storage/path/namespace/project + # * /namespace/project + # * namespace/project + # + # In addition, they may have a '.git' extension and multiple namespaces + # + # Transform all these cases to 'namespace/project' + def clean_project_path(project_path, storage_paths = Repository.storages.values) + project_path = project_path.sub(/\.git\z/, '') + + storage_paths.each do |storage_path| + storage_path = File.expand_path(storage_path) + + if project_path.start_with?(storage_path) + project_path = project_path.sub(storage_path, '') + break + end + end + + project_path.sub(/\A\//, '') + end + + def project_path + @project_path ||= clean_project_path(params[:project]) + end + + def wiki? + @wiki ||= project_path.end_with?('.wiki') && + !Project.find_with_namespace(project_path) + end + + def project + @project ||= begin + # Check for *.wiki repositories. + # Strip out the .wiki from the pathname before finding the + # project. This applies the correct project permissions to + # the wiki repository as well. + project_path.chomp!('.wiki') if wiki? + + Project.find_with_namespace(project_path) + end + end + + def ssh_authentication_abilities + [ + :read_project, + :download_code, + :push_code + ] + end + end + end +end diff --git a/lib/api/internal.rb b/lib/api/internal.rb index ccf181402f9..7087ce11401 100644 --- a/lib/api/internal.rb +++ b/lib/api/internal.rb @@ -3,6 +3,8 @@ module API class Internal < Grape::API before { authenticate_by_gitlab_shell_token! } + helpers ::API::Helpers::InternalHelpers + namespace 'internal' do # Check if git command is allowed to project # @@ -14,42 +16,6 @@ module API # ref - branch name # forced_push - forced_push # protocol - Git access protocol being used, e.g. HTTP or SSH - # - - helpers do - def project_path - @project_path ||= begin - project_path = params[:project].sub(/\.git\z/, '') - Repository.remove_storage_from_path(project_path) - end - end - - def wiki? - @wiki ||= project_path.end_with?('.wiki') && - !Project.find_with_namespace(project_path) - end - - def project - @project ||= begin - # Check for *.wiki repositories. - # Strip out the .wiki from the pathname before finding the - # project. This applies the correct project permissions to - # the wiki repository as well. - project_path.chomp!('.wiki') if wiki? - - Project.find_with_namespace(project_path) - end - end - - def ssh_authentication_abilities - [ - :read_project, - :download_code, - :push_code - ] - end - end - post "/allowed" do status 200 diff --git a/lib/api/labels.rb b/lib/api/labels.rb index 97218054f37..652786d4e3e 100644 --- a/lib/api/labels.rb +++ b/lib/api/labels.rb @@ -30,10 +30,7 @@ module API conflict!('Label already exists') if label priority = params.delete(:priority) - label_params = declared(params, - include_parent_namespaces: false, - include_missing: false).to_h - label = user_project.labels.create(label_params) + label = user_project.labels.create(declared_params(include_missing: false)) if label.valid? label.prioritize!(user_project, priority) if priority @@ -77,11 +74,9 @@ module API update_priority = params.key?(:priority) priority = params.delete(:priority) - label_params = declared(params, - include_parent_namespaces: false, - include_missing: false).to_h + label_params = declared_params(include_missing: false) # Rename new name to the actual label attribute name - label_params[:name] = label_params.delete('new_name') if label_params.key?('new_name') + label_params[:name] = label_params.delete(:new_name) if label_params.key?(:new_name) render_validation_error!(label) unless label.update(label_params) diff --git a/lib/api/members.rb b/lib/api/members.rb index b80818f0eb6..2d4d5cedf20 100644 --- a/lib/api/members.rb +++ b/lib/api/members.rb @@ -120,7 +120,7 @@ module API if member.nil? { message: "Access revoked", id: params[:user_id].to_i } else - ::Members::DestroyService.new(source, current_user, declared(params)).execute + ::Members::DestroyService.new(source, current_user, declared_params).execute present member.user, with: Entities::Member, member: member end diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index bf8504e1101..f9720786e63 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -1,8 +1,12 @@ module API - # MergeRequest API class MergeRequests < Grape::API + DEPRECATION_MESSAGE = 'This endpoint is deprecated and will be removed in GitLab 9.0.'.freeze + before { authenticate! } + params do + requires :id, type: String, desc: 'The ID of a project' + end resource :projects do helpers do def handle_merge_request_errors!(errors) @@ -18,27 +22,27 @@ module API render_api_error!(errors, 400) end + + params :optional_params do + optional :description, type: String, desc: 'The description of the merge request' + 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' + end end - # List merge requests - # - # Parameters: - # id (required) - The ID of a project - # iid (optional) - Return the project MR having the given `iid` - # state (optional) - Return requests "merged", "opened" or "closed" - # 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: - # GET /projects/:id/merge_requests - # GET /projects/:id/merge_requests?state=opened - # GET /projects/:id/merge_requests?state=closed - # GET /projects/:id/merge_requests?order_by=created_at - # GET /projects/:id/merge_requests?order_by=updated_at - # GET /projects/:id/merge_requests?sort=desc - # GET /projects/:id/merge_requests?sort=asc - # GET /projects/:id/merge_requests?iid=42 - # + desc 'List merge requests' do + success Entities::MergeRequest + end + params do + optional :state, type: String, values: %w[opened closed merged all], default: 'all', + desc: 'Return opened, closed, merged, or all merge requests' + optional :order_by, type: String, values: %w[created_at updated_at], default: 'created_at', + desc: 'Return merge requests ordered by `created_at` or `updated_at` fields.' + optional :sort, type: String, values: %w[asc desc], default: 'desc', + desc: 'Return merge requests sorted in `asc` or `desc` order.' + optional :iid, type: Integer, desc: 'The IID of the merge requests' + end get ":id/merge_requests" do authorize! :read_merge_request, user_project merge_requests = user_project.merge_requests.inc_notes_with_associations @@ -48,10 +52,10 @@ module API end merge_requests = - case params["state"] - when "opened" then merge_requests.opened - when "closed" then merge_requests.closed - when "merged" then merge_requests.merged + case params[:state] + when 'opened' then merge_requests.opened + when 'closed' then merge_requests.closed + when 'merged' then merge_requests.merged else merge_requests end @@ -59,36 +63,28 @@ module API present paginate(merge_requests), with: Entities::MergeRequest, current_user: current_user end - # Create MR - # - # Parameters: - # - # id (required) - The ID of a project - this will be the source of the merge request - # source_branch (required) - The source branch - # target_branch (required) - The target branch - # target_project_id - The target project of the merge request defaults to the :id of the project - # assignee_id - Assignee user ID - # title (required) - Title of MR - # description - Description of MR - # labels (optional) - Labels for MR as a comma-separated list - # milestone_id (optional) - Milestone ID - # - # Example: - # POST /projects/:id/merge_requests - # + desc 'Create a merge request' do + success Entities::MergeRequest + end + params do + requires :title, type: String, desc: 'The title of the merge request' + requires :source_branch, type: String, desc: 'The source branch' + requires :target_branch, type: String, desc: 'The target branch' + optional :target_project_id, type: Integer, + desc: 'The target project of the merge request defaults to the :id of the project' + use :optional_params + end post ":id/merge_requests" do authorize! :create_merge_request, user_project - required_attributes! [:source_branch, :target_branch, :title] - attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title, :target_project_id, :description, :milestone_id] + + mr_params = declared_params # Validate label names in advance - if (errors = validate_label_params(params)).any? + if (errors = validate_label_params(mr_params)).any? render_api_error!({ labels: errors }, 400) end - attrs[:labels] = params[:labels] if params[:labels] - - merge_request = ::MergeRequests::CreateService.new(user_project, current_user, attrs).execute + merge_request = ::MergeRequests::CreateService.new(user_project, current_user, mr_params).execute if merge_request.valid? present merge_request, with: Entities::MergeRequest, current_user: current_user @@ -97,11 +93,10 @@ module API end end - # Delete a MR - # - # Parameters: - # id (required) - The ID of the project - # merge_request_id (required) - The MR id + desc 'Delete a merge request' + params do + requires :merge_request_id, type: Integer, desc: 'The ID of a merge request' + end delete ":id/merge_requests/:merge_request_id" do merge_request = user_project.merge_requests.find_by(id: params[:merge_request_id]) @@ -112,89 +107,64 @@ module API # Routing "merge_request/:merge_request_id/..." is DEPRECATED and WILL BE REMOVED in version 9.0 # Use "merge_requests/:merge_request_id/..." instead. # - [":id/merge_request/:merge_request_id", ":id/merge_requests/:merge_request_id"].each do |path| - # Show MR - # - # Parameters: - # id (required) - The ID of a project - # merge_request_id (required) - The ID of MR - # - # Example: - # GET /projects/:id/merge_requests/:merge_request_id - # + params do + requires :merge_request_id, type: Integer, desc: 'The ID of a merge request' + end + { ":id/merge_request/:merge_request_id" => :deprecated, ":id/merge_requests/:merge_request_id" => :ok }.each do |path, status| + desc 'Get a single merge request' do + if status == :deprecated + detail DEPRECATION_MESSAGE + end + success Entities::MergeRequest + end get path do merge_request = user_project.merge_requests.find(params[:merge_request_id]) - authorize! :read_merge_request, merge_request - present merge_request, with: Entities::MergeRequest, current_user: current_user end - # Show MR commits - # - # Parameters: - # id (required) - The ID of a project - # merge_request_id (required) - The ID of MR - # - # Example: - # GET /projects/:id/merge_requests/:merge_request_id/commits - # + desc 'Get the commits of a merge request' do + success Entities::RepoCommit + end get "#{path}/commits" do - merge_request = user_project.merge_requests. - find(params[:merge_request_id]) + merge_request = user_project.merge_requests.find(params[:merge_request_id]) authorize! :read_merge_request, merge_request present merge_request.commits, with: Entities::RepoCommit end - # Show MR changes - # - # Parameters: - # id (required) - The ID of a project - # merge_request_id (required) - The ID of MR - # - # Example: - # GET /projects/:id/merge_requests/:merge_request_id/changes - # + desc 'Show the merge request changes' do + success Entities::MergeRequestChanges + end get "#{path}/changes" do - merge_request = user_project.merge_requests. - find(params[:merge_request_id]) + merge_request = user_project.merge_requests.find(params[:merge_request_id]) authorize! :read_merge_request, merge_request present merge_request, with: Entities::MergeRequestChanges, current_user: current_user end - # Update MR - # - # Parameters: - # id (required) - The ID of a project - # merge_request_id (required) - ID of MR - # target_branch - The target branch - # assignee_id - Assignee user ID - # title - Title of MR - # state_event - Status of MR. (close|reopen|merge) - # description - Description of MR - # labels (optional) - Labels for a MR as a comma-separated list - # milestone_id (optional) - Milestone ID - # Example: - # PUT /projects/:id/merge_requests/:merge_request_id - # + desc 'Update a merge request' do + 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 :state_event, type: String, values: %w[close reopen merge], + desc: 'Status of the merge request' + use :optional_params + at_least_one_of :title, :target_branch, :description, :assignee_id, + :milestone_id, :labels, :state_event + end put path do - attrs = attributes_for_keys [:target_branch, :assignee_id, :title, :state_event, :description, :milestone_id] - merge_request = user_project.merge_requests.find(params[:merge_request_id]) + merge_request = user_project.merge_requests.find(params.delete(:merge_request_id)) authorize! :update_merge_request, merge_request - # Ensure source_branch is not specified - if params[:source_branch].present? - render_api_error!('Source branch cannot be changed', 400) - end + mr_params = declared_params(include_missing: false) # Validate label names in advance - if (errors = validate_label_params(params)).any? + if (errors = validate_label_params(mr_params)).any? render_api_error!({ labels: errors }, 400) end - attrs[:labels] = params[:labels] if params[:labels] - - merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, attrs).execute(merge_request) + merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, mr_params).execute(merge_request) if merge_request.valid? present merge_request, with: Entities::MergeRequest, current_user: current_user @@ -203,18 +173,17 @@ module API end end - # Merge MR - # - # Parameters: - # id (required) - The ID of a project - # merge_request_id (required) - ID of MR - # merge_commit_message (optional) - Custom merge commit message - # should_remove_source_branch (optional) - When true, the source branch will be deleted if possible - # merge_when_build_succeeds (optional) - When true, this MR will be merged when the build succeeds - # sha (optional) - When present, must have the HEAD SHA of the source branch - # Example: - # PUT /projects/:id/merge_requests/:merge_request_id/merge - # + desc 'Merge a merge request' do + success Entities::MergeRequest + end + params do + optional :merge_commit_message, type: String, desc: 'Custom merge commit message' + 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' + optional :sha, type: String, desc: 'When present, must have the HEAD SHA of the source branch' + end put "#{path}/merge" do merge_request = user_project.merge_requests.find(params[:merge_request_id]) @@ -235,7 +204,7 @@ module API should_remove_source_branch: params[:should_remove_source_branch] } - if to_boolean(params[:merge_when_build_succeeds]) && merge_request.pipeline && merge_request.pipeline.active? + 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) else @@ -246,11 +215,9 @@ module API present merge_request, with: Entities::MergeRequest, current_user: current_user end - # Cancel Merge if Merge When build succeeds is enabled - # Parameters: - # id (required) - The ID of a project - # merge_request_id (required) - ID of MR - # + desc 'Cancel merge if "Merge when build succeeds" is enabled' do + success Entities::MergeRequest + end post "#{path}/cancel_merge_when_build_succeeds" do merge_request = user_project.merge_requests.find(params[:merge_request_id]) @@ -259,17 +226,10 @@ module API ::MergeRequest::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user).cancel(merge_request) end - # Duplicate. DEPRECATED and WILL BE REMOVED in 9.0. - # Use GET "/projects/:id/merge_requests/:merge_request_id/notes" instead - # - # Get a merge request's comments - # - # Parameters: - # id (required) - The ID of a project - # merge_request_id (required) - ID of MR - # Examples: - # GET /projects/:id/merge_requests/:merge_request_id/comments - # + desc 'Get the comments of a merge request' do + detail 'Duplicate. DEPRECATED and WILL BE REMOVED in 9.0' + success Entities::MRNote + end get "#{path}/comments" do merge_request = user_project.merge_requests.find(params[:merge_request_id]) @@ -278,23 +238,15 @@ module API present paginate(merge_request.notes.fresh), with: Entities::MRNote end - # Duplicate. DEPRECATED and WILL BE REMOVED in 9.0. - # Use POST "/projects/:id/merge_requests/:merge_request_id/notes" instead - # - # Post comment to merge request - # - # Parameters: - # id (required) - The ID of a project - # merge_request_id (required) - ID of MR - # note (required) - Text of comment - # Examples: - # POST /projects/:id/merge_requests/:merge_request_id/comments - # + desc 'Post a comment to a merge request' do + detail 'Duplicate. DEPRECATED and WILL BE REMOVED in 9.0' + success Entities::MRNote + end + params do + requires :note, type: String, desc: 'The text of the comment' + end post "#{path}/comments" do - required_attributes! [:note] - merge_request = user_project.merge_requests.find(params[:merge_request_id]) - authorize! :create_note, merge_request opts = { @@ -312,13 +264,9 @@ module API end end - # List issues that will close on merge - # - # Parameters: - # id (required) - The ID of a project - # merge_request_id (required) - ID of MR - # Examples: - # GET /projects/:id/merge_requests/:merge_request_id/closes_issues + desc 'List issues that will be closed on merge' do + success Entities::MRNote + 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 8984cf8cdcd..ba4a84275bc 100644 --- a/lib/api/milestones.rb +++ b/lib/api/milestones.rb @@ -62,9 +62,8 @@ module API end post ":id/milestones" do authorize! :admin_milestone, user_project - milestone_params = declared(params, include_parent_namespaces: false) - milestone = ::Milestones::CreateService.new(user_project, current_user, milestone_params).execute + milestone = ::Milestones::CreateService.new(user_project, current_user, declared_params).execute if milestone.valid? present milestone, with: Entities::Milestone @@ -86,9 +85,9 @@ module API end put ":id/milestones/:milestone_id" do authorize! :admin_milestone, user_project - milestone_params = declared(params, include_parent_namespaces: false, include_missing: false) + milestone = user_project.milestones.find(params.delete(:milestone_id)) - milestone = user_project.milestones.find(milestone_params.delete(:milestone_id)) + milestone_params = declared_params(include_missing: false) milestone = ::Milestones::UpdateService.new(user_project, current_user, milestone_params).execute(milestone) if milestone.valid? diff --git a/lib/api/notes.rb b/lib/api/notes.rb index c5c214d4d13..b255b47742b 100644 --- a/lib/api/notes.rb +++ b/lib/api/notes.rb @@ -5,23 +5,23 @@ module API NOTEABLE_TYPES = [Issue, MergeRequest, Snippet] + params do + requires :id, type: String, desc: 'The ID of a project' + end resource :projects do NOTEABLE_TYPES.each do |noteable_type| noteables_str = noteable_type.to_s.underscore.pluralize - noteable_id_str = "#{noteable_type.to_s.underscore}_id" - - # Get a list of project +noteable+ notes - # - # Parameters: - # id (required) - The ID of a project - # noteable_id (required) - The ID of an issue or snippet - # Example Request: - # GET /projects/:id/issues/:noteable_id/notes - # GET /projects/:id/snippets/:noteable_id/notes - get ":id/#{noteables_str}/:#{noteable_id_str}/notes" do - @noteable = user_project.send(noteables_str.to_sym).find(params[noteable_id_str.to_sym]) - - if can?(current_user, noteable_read_ability_name(@noteable), @noteable) + + desc 'Get a list of project +noteable+ notes' do + success Entities::Note + end + params do + requires :noteable_id, type: Integer, desc: 'The ID of the noteable' + end + get ":id/#{noteables_str}/:noteable_id/notes" do + noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id]) + + if can?(current_user, noteable_read_ability_name(noteable), noteable) # We exclude notes that are cross-references and that cannot be viewed # by the current user. By doing this exclusion at this level and not # at the DB query level (which we cannot in that case), the current @@ -31,7 +31,7 @@ module API # paginate() only works with a relation. This could lead to a # mismatch between the pagination headers info and the actual notes # array returned, but this is really a edge-case. - paginate(@noteable.notes). + paginate(noteable.notes). reject { |n| n.cross_reference_not_visible_for?(current_user) } present notes, with: Entities::Note else @@ -39,44 +39,40 @@ module API end end - # Get a single +noteable+ note - # - # Parameters: - # id (required) - The ID of a project - # noteable_id (required) - The ID of an issue or snippet - # note_id (required) - The ID of a note - # Example Request: - # GET /projects/:id/issues/:noteable_id/notes/:note_id - # GET /projects/:id/snippets/:noteable_id/notes/:note_id - get ":id/#{noteables_str}/:#{noteable_id_str}/notes/:note_id" do - @noteable = user_project.send(noteables_str.to_sym).find(params[noteable_id_str.to_sym]) - @note = @noteable.notes.find(params[:note_id]) - can_read_note = can?(current_user, noteable_read_ability_name(@noteable), @noteable) && !@note.cross_reference_not_visible_for?(current_user) + desc 'Get a single +noteable+ note' do + success Entities::Note + end + params do + requires :note_id, type: Integer, desc: 'The ID of a note' + requires :noteable_id, type: Integer, desc: 'The ID of the noteable' + end + get ":id/#{noteables_str}/:noteable_id/notes/:note_id" do + noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id]) + note = noteable.notes.find(params[:note_id]) + can_read_note = can?(current_user, noteable_read_ability_name(noteable), noteable) && !note.cross_reference_not_visible_for?(current_user) if can_read_note - present @note, with: Entities::Note + present note, with: Entities::Note else not_found!("Note") end end - # Create a new +noteable+ note - # - # Parameters: - # id (required) - The ID of a project - # noteable_id (required) - The ID of an issue or snippet - # body (required) - The content of a note - # created_at (optional) - The date - # Example Request: - # POST /projects/:id/issues/:noteable_id/notes - # POST /projects/:id/snippets/:noteable_id/notes - post ":id/#{noteables_str}/:#{noteable_id_str}/notes" do + desc 'Create a new +noteable+ note' do + success Entities::Note + end + params do + requires :noteable_id, type: Integer, desc: 'The ID of the noteable' + requires :body, type: String, desc: 'The content of a note' + optional :created_at, type: String, desc: 'The creation date of the note' + end + post ":id/#{noteables_str}/:noteable_id/notes" do required_attributes! [:body] opts = { note: params[:body], noteable_type: noteables_str.classify, - noteable_id: params[noteable_id_str] + noteable_id: params[:noteable_id] } if params[:created_at] && (current_user.is_admin? || user_project.owner == current_user) @@ -92,19 +88,15 @@ module API end end - # Modify existing +noteable+ note - # - # Parameters: - # id (required) - The ID of a project - # noteable_id (required) - The ID of an issue or snippet - # node_id (required) - The ID of a note - # body (required) - New content of a note - # Example Request: - # PUT /projects/:id/issues/:noteable_id/notes/:note_id - # PUT /projects/:id/snippets/:noteable_id/notes/:node_id - put ":id/#{noteables_str}/:#{noteable_id_str}/notes/:note_id" do - required_attributes! [:body] - + desc 'Update an existing +noteable+ note' do + success Entities::Note + end + params do + requires :noteable_id, type: Integer, desc: 'The ID of the noteable' + requires :note_id, type: Integer, desc: 'The ID of a note' + requires :body, type: String, desc: 'The content of a note' + end + put ":id/#{noteables_str}/:noteable_id/notes/:note_id" do note = user_project.notes.find(params[:note_id]) authorize! :admin_note, note @@ -113,25 +105,23 @@ module API note: params[:body] } - @note = ::Notes::UpdateService.new(user_project, current_user, opts).execute(note) + note = ::Notes::UpdateService.new(user_project, current_user, opts).execute(note) - if @note.valid? - present @note, with: Entities::Note + if note.valid? + present note, with: Entities::Note else render_api_error!("Failed to save note #{note.errors.messages}", 400) end end - # Delete a +noteable+ note - # - # Parameters: - # id (required) - The ID of a project - # noteable_id (required) - The ID of an issue, MR, or snippet - # node_id (required) - The ID of a note - # Example Request: - # DELETE /projects/:id/issues/:noteable_id/notes/:note_id - # DELETE /projects/:id/snippets/:noteable_id/notes/:node_id - delete ":id/#{noteables_str}/:#{noteable_id_str}/notes/:note_id" do + desc 'Delete a +noteable+ note' do + success Entities::Note + end + params do + requires :noteable_id, type: Integer, desc: 'The ID of the noteable' + requires :note_id, type: Integer, desc: 'The ID of a note' + end + delete ":id/#{noteables_str}/:noteable_id/notes/:note_id" do note = user_project.notes.find(params[:note_id]) authorize! :admin_note, note diff --git a/lib/api/notification_settings.rb b/lib/api/notification_settings.rb index a70a7e71073..c5e9b3ad69b 100644 --- a/lib/api/notification_settings.rb +++ b/lib/api/notification_settings.rb @@ -33,10 +33,9 @@ module API begin notification_setting.transaction do new_notification_email = params.delete(:notification_email) - declared_params = declared(params, include_missing: false).to_h current_user.update(notification_email: new_notification_email) if new_notification_email - notification_setting.update(declared_params) + notification_setting.update(declared_params(include_missing: false)) end rescue ArgumentError => e # catch level enum error render_api_error! e.to_s, 400 @@ -81,9 +80,7 @@ module API notification_setting = current_user.notification_settings_for(source) begin - declared_params = declared(params, include_missing: false).to_h - - notification_setting.update(declared_params) + notification_setting.update(declared_params(include_missing: false)) rescue ArgumentError => e # catch level enum error render_api_error! e.to_s, 400 end diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb index eef343c2ac6..2b36ef7c426 100644 --- a/lib/api/project_hooks.rb +++ b/lib/api/project_hooks.rb @@ -51,8 +51,7 @@ module API use :project_hook_properties end post ":id/hooks" do - new_hook_params = declared(params, include_missing: false, include_parent_namespaces: false).to_h - hook = user_project.hooks.new(new_hook_params) + hook = user_project.hooks.new(declared_params(include_missing: false)) if hook.save present hook, with: Entities::ProjectHook @@ -71,12 +70,9 @@ module API use :project_hook_properties end put ":id/hooks/:hook_id" do - hook = user_project.hooks.find(params[:hook_id]) - - new_params = declared(params, include_missing: false, include_parent_namespaces: false).to_h - new_params.delete('hook_id') + hook = user_project.hooks.find(params.delete(:hook_id)) - if hook.update_attributes(new_params) + if hook.update_attributes(declared_params(include_missing: false)) present hook, with: Entities::ProjectHook else error!("Invalid url given", 422) if hook.errors[:url].present? diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb index f55aceed92c..0bb2f74809a 100644 --- a/lib/api/repositories.rb +++ b/lib/api/repositories.rb @@ -21,16 +21,18 @@ module API # Parameters: # id (required) - The ID of a project # ref_name (optional) - The name of a repository branch or tag, if not given the default branch is used + # recursive (optional) - Used to get a recursive tree # Example Request: # GET /projects/:id/repository/tree get ':id/repository/tree' do ref = params[:ref_name] || user_project.try(:default_branch) || 'master' path = params[:path] || nil + recursive = to_boolean(params[:recursive]) commit = user_project.commit(ref) not_found!('Tree') unless commit - tree = user_project.repository.tree(commit.id, path) + tree = user_project.repository.tree(commit.id, path, recursive: recursive) present tree.sorted_entries, with: Entities::RepoTreeObject end diff --git a/lib/api/runners.rb b/lib/api/runners.rb index 84c19c432b0..b145cce7e3e 100644 --- a/lib/api/runners.rb +++ b/lib/api/runners.rb @@ -57,9 +57,7 @@ module API runner = get_runner(params.delete(:id)) authenticate_update_runner!(runner) - runner_params = declared(params, include_missing: false) - - if runner.update(runner_params) + if runner.update(declared_params(include_missing: false)) present runner, with: Entities::RunnerDetails, current_user: current_user else render_validation_error!(runner) diff --git a/lib/api/subscriptions.rb b/lib/api/subscriptions.rb index c49e2a21b82..00a79c24f96 100644 --- a/lib/api/subscriptions.rb +++ b/lib/api/subscriptions.rb @@ -9,23 +9,20 @@ module API 'labels' => proc { |id| find_project_label(id) }, } + params do + requires :id, type: String, desc: 'The ID of a project' + requires :subscribable_id, type: String, desc: 'The ID of a resource' + end resource :projects do subscribable_types.each do |type, finder| type_singularized = type.singularize - type_id_str = :"#{type_singularized}_id" entity_class = Entities.const_get(type_singularized.camelcase) - # Subscribe to a resource - # - # Parameters: - # id (required) - The ID of a project - # subscribable_id (required) - The ID of a resource - # Example Request: - # POST /projects/:id/labels/:subscribable_id/subscription - # POST /projects/:id/issues/:subscribable_id/subscription - # POST /projects/:id/merge_requests/:subscribable_id/subscription - post ":id/#{type}/:#{type_id_str}/subscription" do - resource = instance_exec(params[type_id_str], &finder) + desc 'Subscribe to a resource' do + success entity_class + end + post ":id/#{type}/:subscribable_id/subscription" do + resource = instance_exec(params[:subscribable_id], &finder) if resource.subscribed?(current_user) not_modified! @@ -35,17 +32,11 @@ module API end end - # Unsubscribe from a resource - # - # Parameters: - # id (required) - The ID of a project - # subscribable_id (required) - The ID of a resource - # Example Request: - # DELETE /projects/:id/labels/:subscribable_id/subscription - # DELETE /projects/:id/issues/:subscribable_id/subscription - # DELETE /projects/:id/merge_requests/:subscribable_id/subscription - delete ":id/#{type}/:#{type_id_str}/subscription" do - resource = instance_exec(params[type_id_str], &finder) + desc 'Unsubscribe from a resource' do + success entity_class + end + delete ":id/#{type}/:subscribable_id/subscription" do + resource = instance_exec(params[:subscribable_id], &finder) if !resource.subscribed?(current_user) not_modified! diff --git a/lib/api/system_hooks.rb b/lib/api/system_hooks.rb index b6bfff9f20f..708ec8cfe70 100644 --- a/lib/api/system_hooks.rb +++ b/lib/api/system_hooks.rb @@ -27,7 +27,7 @@ module API optional :enable_ssl_verification, type: Boolean, desc: "Do SSL verification when triggering the hook" end post do - hook = SystemHook.new declared(params, include_missing: false).to_h + hook = SystemHook.new(declared_params(include_missing: false)) if hook.save present hook, with: Entities::Hook diff --git a/lib/api/tags.rb b/lib/api/tags.rb index bf2a199ce21..cd33f9a9903 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -40,10 +40,9 @@ module API end post ':id/repository/tags' do authorize_push_project - create_params = declared(params) result = CreateTagService.new(user_project, current_user). - execute(create_params[:tag_name], create_params[:ref], create_params[:message], create_params[:release_description]) + execute(params[:tag_name], params[:ref], params[:message], params[:release_description]) if result[:status] == :success present result[:tag], diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb index 9a4f1cd342f..569598fbd2c 100644 --- a/lib/api/triggers.rb +++ b/lib/api/triggers.rb @@ -12,7 +12,7 @@ module API requires :token, type: String, desc: 'The unique token of trigger' optional :variables, type: Hash, desc: 'The list of variables to be injected into build' end - post ":id/trigger/builds" do + post ":id/(ref/:ref/)trigger/builds" do project = Project.find_with_namespace(params[:id]) || Project.find_by(id: params[:id]) trigger = Ci::Trigger.find_by_token(params[:token].to_s) not_found! unless project && trigger diff --git a/lib/api/users.rb b/lib/api/users.rb index 298c401a816..aea328d2f8f 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -335,7 +335,7 @@ module API requires :id, type: String, desc: 'The user ID' end get ':id/events' do - user = User.find_by(id: declared(params).id) + user = User.find_by(id: params[:id]) not_found!('User') unless user events = user.events. diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 3e33c9399e2..fef652cb975 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -2,7 +2,7 @@ module Ci class GitlabCiYamlProcessor class ValidationError < StandardError; end - include Gitlab::Ci::Config::Node::LegacyValidationHelpers + include Gitlab::Ci::Config::Entry::LegacyValidationHelpers attr_reader :path, :cache, :stages, :jobs diff --git a/lib/gitlab/ci/config.rb b/lib/gitlab/ci/config.rb index bbfa6cf7d05..06599238d22 100644 --- a/lib/gitlab/ci/config.rb +++ b/lib/gitlab/ci/config.rb @@ -13,7 +13,7 @@ module Gitlab def initialize(config) @config = Loader.new(config).load! - @global = Node::Global.new(@config) + @global = Entry::Global.new(@config) @global.compose! end diff --git a/lib/gitlab/ci/config/node/artifacts.rb b/lib/gitlab/ci/config/entry/artifacts.rb index 844bd2fe998..b756b0d4555 100644 --- a/lib/gitlab/ci/config/node/artifacts.rb +++ b/lib/gitlab/ci/config/entry/artifacts.rb @@ -1,11 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## # Entry that represents a configuration of job artifacts. # - class Artifacts < Entry + class Artifacts < Node include Validatable include Attributable diff --git a/lib/gitlab/ci/config/node/attributable.rb b/lib/gitlab/ci/config/entry/attributable.rb index 221b666f9f6..1c8b55ee4c4 100644 --- a/lib/gitlab/ci/config/node/attributable.rb +++ b/lib/gitlab/ci/config/entry/attributable.rb @@ -1,7 +1,7 @@ module Gitlab module Ci class Config - module Node + module Entry module Attributable extend ActiveSupport::Concern diff --git a/lib/gitlab/ci/config/node/boolean.rb b/lib/gitlab/ci/config/entry/boolean.rb index 84b03ee7832..f3357f85b99 100644 --- a/lib/gitlab/ci/config/node/boolean.rb +++ b/lib/gitlab/ci/config/entry/boolean.rb @@ -1,11 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## # Entry that represents a boolean value. # - class Boolean < Entry + class Boolean < Node include Validatable validations do diff --git a/lib/gitlab/ci/config/node/cache.rb b/lib/gitlab/ci/config/entry/cache.rb index b4bda2841ac..7653cab668b 100644 --- a/lib/gitlab/ci/config/node/cache.rb +++ b/lib/gitlab/ci/config/entry/cache.rb @@ -1,11 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## # Entry that represents a cache configuration # - class Cache < Entry + class Cache < Node include Configurable ALLOWED_KEYS = %i[key untracked paths] @@ -14,13 +14,13 @@ module Gitlab validates :config, allowed_keys: ALLOWED_KEYS end - node :key, Node::Key, + entry :key, Entry::Key, description: 'Cache key used to define a cache affinity.' - node :untracked, Node::Boolean, + entry :untracked, Entry::Boolean, description: 'Cache all untracked files.' - node :paths, Node::Paths, + entry :paths, Entry::Paths, description: 'Specify which paths should be cached across builds.' end end diff --git a/lib/gitlab/ci/config/node/commands.rb b/lib/gitlab/ci/config/entry/commands.rb index d7657ae314b..65d19db249c 100644 --- a/lib/gitlab/ci/config/node/commands.rb +++ b/lib/gitlab/ci/config/entry/commands.rb @@ -1,11 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## # Entry that represents a job script. # - class Commands < Entry + class Commands < Node include Validatable validations do diff --git a/lib/gitlab/ci/config/node/configurable.rb b/lib/gitlab/ci/config/entry/configurable.rb index 6b7ab2fdaf2..0f438faeda2 100644 --- a/lib/gitlab/ci/config/node/configurable.rb +++ b/lib/gitlab/ci/config/entry/configurable.rb @@ -1,7 +1,7 @@ module Gitlab module Ci class Config - module Node + module Entry ## # This mixin is responsible for adding DSL, which purpose is to # simplifly process of adding child nodes. @@ -48,8 +48,8 @@ module Gitlab private # rubocop:disable Lint/UselessAccessModifier - def node(key, node, metadata) - factory = Node::Factory.new(node) + def entry(key, entry, metadata) + factory = Entry::Factory.new(entry) .with(description: metadata[:description]) (@nodes ||= {}).merge!(key.to_sym => factory) diff --git a/lib/gitlab/ci/config/node/environment.rb b/lib/gitlab/ci/config/entry/environment.rb index 9a95ef43628..b7b4b91eb51 100644 --- a/lib/gitlab/ci/config/node/environment.rb +++ b/lib/gitlab/ci/config/entry/environment.rb @@ -1,11 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## # Entry that represents an environment. # - class Environment < Entry + class Environment < Node include Validatable ALLOWED_KEYS = %i[name url action on_stop] diff --git a/lib/gitlab/ci/config/node/factory.rb b/lib/gitlab/ci/config/entry/factory.rb index 5387f29ad59..9f5e393d191 100644 --- a/lib/gitlab/ci/config/node/factory.rb +++ b/lib/gitlab/ci/config/entry/factory.rb @@ -1,15 +1,15 @@ module Gitlab module Ci class Config - module Node + module Entry ## - # Factory class responsible for fabricating node entry objects. + # Factory class responsible for fabricating entry objects. # class Factory class InvalidFactory < StandardError; end - def initialize(node) - @node = node + def initialize(entry) + @entry = entry @metadata = {} @attributes = {} end @@ -37,11 +37,11 @@ module Gitlab # See issue #18775. # if @value.nil? - Node::Unspecified.new( + Entry::Unspecified.new( fabricate_unspecified ) else - fabricate(@node, @value) + fabricate(@entry, @value) end end @@ -49,21 +49,21 @@ module Gitlab def fabricate_unspecified ## - # If node has a default value we fabricate concrete node + # If entry has a default value we fabricate concrete node # with default value. # - if @node.default.nil? - fabricate(Node::Undefined) + if @entry.default.nil? + fabricate(Entry::Undefined) else - fabricate(@node, @node.default) + fabricate(@entry, @entry.default) end end - def fabricate(node, value = nil) - node.new(value, @metadata).tap do |entry| - entry.key = @attributes[:key] - entry.parent = @attributes[:parent] - entry.description = @attributes[:description] + def fabricate(entry, value = nil) + entry.new(value, @metadata).tap do |node| + node.key = @attributes[:key] + node.parent = @attributes[:parent] + node.description = @attributes[:description] end end end diff --git a/lib/gitlab/ci/config/node/global.rb b/lib/gitlab/ci/config/entry/global.rb index 2a2943c9288..a4ec8f0ff2f 100644 --- a/lib/gitlab/ci/config/node/global.rb +++ b/lib/gitlab/ci/config/entry/global.rb @@ -1,36 +1,36 @@ module Gitlab module Ci class Config - module Node + module Entry ## - # This class represents a global entry - root node for entire + # This class represents a global entry - root Entry for entire # GitLab CI Configuration file. # - class Global < Entry + class Global < Node include Configurable - node :before_script, Node::Script, + entry :before_script, Entry::Script, description: 'Script that will be executed before each job.' - node :image, Node::Image, + entry :image, Entry::Image, description: 'Docker image that will be used to execute jobs.' - node :services, Node::Services, + entry :services, Entry::Services, description: 'Docker images that will be linked to the container.' - node :after_script, Node::Script, + entry :after_script, Entry::Script, description: 'Script that will be executed after each job.' - node :variables, Node::Variables, + entry :variables, Entry::Variables, description: 'Environment variables that will be used.' - node :stages, Node::Stages, + entry :stages, Entry::Stages, description: 'Configuration of stages for this pipeline.' - node :types, Node::Stages, + entry :types, Entry::Stages, description: 'Deprecated: stages for this pipeline.' - node :cache, Node::Cache, + entry :cache, Entry::Cache, description: 'Configure caching between build jobs.' helpers :before_script, :image, :services, :after_script, @@ -46,7 +46,7 @@ module Gitlab private def compose_jobs! - factory = Node::Factory.new(Node::Jobs) + factory = Entry::Factory.new(Entry::Jobs) .value(@config.except(*self.class.nodes.keys)) .with(key: :jobs, parent: self, description: 'Jobs definition for this pipeline') diff --git a/lib/gitlab/ci/config/node/hidden.rb b/lib/gitlab/ci/config/entry/hidden.rb index fe4ee8a7fc6..6fc3aa385bc 100644 --- a/lib/gitlab/ci/config/node/hidden.rb +++ b/lib/gitlab/ci/config/entry/hidden.rb @@ -1,11 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## - # Entry that represents a hidden CI/CD job. + # Entry that represents a hidden CI/CD key. # - class Hidden < Entry + class Hidden < Node include Validatable validations do diff --git a/lib/gitlab/ci/config/node/image.rb b/lib/gitlab/ci/config/entry/image.rb index 5d3c7c5eab0..b5050257688 100644 --- a/lib/gitlab/ci/config/node/image.rb +++ b/lib/gitlab/ci/config/entry/image.rb @@ -1,11 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## # Entry that represents a Docker image. # - class Image < Entry + class Image < Node include Validatable validations do diff --git a/lib/gitlab/ci/config/node/job.rb b/lib/gitlab/ci/config/entry/job.rb index 603334d6793..ab4ef333629 100644 --- a/lib/gitlab/ci/config/node/job.rb +++ b/lib/gitlab/ci/config/entry/job.rb @@ -1,11 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## # Entry that represents a concrete CI/CD job. # - class Job < Entry + class Job < Node include Configurable include Attributable @@ -34,43 +34,43 @@ module Gitlab end end - node :before_script, Node::Script, + entry :before_script, Entry::Script, description: 'Global before script overridden in this job.' - node :script, Node::Commands, + entry :script, Entry::Commands, description: 'Commands that will be executed in this job.' - node :stage, Node::Stage, + entry :stage, Entry::Stage, description: 'Pipeline stage this job will be executed into.' - node :type, Node::Stage, + entry :type, Entry::Stage, description: 'Deprecated: stage this job will be executed into.' - node :after_script, Node::Script, + entry :after_script, Entry::Script, description: 'Commands that will be executed when finishing job.' - node :cache, Node::Cache, + entry :cache, Entry::Cache, description: 'Cache definition for this job.' - node :image, Node::Image, + entry :image, Entry::Image, description: 'Image that will be used to execute this job.' - node :services, Node::Services, + entry :services, Entry::Services, description: 'Services that will be used to execute this job.' - node :only, Node::Trigger, + entry :only, Entry::Trigger, description: 'Refs policy this job will be executed for.' - node :except, Node::Trigger, + entry :except, Entry::Trigger, description: 'Refs policy this job will be executed for.' - node :variables, Node::Variables, + entry :variables, Entry::Variables, description: 'Environment variables available for this job.' - node :artifacts, Node::Artifacts, + entry :artifacts, Entry::Artifacts, description: 'Artifacts configuration for this job.' - node :environment, Node::Environment, + entry :environment, Entry::Environment, description: 'Environment configuration for this job.' helpers :before_script, :script, :stage, :type, :after_script, diff --git a/lib/gitlab/ci/config/node/jobs.rb b/lib/gitlab/ci/config/entry/jobs.rb index d10e80d1a7d..5671a09480b 100644 --- a/lib/gitlab/ci/config/node/jobs.rb +++ b/lib/gitlab/ci/config/entry/jobs.rb @@ -1,11 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## # Entry that represents a set of jobs. # - class Jobs < Entry + class Jobs < Node include Validatable validations do @@ -29,9 +29,9 @@ module Gitlab def compose!(deps = nil) super do @config.each do |name, config| - node = hidden?(name) ? Node::Hidden : Node::Job + node = hidden?(name) ? Entry::Hidden : Entry::Job - factory = Node::Factory.new(node) + factory = Entry::Factory.new(node) .value(config || {}) .metadata(name: name) .with(key: name, parent: self, diff --git a/lib/gitlab/ci/config/node/key.rb b/lib/gitlab/ci/config/entry/key.rb index f8b461ca098..0e4c9fe6edc 100644 --- a/lib/gitlab/ci/config/node/key.rb +++ b/lib/gitlab/ci/config/entry/key.rb @@ -1,11 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## # Entry that represents a key. # - class Key < Entry + class Key < Node include Validatable validations do diff --git a/lib/gitlab/ci/config/node/legacy_validation_helpers.rb b/lib/gitlab/ci/config/entry/legacy_validation_helpers.rb index 0c291efe6a5..f01975aab5c 100644 --- a/lib/gitlab/ci/config/node/legacy_validation_helpers.rb +++ b/lib/gitlab/ci/config/entry/legacy_validation_helpers.rb @@ -1,7 +1,7 @@ module Gitlab module Ci class Config - module Node + module Entry module LegacyValidationHelpers private diff --git a/lib/gitlab/ci/config/node/entry.rb b/lib/gitlab/ci/config/entry/node.rb index 8717eabf81e..5eef2868cd6 100644 --- a/lib/gitlab/ci/config/node/entry.rb +++ b/lib/gitlab/ci/config/entry/node.rb @@ -1,11 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## # Base abstract class for each configuration entry node. # - class Entry + class Node class InvalidError < StandardError; end attr_reader :config, :metadata @@ -21,7 +21,7 @@ module Gitlab end def [](key) - @entries[key] || Node::Undefined.new + @entries[key] || Entry::Undefined.new end def compose!(deps = nil) diff --git a/lib/gitlab/ci/config/node/paths.rb b/lib/gitlab/ci/config/entry/paths.rb index 3c6d3a52966..68dad161149 100644 --- a/lib/gitlab/ci/config/node/paths.rb +++ b/lib/gitlab/ci/config/entry/paths.rb @@ -1,11 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## # Entry that represents an array of paths. # - class Paths < Entry + class Paths < Node include Validatable validations do diff --git a/lib/gitlab/ci/config/node/script.rb b/lib/gitlab/ci/config/entry/script.rb index 39328f0fade..29ecd9995ca 100644 --- a/lib/gitlab/ci/config/node/script.rb +++ b/lib/gitlab/ci/config/entry/script.rb @@ -1,11 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## # Entry that represents a script. # - class Script < Entry + class Script < Node include Validatable validations do diff --git a/lib/gitlab/ci/config/node/services.rb b/lib/gitlab/ci/config/entry/services.rb index 481e2b66adc..84f8ab780f5 100644 --- a/lib/gitlab/ci/config/node/services.rb +++ b/lib/gitlab/ci/config/entry/services.rb @@ -1,11 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## # Entry that represents a configuration of Docker services. # - class Services < Entry + class Services < Node include Validatable validations do diff --git a/lib/gitlab/ci/config/node/stage.rb b/lib/gitlab/ci/config/entry/stage.rb index cbc97641f5a..b7afaba1de8 100644 --- a/lib/gitlab/ci/config/node/stage.rb +++ b/lib/gitlab/ci/config/entry/stage.rb @@ -1,11 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## # Entry that represents a stage for a job. # - class Stage < Entry + class Stage < Node include Validatable validations do diff --git a/lib/gitlab/ci/config/node/stages.rb b/lib/gitlab/ci/config/entry/stages.rb index b1fe45357ff..ec187bd3732 100644 --- a/lib/gitlab/ci/config/node/stages.rb +++ b/lib/gitlab/ci/config/entry/stages.rb @@ -1,11 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## # Entry that represents a configuration for pipeline stages. # - class Stages < Entry + class Stages < Node include Validatable validations do diff --git a/lib/gitlab/ci/config/node/trigger.rb b/lib/gitlab/ci/config/entry/trigger.rb index d8b31975088..28b0a9ffe01 100644 --- a/lib/gitlab/ci/config/node/trigger.rb +++ b/lib/gitlab/ci/config/entry/trigger.rb @@ -1,11 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## # Entry that represents a trigger policy for the job. # - class Trigger < Entry + class Trigger < Node include Validatable validations do diff --git a/lib/gitlab/ci/config/node/undefined.rb b/lib/gitlab/ci/config/entry/undefined.rb index 33e78023539..b33b8238230 100644 --- a/lib/gitlab/ci/config/node/undefined.rb +++ b/lib/gitlab/ci/config/entry/undefined.rb @@ -1,13 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## - # This class represents an undefined node. + # This class represents an undefined entry. # - # Implements the Null Object pattern. - # - class Undefined < Entry + class Undefined < Node def initialize(*) super(nil) end diff --git a/lib/gitlab/ci/config/node/unspecified.rb b/lib/gitlab/ci/config/entry/unspecified.rb index a7d1f6131b8..fbb2551e870 100644 --- a/lib/gitlab/ci/config/node/unspecified.rb +++ b/lib/gitlab/ci/config/entry/unspecified.rb @@ -1,9 +1,9 @@ module Gitlab module Ci class Config - module Node + module Entry ## - # This class represents an unspecified entry node. + # This class represents an unspecified entry. # # It decorates original entry adding method that indicates it is # unspecified. diff --git a/lib/gitlab/ci/config/node/validatable.rb b/lib/gitlab/ci/config/entry/validatable.rb index 085e6e988d1..f7f1b111571 100644 --- a/lib/gitlab/ci/config/node/validatable.rb +++ b/lib/gitlab/ci/config/entry/validatable.rb @@ -1,13 +1,13 @@ module Gitlab module Ci class Config - module Node + module Entry module Validatable extend ActiveSupport::Concern class_methods do def validator - @validator ||= Class.new(Node::Validator).tap do |validator| + @validator ||= Class.new(Entry::Validator).tap do |validator| if defined?(@validations) @validations.each { |rules| validator.class_eval(&rules) } end diff --git a/lib/gitlab/ci/config/node/validator.rb b/lib/gitlab/ci/config/entry/validator.rb index 43c7e102b50..55343005fe3 100644 --- a/lib/gitlab/ci/config/node/validator.rb +++ b/lib/gitlab/ci/config/entry/validator.rb @@ -1,14 +1,14 @@ module Gitlab module Ci class Config - module Node + module Entry class Validator < SimpleDelegator include ActiveModel::Validations - include Node::Validators + include Entry::Validators - def initialize(node) - super(node) - @node = node + def initialize(entry) + super(entry) + @entry = entry end def messages @@ -30,7 +30,7 @@ module Gitlab def key_name if key.blank? - @node.class.name.demodulize.underscore.humanize + @entry.class.name.demodulize.underscore.humanize else key end diff --git a/lib/gitlab/ci/config/node/validators.rb b/lib/gitlab/ci/config/entry/validators.rb index e20908ad3cb..8632dd0e233 100644 --- a/lib/gitlab/ci/config/node/validators.rb +++ b/lib/gitlab/ci/config/entry/validators.rb @@ -1,7 +1,7 @@ module Gitlab module Ci class Config - module Node + module Entry module Validators class AllowedKeysValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) diff --git a/lib/gitlab/ci/config/node/variables.rb b/lib/gitlab/ci/config/entry/variables.rb index 5f813f81f55..c3b0e651c3a 100644 --- a/lib/gitlab/ci/config/node/variables.rb +++ b/lib/gitlab/ci/config/entry/variables.rb @@ -1,11 +1,11 @@ module Gitlab module Ci class Config - module Node + module Entry ## # Entry that represents environment variables. # - class Variables < Entry + class Variables < Node include Validatable validations do diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index ef9160d6437..c6bb8f9c8ed 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -23,6 +23,10 @@ module Gitlab settings || fake_application_settings end + def sidekiq_throttling_enabled? + current_application_settings.sidekiq_throttling_enabled? + end + def fake_application_settings OpenStruct.new( default_projects_limit: Settings.gitlab['default_projects_limit'], @@ -50,6 +54,7 @@ module Gitlab repository_checks_enabled: true, container_registry_token_expire_delay: 5, user_default_external: false, + sidekiq_throttling_enabled: false, ) end diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb index ce85e5e0123..c6bf25b5874 100644 --- a/lib/gitlab/diff/file.rb +++ b/lib/gitlab/diff/file.rb @@ -55,6 +55,12 @@ module Gitlab repository.commit(deleted_file ? old_ref : new_ref) end + def old_content_commit + return unless diff_refs + + repository.commit(old_ref) + end + def old_ref diff_refs.try(:base_sha) end @@ -111,13 +117,10 @@ module Gitlab diff_lines.count(&:removed?) end - def old_blob(commit = content_commit) + def old_blob(commit = old_content_commit) return unless commit - parent_id = commit.parent_id - return unless parent_id - - repository.blob_at(parent_id, old_path) + repository.blob_at(commit.id, old_path) end def blob(commit = content_commit) @@ -126,7 +129,7 @@ module Gitlab repository.blob_at(commit.id, file_path) end - def cache_key + def file_identifier "#{file_path}-#{new_file}-#{deleted_file}-#{renamed_file}" end end diff --git a/lib/gitlab/diff/file_collection/merge_request_diff.rb b/lib/gitlab/diff/file_collection/merge_request_diff.rb index dc4d47c878b..fe7adb7bed6 100644 --- a/lib/gitlab/diff/file_collection/merge_request_diff.rb +++ b/lib/gitlab/diff/file_collection/merge_request_diff.rb @@ -39,7 +39,7 @@ module Gitlab # hashes that represent serialized diff lines. # def cache_highlight!(diff_file) - item_key = diff_file.cache_key + item_key = diff_file.file_identifier if highlight_cache[item_key] highlight_diff_file_from_cache!(diff_file, highlight_cache[item_key]) diff --git a/lib/gitlab/ldap/adapter.rb b/lib/gitlab/ldap/adapter.rb index 8b38cfaefb6..7b05290e5cc 100644 --- a/lib/gitlab/ldap/adapter.rb +++ b/lib/gitlab/ldap/adapter.rb @@ -89,9 +89,7 @@ module Gitlab end def user_filter(filter = nil) - if config.user_filter.present? - user_filter = Net::LDAP::Filter.construct(config.user_filter) - end + user_filter = config.constructed_user_filter if config.user_filter.present? if user_filter && filter Net::LDAP::Filter.join(filter, user_filter) diff --git a/lib/gitlab/ldap/authentication.rb b/lib/gitlab/ldap/authentication.rb index bad683c6511..4745311402c 100644 --- a/lib/gitlab/ldap/authentication.rb +++ b/lib/gitlab/ldap/authentication.rb @@ -54,11 +54,9 @@ module Gitlab # Apply LDAP user filter if present if config.user_filter.present? - filter = Net::LDAP::Filter.join( - filter, - Net::LDAP::Filter.construct(config.user_filter) - ) + filter = Net::LDAP::Filter.join(filter, config.constructed_user_filter) end + filter end diff --git a/lib/gitlab/ldap/config.rb b/lib/gitlab/ldap/config.rb index 6ea069d26df..de52ef3fc65 100644 --- a/lib/gitlab/ldap/config.rb +++ b/lib/gitlab/ldap/config.rb @@ -13,7 +13,7 @@ module Gitlab end def self.providers - servers.map {|server| server['provider_name'] } + servers.map { |server| server['provider_name'] } end def self.valid_provider?(provider) @@ -38,13 +38,31 @@ module Gitlab end def adapter_options - { - host: options['host'], - port: options['port'], - encryption: encryption - }.tap do |options| - options.merge!(auth_options) if has_auth? + opts = base_options.merge( + encryption: encryption, + ) + + opts.merge!(auth_options) if has_auth? + + opts + end + + def omniauth_options + opts = base_options.merge( + base: base, + method: options['method'], + filter: omniauth_user_filter, + name_proc: name_proc + ) + + if has_auth? + opts.merge!( + bind_dn: options['bind_dn'], + password: options['password'] + ) end + + opts end def base @@ -68,6 +86,10 @@ module Gitlab options['user_filter'] end + def constructed_user_filter + @constructed_user_filter ||= Net::LDAP::Filter.construct(user_filter) + end + def group_base options['group_base'] end @@ -96,8 +118,27 @@ module Gitlab options['password'] || options['bind_dn'] end + def allow_username_or_email_login + options['allow_username_or_email_login'] + end + + def name_proc + if allow_username_or_email_login + Proc.new { |name| name.gsub(/@.*\z/, '') } + else + Proc.new { |name| name } + end + end + protected + def base_options + { + host: options['host'], + port: options['port'] + } + end + def base_config Gitlab.config.ldap end @@ -126,6 +167,16 @@ module Gitlab } } end + + def omniauth_user_filter + uid_filter = Net::LDAP::Filter.eq(uid, '%{username}') + + if user_filter.present? + Net::LDAP::Filter.join(uid_filter, constructed_user_filter).to_s + else + uid_filter.to_s + end + end end end end diff --git a/lib/gitlab/mail_room.rb b/lib/gitlab/mail_room.rb index a5220d92312..3503fac40e8 100644 --- a/lib/gitlab/mail_room.rb +++ b/lib/gitlab/mail_room.rb @@ -31,6 +31,7 @@ module Gitlab config[:ssl] = false if config[:ssl].nil? config[:start_tls] = false if config[:start_tls].nil? config[:mailbox] = 'inbox' if config[:mailbox].nil? + config[:idle_timeout] = 60 if config[:idle_timeout].nil? if config[:enabled] && config[:address] gitlab_redis = Gitlab::Redis.new(rails_env) diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb index 0a91d3918d5..a8b4dc2a83f 100644 --- a/lib/gitlab/o_auth/user.rb +++ b/lib/gitlab/o_auth/user.rb @@ -102,6 +102,8 @@ module Gitlab Gitlab::LDAP::Config.providers.each do |provider| adapter = Gitlab::LDAP::Adapter.new(provider) @ldap_person = Gitlab::LDAP::Person.find_by_uid(auth_hash.uid, adapter) + # The `uid` might actually be a DN. Try it next. + @ldap_person ||= Gitlab::LDAP::Person.find_by_dn(auth_hash.uid, adapter) break if @ldap_person end @ldap_person diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index b8326a64b22..66e6b29e798 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -5,7 +5,7 @@ module Gitlab def initialize(current_user, project, query, repository_ref = nil) @current_user = current_user @project = project - @repository_ref = repository_ref.presence + @repository_ref = repository_ref.presence || project.default_branch @query = query end @@ -40,10 +40,57 @@ module Gitlab @commits_count ||= commits.count end + def self.parse_search_result(result) + ref = nil + filename = nil + basename = nil + startline = 0 + + result.each_line.each_with_index do |line, index| + if line =~ /^.*:.*:\d+:/ + ref, filename, startline = line.split(':') + startline = startline.to_i - index + extname = Regexp.escape(File.extname(filename)) + basename = filename.sub(/#{extname}$/, '') + break + end + end + + data = "" + + result.each_line do |line| + data << line.sub(ref, '').sub(filename, '').sub(/^:-\d+-/, '').sub(/^::\d+:/, '') + end + + OpenStruct.new( + filename: filename, + basename: basename, + ref: ref, + startline: startline, + data: data + ) + end + private def blobs - @blobs ||= project.repository.search_files(query, repository_ref) + @blobs ||= begin + blobs = project.repository.search_files_by_content(query, repository_ref).first(100) + found_file_names = Set.new + + results = blobs.map do |blob| + blob = self.class.parse_search_result(blob) + found_file_names << blob.filename + + [blob.filename, blob] + end + + project.repository.search_files_by_name(query, repository_ref).first(100).each do |filename| + results << [filename, nil] unless found_file_names.include?(filename) + end + + results.sort_by(&:first) + end end def wiki_blobs diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb index cb1659f9cee..155ca47e04c 100644 --- a/lib/gitlab/regex.rb +++ b/lib/gitlab/regex.rb @@ -26,12 +26,12 @@ module Gitlab end def project_name_regex - @project_name_regex ||= /\A[\p{Alnum}_][\p{Alnum}\p{Pd}_\. ]*\z/.freeze + @project_name_regex ||= /\A[\p{Alnum}\u{00A9}-\u{1f9c0}_][\p{Alnum}\p{Pd}\u{00A9}-\u{1f9c0}_\. ]*\z/.freeze end def project_name_regex_message - "can contain only letters, digits, '_', '.', dash and space. " \ - "It must start with letter, digit or '_'." + "can contain only letters, digits, emojis, '_', '.', dash, space. " \ + "It must start with letter, digit, emoji or '_'." end def project_path_regex diff --git a/lib/gitlab/sidekiq_throttler.rb b/lib/gitlab/sidekiq_throttler.rb new file mode 100644 index 00000000000..d4d39a888e7 --- /dev/null +++ b/lib/gitlab/sidekiq_throttler.rb @@ -0,0 +1,23 @@ +module Gitlab + class SidekiqThrottler + class << self + def execute! + if Gitlab::CurrentSettings.sidekiq_throttling_enabled? + Gitlab::CurrentSettings.current_application_settings.sidekiq_throttling_queues.each do |queue| + Sidekiq::Queue[queue].limit = queue_limit + end + end + end + + private + + def queue_limit + @queue_limit ||= + begin + factor = Gitlab::CurrentSettings.current_application_settings.sidekiq_throttling_factor + (factor * Sidekiq.options[:concurrency]).ceil + end + end + end + end +end |