diff options
Diffstat (limited to 'lib/api')
92 files changed, 825 insertions, 106 deletions
diff --git a/lib/api/access_requests.rb b/lib/api/access_requests.rb index ae13c248171..cecff6d3b81 100644 --- a/lib/api/access_requests.rb +++ b/lib/api/access_requests.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class AccessRequests < Grape::API include PaginationParams @@ -18,6 +20,7 @@ module API params do use :pagination end + # rubocop: disable CodeReuse/ActiveRecord get ":id/access_requests" do source = find_source(source_type, params[:id]) @@ -26,6 +29,7 @@ module API present access_requesters, with: Entities::AccessRequester end + # rubocop: enable CodeReuse/ActiveRecord desc "Requests access for the authenticated user to a #{source_type}." do detail 'This feature was introduced in GitLab 8.11.' @@ -50,6 +54,7 @@ module API requires :user_id, type: Integer, desc: 'The user ID of the access requester' optional :access_level, type: Integer, desc: 'A valid access level (defaults: `30`, developer access level)' end + # rubocop: disable CodeReuse/ActiveRecord put ':id/access_requests/:user_id/approve' do source = find_source(source_type, params[:id]) @@ -61,6 +66,7 @@ module API status :created present member, with: Entities::Member end + # rubocop: enable CodeReuse/ActiveRecord desc 'Denies an access request for the given user.' do detail 'This feature was introduced in GitLab 8.11.' @@ -68,6 +74,7 @@ module API params do requires :user_id, type: Integer, desc: 'The user ID of the access requester' end + # rubocop: disable CodeReuse/ActiveRecord delete ":id/access_requests/:user_id" do source = find_source(source_type, params[:id]) member = source.requesters.find_by!(user_id: params[:user_id]) @@ -76,6 +83,7 @@ module API ::Members::DestroyService.new(current_user).execute(member) end end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/api.rb b/lib/api/api.rb index c000666d992..c49c52213bf 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class API < Grape::API include APIGuard @@ -15,8 +17,10 @@ module API include: [ GrapeLogging::Loggers::FilterParameters.new, GrapeLogging::Loggers::ClientEnv.new, + Gitlab::GrapeLogging::Loggers::RouteLogger.new, Gitlab::GrapeLogging::Loggers::UserLogger.new, - Gitlab::GrapeLogging::Loggers::QueueDurationLogger.new + Gitlab::GrapeLogging::Loggers::QueueDurationLogger.new, + Gitlab::GrapeLogging::Loggers::PerfLogger.new ] allow_access_with_scope :api @@ -116,6 +120,7 @@ module API mount ::API::Namespaces mount ::API::Notes mount ::API::Discussions + mount ::API::ResourceLabelEvents mount ::API::NotificationSettings mount ::API::PagesDomains mount ::API::Pipelines @@ -127,6 +132,7 @@ module API mount ::API::Projects mount ::API::ProjectSnapshots mount ::API::ProjectSnippets + mount ::API::ProjectTemplates mount ::API::ProtectedBranches mount ::API::ProtectedTags mount ::API::Repositories diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb index 8ee7987cfff..61357b3f1d6 100644 --- a/lib/api/api_guard.rb +++ b/lib/api/api_guard.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Guard API with OAuth 2.0 Access Token require 'rack/oauth2' diff --git a/lib/api/applications.rb b/lib/api/applications.rb index b122cdefe4e..f29cd7fc003 100644 --- a/lib/api/applications.rb +++ b/lib/api/applications.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API # External applications API class Applications < Grape::API diff --git a/lib/api/avatar.rb b/lib/api/avatar.rb index 70219bc8ea0..0f14d003065 100644 --- a/lib/api/avatar.rb +++ b/lib/api/avatar.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Avatar < Grape::API resource :avatar do diff --git a/lib/api/award_emoji.rb b/lib/api/award_emoji.rb index bde4b3ff4f6..c2abf9155f3 100644 --- a/lib/api/award_emoji.rb +++ b/lib/api/award_emoji.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class AwardEmoji < Grape::API include PaginationParams @@ -103,6 +105,7 @@ module API awardable.user_can_award?(current_user) end + # rubocop: disable CodeReuse/ActiveRecord def awardable @awardable ||= begin @@ -119,6 +122,7 @@ module API end end end + # rubocop: enable CodeReuse/ActiveRecord def read_ability(awardable) case awardable diff --git a/lib/api/badges.rb b/lib/api/badges.rb index 8ceffe9c5ef..ab670988f47 100644 --- a/lib/api/badges.rb +++ b/lib/api/badges.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Badges < Grape::API include PaginationParams diff --git a/lib/api/boards.rb b/lib/api/boards.rb index 0f89414148b..c80e1c57864 100644 --- a/lib/api/boards.rb +++ b/lib/api/boards.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Boards < Grape::API include BoardsResponses diff --git a/lib/api/boards_responses.rb b/lib/api/boards_responses.rb index 7e873012efe..86d9b24802f 100644 --- a/lib/api/boards_responses.rb +++ b/lib/api/boards_responses.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module BoardsResponses extend ActiveSupport::Concern @@ -49,11 +51,13 @@ module API end end + # rubocop: disable CodeReuse/ActiveRecord def authorize_list_type_resource! unless available_labels_for(board_parent).exists?(params[:label_id]) render_api_error!({ error: 'Label not found!' }, 400) end end + # rubocop: enable CodeReuse/ActiveRecord params :list_creation_params do requires :label_id, type: Integer, desc: 'The ID of an existing label' diff --git a/lib/api/branches.rb b/lib/api/branches.rb index 3e445e6b1fa..2735d410c8e 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'mime/types' module API @@ -9,14 +11,6 @@ module API before { authorize! :download_code, user_project } helpers do - def find_branch!(branch_name) - begin - user_project.repository.find_branch(branch_name) || not_found!('Branch') - rescue Gitlab::Git::CommandError - render_api_error!('The branch refname is invalid', 400) - end - end - params :filter_params do optional :search, type: String, desc: 'Return list of branches matching the search criteria' optional :sort, type: String, desc: 'Return list of branches sorted by the given field' @@ -77,10 +71,11 @@ module API success Entities::Branch end params do - requires :branch, type: String, desc: 'The name of the branch' + requires :branch, type: String, desc: 'The name of the branch', allow_blank: false 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 + # rubocop: disable CodeReuse/ActiveRecord put ':id/repository/branches/:branch/protect', requirements: BRANCH_ENDPOINT_REQUIREMENTS do authorize_admin_project @@ -108,14 +103,16 @@ module API render_api_error!(protected_branch.errors.full_messages, 422) end end + # rubocop: enable CodeReuse/ActiveRecord # Note: This API will be deprecated in favor of the protected branches API. desc 'Unprotect a single branch' do success Entities::Branch end params do - requires :branch, type: String, desc: 'The name of the branch' + requires :branch, type: String, desc: 'The name of the branch', allow_blank: false end + # rubocop: disable CodeReuse/ActiveRecord put ':id/repository/branches/:branch/unprotect', requirements: BRANCH_ENDPOINT_REQUIREMENTS do authorize_admin_project @@ -125,13 +122,14 @@ module API present branch, with: Entities::Branch, current_user: current_user, project: user_project end + # rubocop: enable CodeReuse/ActiveRecord desc 'Create branch' do success Entities::Branch end params do - requires :branch, type: String, desc: 'The name of the branch' - requires :ref, type: String, desc: 'Create branch from commit sha or existing branch' + requires :branch, type: String, desc: 'The name of the branch', allow_blank: false + requires :ref, type: String, desc: 'Create branch from commit sha or existing branch', allow_blank: false end post ':id/repository/branches' do authorize_push_project @@ -151,7 +149,7 @@ module API desc 'Delete a branch' params do - requires :branch, type: String, desc: 'The name of the branch' + requires :branch, type: String, desc: 'The name of the branch', allow_blank: false end delete ':id/repository/branches/:branch', requirements: BRANCH_ENDPOINT_REQUIREMENTS do authorize_push_project diff --git a/lib/api/broadcast_messages.rb b/lib/api/broadcast_messages.rb index d7138b2f2fe..19148758fc5 100644 --- a/lib/api/broadcast_messages.rb +++ b/lib/api/broadcast_messages.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class BroadcastMessages < Grape::API include PaginationParams diff --git a/lib/api/circuit_breakers.rb b/lib/api/circuit_breakers.rb index c13154dc0ec..6eddc5e5b61 100644 --- a/lib/api/circuit_breakers.rb +++ b/lib/api/circuit_breakers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class CircuitBreakers < Grape::API before { authenticated_as_admin! } diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index 829eef18795..99553d993ca 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'mime/types' module API @@ -21,6 +23,7 @@ module API optional :all, type: String, desc: 'Show all statuses, default: false' use :pagination end + # rubocop: disable CodeReuse/ActiveRecord get ':id/repository/commits/:sha/statuses' do authorize!(:read_commit_status, user_project) @@ -34,6 +37,7 @@ module API statuses = statuses.where(name: params[:name]) if params[:name].present? present paginate(statuses), with: Entities::CommitStatus end + # rubocop: enable CodeReuse/ActiveRecord desc 'Post status to a commit' do success Entities::CommitStatus @@ -49,6 +53,7 @@ module API optional :context, type: String, desc: 'A string label to differentiate this status from the status of other systems. Default: "default"' optional :coverage, type: Float, desc: 'The total code coverage' end + # rubocop: disable CodeReuse/ActiveRecord post ':id/statuses/:sha' do authorize! :create_commit_status, user_project @@ -118,6 +123,7 @@ module API render_api_error!(e.message, 400) end end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/commits.rb b/lib/api/commits.rb index 92329465b2c..e59abd3e3d0 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'mime/types' module API @@ -71,12 +73,32 @@ module API detail 'This feature was introduced in GitLab 8.13' end params do - requires :branch, type: String, desc: 'Name of the branch to commit into. To create a new branch, also provide `start_branch`.' + requires :branch, type: String, desc: 'Name of the branch to commit into. To create a new branch, also provide `start_branch`.', allow_blank: false requires :commit_message, type: String, desc: 'Commit message' - requires :actions, type: Array[Hash], desc: 'Actions to perform in commit' + requires :actions, type: Array, desc: 'Actions to perform in commit' do + requires :action, type: String, desc: 'The action to perform, `create`, `delete`, `move`, `update`, `chmod`', values: %w[create update move delete chmod].freeze + requires :file_path, type: String, desc: 'Full path to the file. Ex. `lib/class.rb`' + given action: ->(action) { action == 'move' } do + requires :previous_path, type: String, desc: 'Original full path to the file being moved. Ex. `lib/class1.rb`' + end + given action: ->(action) { %w[create move].include? action } do + optional :content, type: String, desc: 'File content' + end + given action: ->(action) { action == 'update' } do + requires :content, type: String, desc: 'File content' + end + optional :encoding, type: String, desc: '`text` or `base64`', default: 'text', values: %w[text base64] + given action: ->(action) { %w[update move delete].include? action } do + optional :last_commit_id, type: String, desc: 'Last known file commit id' + end + given action: ->(action) { action == 'chmod' } do + requires :execute_filemode, type: Boolean, desc: 'When `true/false` enables/disables the execute flag on the file.' + end + end optional :start_branch, type: String, desc: 'Name of the branch to start the new commit from' optional :author_email, type: String, desc: 'Author email for commit' optional :author_name, type: String, desc: 'Author name for commit' + optional :stats, type: Boolean, default: true, desc: 'Include commit stats' end post ':id/repository/commits' do authorize_push_to_branch!(params[:branch]) @@ -89,7 +111,10 @@ module API if result[:status] == :success commit_detail = user_project.repository.commit(result[:result]) - present commit_detail, with: Entities::CommitDetail + + Gitlab::WebIdeCommitsCounter.increment if find_user_from_warden + + present commit_detail, with: Entities::CommitDetail, stats: params[:stats] else render_api_error!(result[:message], 400) end @@ -136,6 +161,7 @@ module API use :pagination requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag' end + # rubocop: disable CodeReuse/ActiveRecord get ':id/repository/commits/:sha/comments', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do commit = user_project.commit(params[:sha]) @@ -144,6 +170,7 @@ module API present paginate(notes), with: Entities::CommitNote end + # rubocop: enable CodeReuse/ActiveRecord desc 'Cherry pick commit into a branch' do detail 'This feature was introduced in GitLab 8.15' @@ -151,7 +178,7 @@ module API end params do requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag to be cherry picked' - requires :branch, type: String, desc: 'The name of the branch' + requires :branch, type: String, desc: 'The name of the branch', allow_blank: false end post ':id/repository/commits/:sha/cherry_pick', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do authorize_push_to_branch!(params[:branch]) @@ -159,8 +186,7 @@ module API commit = user_project.commit(params[:sha]) not_found!('Commit') unless commit - branch = user_project.repository.find_branch(params[:branch]) - not_found!('Branch') unless branch + find_branch!(params[:branch]) commit_params = { commit: commit, @@ -171,7 +197,7 @@ module API result = ::Commits::CherryPickService.new(user_project, current_user, commit_params).execute if result[:status] == :success - branch = user_project.repository.find_branch(params[:branch]) + branch = find_branch!(params[:branch]) present user_project.repository.commit(branch.dereferenced_target), with: Entities::Commit else render_api_error!(result[:message], 400) diff --git a/lib/api/custom_attributes_endpoints.rb b/lib/api/custom_attributes_endpoints.rb index 5000aa0d9ac..2149e04451e 100644 --- a/lib/api/custom_attributes_endpoints.rb +++ b/lib/api/custom_attributes_endpoints.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module CustomAttributesEndpoints extend ActiveSupport::Concern @@ -30,6 +32,7 @@ module API params do use :custom_attributes_key end + # rubocop: disable CodeReuse/ActiveRecord get ':id/custom_attributes/:key' do resource = public_send(attributable_finder, params[:id]) # rubocop:disable GitlabSecurity/PublicSend authorize! :read_custom_attribute @@ -38,12 +41,14 @@ module API present custom_attribute, with: Entities::CustomAttribute end + # rubocop: enable CodeReuse/ActiveRecord desc "Set a custom attribute on a #{attributable_name}" params do use :custom_attributes_key requires :value, type: String, desc: 'The value of the custom attribute' end + # rubocop: disable CodeReuse/ActiveRecord put ':id/custom_attributes/:key' do resource = public_send(attributable_finder, params[:id]) # rubocop:disable GitlabSecurity/PublicSend authorize! :update_custom_attribute @@ -59,11 +64,13 @@ module API render_validation_error!(custom_attribute) end end + # rubocop: enable CodeReuse/ActiveRecord desc "Delete a custom attribute on a #{attributable_name}" params do use :custom_attributes_key end + # rubocop: disable CodeReuse/ActiveRecord delete ':id/custom_attributes/:key' do resource = public_send(attributable_finder, params[:id]) # rubocop:disable GitlabSecurity/PublicSend authorize! :update_custom_attribute @@ -72,6 +79,7 @@ module API status 204 end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb index 6769855b899..ce35720d408 100644 --- a/lib/api/deploy_keys.rb +++ b/lib/api/deploy_keys.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class DeployKeys < Grape::API include PaginationParams @@ -9,9 +11,11 @@ module API project.deploy_keys_projects.create(attrs) end + # rubocop: disable CodeReuse/ActiveRecord def find_by_deploy_key(project, key_id) project.deploy_keys_projects.find_by!(deploy_key: key_id) end + # rubocop: enable CodeReuse/ActiveRecord end desc 'Return all deploy keys' @@ -36,11 +40,13 @@ module API params do use :pagination end + # rubocop: disable CodeReuse/ActiveRecord get ":id/deploy_keys" do keys = user_project.deploy_keys_projects.preload(:deploy_key) present paginate(keys), with: Entities::DeployKeysProject end + # rubocop: enable CodeReuse/ActiveRecord desc 'Get single deploy key' do success Entities::DeployKeysProject @@ -62,6 +68,7 @@ module API requires :title, type: String, desc: 'The name of the deploy key' optional :can_push, type: Boolean, desc: "Can deploy key push to the project's repository" end + # rubocop: disable CodeReuse/ActiveRecord post ":id/deploy_keys" do params[:key].strip! @@ -94,6 +101,7 @@ module API render_validation_error!(deploy_key_project) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Update an existing deploy key for a project' do success Entities::SSHKey @@ -147,12 +155,14 @@ module API params do requires :key_id, type: Integer, desc: 'The ID of the deploy key' end + # rubocop: disable CodeReuse/ActiveRecord delete ":id/deploy_keys/:key_id" do deploy_key_project = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id]) not_found!('Deploy Key') unless deploy_key_project destroy_conditionally!(deploy_key_project) end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/deployments.rb b/lib/api/deployments.rb index 184fae0eb76..6747e2e5005 100644 --- a/lib/api/deployments.rb +++ b/lib/api/deployments.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API # Deployments RESTful API endpoints class Deployments < Grape::API @@ -18,11 +20,13 @@ module API optional :order_by, type: String, values: %w[id iid created_at ref], default: 'id', desc: 'Return deployments ordered by `id` or `iid` or `created_at` or `ref`' optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)' end + # rubocop: disable CodeReuse/ActiveRecord get ':id/deployments' do authorize! :read_deployment, user_project present paginate(user_project.deployments.order(params[:order_by] => params[:sort])), with: Entities::Deployment end + # rubocop: enable CodeReuse/ActiveRecord desc 'Gets a specific deployment' do detail 'This feature was introduced in GitLab 8.11.' diff --git a/lib/api/discussions.rb b/lib/api/discussions.rb index 13c34e3473a..39c6d28391d 100644 --- a/lib/api/discussions.rb +++ b/lib/api/discussions.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Discussions < Grape::API include PaginationParams @@ -23,6 +25,7 @@ module API requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable' use :pagination end + # rubocop: disable CodeReuse/ActiveRecord get ":id/#{noteables_path}/:noteable_id/discussions" do noteable = find_noteable(parent_type, noteables_str, params[:noteable_id]) @@ -36,6 +39,7 @@ module API present paginate(discussions), with: Entities::Discussion end + # rubocop: enable CodeReuse/ActiveRecord desc "Get a single #{noteable_type.to_s.downcase} discussion" do success Entities::Discussion @@ -219,6 +223,7 @@ module API end helpers do + # rubocop: disable CodeReuse/ActiveRecord def readable_discussion_notes(noteable, discussion_id) notes = noteable.notes .where(discussion_id: discussion_id) @@ -228,6 +233,7 @@ module API notes.reject { |n| n.cross_reference_not_visible_for?(current_user) } end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 95b25d7351a..120545792f2 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module Entities class WikiPageBasic < Grape::Entity @@ -10,6 +12,28 @@ module API expose :content end + class WikiAttachment < Grape::Entity + include Gitlab::FileMarkdownLinkBuilder + + expose :file_name + expose :file_path + expose :branch + expose :link do + expose :file_path, as: :url + expose :markdown do |_entity| + self.markdown_link + end + end + + def filename + object.file_name + end + + def secure_url + object.file_path + end + end + class UserSafe < Grape::Entity expose :id, :name, :username end @@ -31,7 +55,7 @@ module API class User < UserBasic expose :created_at, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) } - expose :bio, :location, :skype, :linkedin, :twitter, :website_url, :organization + expose :bio, :location, :public_email, :skype, :linkedin, :twitter, :website_url, :organization end class UserActivity < Grape::Entity @@ -83,6 +107,7 @@ module API expose :project_id, :issues_events, :confidential_issues_events expose :note_events, :confidential_note_events, :pipeline_events, :wiki_page_events expose :job_events + expose :push_events_branch_filter end class SharedGroup < Grape::Entity @@ -145,6 +170,7 @@ module API expose :namespace, using: 'API::Entities::NamespaceBasic' expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes + # rubocop: disable CodeReuse/ActiveRecord def self.preload_relation(projects_relation, options = {}) # Preloading tags, should be done with using only `:tags`, # as `:tags` are defined as: `has_many :tags, through: :taggings` @@ -154,6 +180,7 @@ module API .preload(:import_state, :tags) .preload(namespace: [:route, :owner]) end + # rubocop: enable CodeReuse/ActiveRecord end class Project < BasicProjectDetails @@ -224,6 +251,7 @@ module API expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics + # rubocop: disable CodeReuse/ActiveRecord def self.preload_relation(projects_relation, options = {}) # Preloading tags, should be done with using only `:tags`, # as `:tags` are defined as: `has_many :tags, through: :taggings` @@ -235,6 +263,7 @@ module API forked_project_link: :forked_from_project, forked_from_project: [:route, :forks, :tags, namespace: :route]) end + # rubocop: enable CodeReuse/ActiveRecord def self.forks_counting_projects(projects_relation) projects_relation + projects_relation.map(&:forked_from_project).compact @@ -370,6 +399,10 @@ module API expose :can_push do |repo_branch, options| Gitlab::UserAccess.new(options[:current_user], project: options[:project]).can_push_to_branch?(repo_branch.name) end + + expose :default do |repo_branch, options| + options[:project].default_branch == repo_branch.name + end end class TreeObject < Grape::Entity @@ -531,10 +564,12 @@ module API expose :total_time_spent, as: :human_total_time_spent end + # rubocop: disable CodeReuse/ActiveRecord def total_time_spent # Avoids an N+1 query since timelogs are preloaded object.timelogs.map(&:time_spent).sum end + # rubocop: enable CodeReuse/ActiveRecord end class ExternalIssue < Grape::Entity @@ -661,6 +696,8 @@ module API expose :diff_refs, using: Entities::DiffRefs + expose :diverged_commits_count, as: :diverged_commits_count, if: -> (_, options) { options[:include_diverged_commits_count] } + def build_available?(options) options[:project]&.feature_available?(:builds, options[:current_user]) end @@ -907,6 +944,7 @@ module API end end + # rubocop: disable CodeReuse/ActiveRecord def self.preload_relation(projects_relation, options = {}) relation = super(projects_relation, options) @@ -931,6 +969,7 @@ module API relation end + # rubocop: enable CodeReuse/ActiveRecord end class LabelBasic < Grape::Entity @@ -1037,9 +1076,11 @@ module API options[:project].repository.commit(repo_tag.dereferenced_target) end + # rubocop: disable CodeReuse/ActiveRecord expose :release, using: Entities::Release do |repo_tag, options| options[:project].releases.find_by(tag: repo_tag.name) end + # rubocop: enable CodeReuse/ActiveRecord end class Runner < Grape::Entity @@ -1062,6 +1103,7 @@ module API expose :version, :revision, :platform, :architecture expose :contacted_at expose :token, if: lambda { |runner, options| options[:current_user].admin? || !runner.instance_type? } + # rubocop: disable CodeReuse/ActiveRecord expose :projects, with: Entities::BasicProjectDetails do |runner, options| if options[:current_user].admin? runner.projects @@ -1069,6 +1111,8 @@ module API options[:current_user].authorized_projects.where(id: runner.projects) end end + # rubocop: enable CodeReuse/ActiveRecord + # rubocop: disable CodeReuse/ActiveRecord expose :groups, with: Entities::BasicGroupDetails do |runner, options| if options[:current_user].admin? runner.groups @@ -1076,6 +1120,7 @@ module API options[:current_user].authorized_groups.where(id: runner.groups) end end + # rubocop: enable CodeReuse/ActiveRecord end class RunnerRegistrationDetails < Grape::Entity @@ -1176,6 +1221,7 @@ module API end class TemplatesList < Grape::Entity + expose :key expose :name end @@ -1408,5 +1454,19 @@ module API badge.type == 'ProjectBadge' ? 'project' : 'group' end end + + class ResourceLabelEvent < Grape::Entity + expose :id + expose :user, using: Entities::UserBasic + expose :created_at + expose :resource_type do |event, options| + event.issuable.class.name + end + expose :resource_id do |event, options| + event.issuable.id + end + expose :label, using: Entities::LabelBasic + expose :action + end end end diff --git a/lib/api/environments.rb b/lib/api/environments.rb index fa828f43001..c64217a6977 100644 --- a/lib/api/environments.rb +++ b/lib/api/environments.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API # Environments RESTfull API endpoints class Environments < Grape::API diff --git a/lib/api/events.rb b/lib/api/events.rb index a415508a632..6e0b508be19 100644 --- a/lib/api/events.rb +++ b/lib/api/events.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Events < Grape::API include PaginationParams @@ -16,12 +18,29 @@ module API desc: 'Return events sorted in ascending and descending order' end - def present_events(events) + RedactedEvent = OpenStruct.new(target_title: 'Confidential event').freeze + + def redact_events(events) + events.map do |event| + if event.visible_to_user?(current_user) + event + else + RedactedEvent + end + end + end + + # rubocop: disable CodeReuse/ActiveRecord + def present_events(events, redact: true) events = events.reorder(created_at: params[:sort]) .with_associations - present paginate(events), with: Entities::Event + events = paginate(events) + events = redact_events(events) if redact + + present events, with: Entities::Event end + # rubocop: enable CodeReuse/ActiveRecord end resource :events do @@ -36,13 +55,16 @@ module API use :event_filter_params use :sort_params end + # rubocop: disable CodeReuse/ActiveRecord get do authenticate! events = EventsFinder.new(params.merge(source: current_user, current_user: current_user)).execute.preload(:author, :target) - present_events(events) + # Since we're viewing our own events, redaction is unnecessary + present_events(events, redact: false) end + # rubocop: enable CodeReuse/ActiveRecord end params do @@ -60,6 +82,7 @@ module API use :event_filter_params use :sort_params end + # rubocop: disable CodeReuse/ActiveRecord get ':id/events' do user = find_user(params[:id]) not_found!('User') unless user @@ -68,6 +91,7 @@ module API present_events(events) end + # rubocop: enable CodeReuse/ActiveRecord end params do @@ -82,11 +106,13 @@ module API use :event_filter_params use :sort_params end + # rubocop: disable CodeReuse/ActiveRecord get ":id/events" do events = EventsFinder.new(params.merge(source: user_project, current_user: current_user)).execute.preload(:author, :target) present_events(events) end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/features.rb b/lib/api/features.rb index 11d848584d9..6f2422af13a 100644 --- a/lib/api/features.rb +++ b/lib/api/features.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Features < Grape::API before { authenticated_as_admin! } @@ -14,6 +16,7 @@ module API end end + # rubocop: disable CodeReuse/ActiveRecord def gate_targets(params) targets = [] targets << Feature.group(params[:feature_group]) if params[:feature_group] @@ -21,6 +24,7 @@ module API targets end + # rubocop: enable CodeReuse/ActiveRecord end resource :features do diff --git a/lib/api/files.rb b/lib/api/files.rb index ff4f75c12df..bcd2cd48a45 100644 --- a/lib/api/files.rb +++ b/lib/api/files.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Files < Grape::API FILE_ENDPOINT_REQUIREMENTS = API::PROJECT_ENDPOINT_REQUIREMENTS.merge(file_path: API::NO_SLASH_URL_PART_REGEX) @@ -58,7 +60,7 @@ module API params :simple_file_params do requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb' - requires :branch, type: String, desc: 'Name of the branch to commit into. To create a new branch, also provide `start_branch`.' + requires :branch, type: String, desc: 'Name of the branch to commit into. To create a new branch, also provide `start_branch`.', allow_blank: false requires :commit_message, type: String, allow_blank: false, desc: 'Commit message' optional :start_branch, type: String, desc: 'Name of the branch to start the new commit from' optional :author_email, type: String, desc: 'The email of the author' @@ -80,7 +82,7 @@ module API desc 'Get raw file metadata from repository' params do requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb' - requires :ref, type: String, desc: 'The name of branch, tag or commit' + requires :ref, type: String, desc: 'The name of branch, tag or commit', allow_blank: false end head ":id/repository/files/:file_path/raw", requirements: FILE_ENDPOINT_REQUIREMENTS do assign_file_vars! @@ -91,7 +93,7 @@ module API desc 'Get raw file contents from the repository' params do requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb' - requires :ref, type: String, desc: 'The name of branch, tag commit' + requires :ref, type: String, desc: 'The name of branch, tag commit', allow_blank: false end get ":id/repository/files/:file_path/raw", requirements: FILE_ENDPOINT_REQUIREMENTS do assign_file_vars! @@ -104,7 +106,7 @@ module API desc 'Get file metadata from repository' params do requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb' - requires :ref, type: String, desc: 'The name of branch, tag or commit' + requires :ref, type: String, desc: 'The name of branch, tag or commit', allow_blank: false end head ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do assign_file_vars! @@ -115,7 +117,7 @@ module API desc 'Get a file from the repository' params do requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb' - requires :ref, type: String, desc: 'The name of branch, tag or commit' + requires :ref, type: String, desc: 'The name of branch, tag or commit', allow_blank: false end get ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do assign_file_vars! diff --git a/lib/api/group_boards.rb b/lib/api/group_boards.rb index 3832cdc10a8..dc30e868e2e 100644 --- a/lib/api/group_boards.rb +++ b/lib/api/group_boards.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class GroupBoards < Grape::API include BoardsResponses diff --git a/lib/api/group_milestones.rb b/lib/api/group_milestones.rb index 4b4352c2b27..b36436dbf43 100644 --- a/lib/api/group_milestones.rb +++ b/lib/api/group_milestones.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class GroupMilestones < Grape::API include MilestoneResponses diff --git a/lib/api/group_variables.rb b/lib/api/group_variables.rb index 55d5c7f1606..ae7241e9a30 100644 --- a/lib/api/group_variables.rb +++ b/lib/api/group_variables.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class GroupVariables < Grape::API include PaginationParams @@ -27,6 +29,7 @@ module API params do requires :key, type: String, desc: 'The key of the variable' end + # rubocop: disable CodeReuse/ActiveRecord get ':id/variables/:key' do key = params[:key] variable = user_group.variables.find_by(key: key) @@ -35,6 +38,7 @@ module API present variable, with: Entities::Variable end + # rubocop: enable CodeReuse/ActiveRecord desc 'Create a new variable in a group' do success Entities::Variable @@ -64,6 +68,7 @@ module API optional :value, type: String, desc: 'The value of the variable' optional :protected, type: String, desc: 'Whether the variable is protected' end + # rubocop: disable CodeReuse/ActiveRecord put ':id/variables/:key' do variable = user_group.variables.find_by(key: params[:key]) @@ -77,6 +82,7 @@ module API render_validation_error!(variable) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Delete an existing variable from a group' do success Entities::Variable @@ -84,12 +90,14 @@ module API params do requires :key, type: String, desc: 'The key of the variable' end + # rubocop: disable CodeReuse/ActiveRecord delete ':id/variables/:key' do variable = user_group.variables.find_by(key: params[:key]) not_found!('GroupVariable') unless variable destroy_conditionally!(variable) end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/groups.rb b/lib/api/groups.rb index b4f441f6a4f..64b998ab455 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Groups < Grape::API include PaginationParams @@ -38,6 +40,7 @@ module API use :pagination end + # rubocop: disable CodeReuse/ActiveRecord def find_groups(params, parent_id = nil) find_params = params.slice(:all_available, :custom_attributes, :owned, :min_access_level) find_params[:parent] = find_group!(parent_id) if parent_id @@ -53,6 +56,7 @@ module API groups end + # rubocop: enable CodeReuse/ActiveRecord def find_group_projects(params) group = find_group!(params[:id]) diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index be17653dbb2..a7ba8066233 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module Helpers include Gitlab::Utils @@ -94,6 +96,7 @@ module API LabelsFinder.new(current_user, search_params).execute end + # rubocop: disable CodeReuse/ActiveRecord def find_user(id) if id =~ /^\d+$/ User.find_by(id: id) @@ -101,14 +104,19 @@ module API User.find_by(username: id) end end + # rubocop: enable CodeReuse/ActiveRecord + # rubocop: disable CodeReuse/ActiveRecord def find_project(id) + projects = Project.without_deleted + if id.is_a?(Integer) || id =~ /^\d+$/ - Project.find_by(id: id) + projects.find_by(id: id) elsif id.include?("/") - Project.find_by_full_path(id) + projects.find_by_full_path(id) end end + # rubocop: enable CodeReuse/ActiveRecord def find_project!(id) project = find_project(id) @@ -120,6 +128,7 @@ module API end end + # rubocop: disable CodeReuse/ActiveRecord def find_group(id) if id.to_s =~ /^\d+$/ Group.find_by(id: id) @@ -127,6 +136,7 @@ module API Group.find_by_full_path(id) end end + # rubocop: enable CodeReuse/ActiveRecord def find_group!(id) group = find_group(id) @@ -138,6 +148,7 @@ module API end end + # rubocop: disable CodeReuse/ActiveRecord def find_namespace(id) if id.to_s =~ /^\d+$/ Namespace.find_by(id: id) @@ -145,6 +156,7 @@ module API Namespace.find_by_full_path(id) end end + # rubocop: enable CodeReuse/ActiveRecord def find_namespace!(id) namespace = find_namespace(id) @@ -156,6 +168,12 @@ module API end end + def find_branch!(branch_name) + user_project.repository.find_branch(branch_name) || not_found!('Branch') + rescue Gitlab::Git::CommandError + render_api_error!('The branch refname is invalid', 400) + end + def find_project_label(id) labels = available_labels_for(user_project) label = labels.find_by_id(id) || labels.find_by_title(id) @@ -163,13 +181,17 @@ module API label || not_found!('Label') end + # rubocop: disable CodeReuse/ActiveRecord def find_project_issue(iid) IssuesFinder.new(current_user, project_id: user_project.id).find_by!(iid: iid) end + # rubocop: enable CodeReuse/ActiveRecord + # rubocop: disable CodeReuse/ActiveRecord def find_project_merge_request(iid) MergeRequestsFinder.new(current_user, project_id: user_project.id).find_by!(iid: iid) end + # rubocop: enable CodeReuse/ActiveRecord def find_project_commit(id) user_project.commit_by(oid: id) @@ -180,11 +202,13 @@ module API SnippetsFinder.new(current_user, finder_params).find(id) end + # rubocop: disable CodeReuse/ActiveRecord def find_merge_request_with_access(iid, access_level = :read_merge_request) merge_request = user_project.merge_requests.find_by!(iid: iid) authorize! access_level, merge_request merge_request end + # rubocop: enable CodeReuse/ActiveRecord def find_build!(id) user_project.builds.find(id.to_i) @@ -276,9 +300,11 @@ module API Gitlab.rails5? ? permitted_attrs.to_h : permitted_attrs end + # rubocop: disable CodeReuse/ActiveRecord def filter_by_iid(items, iid) items.where(iid: iid) end + # rubocop: enable CodeReuse/ActiveRecord def filter_by_search(items, text) items.search(text) @@ -357,9 +383,10 @@ module API # lifted from https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb#L60 trace = exception.backtrace - message = "\n#{exception.class} (#{exception.message}):\n" + message = ["\n#{exception.class} (#{exception.message}):\n"] message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code) message << " " << trace.join("\n ") + message = message.join API.logger.add Logger::FATAL, message @@ -375,12 +402,14 @@ module API # project helpers + # rubocop: disable CodeReuse/ActiveRecord def reorder_projects(projects) projects.reorder(params[:order_by] => params[:sort]) end + # rubocop: enable CodeReuse/ActiveRecord def project_finder_params - finder_params = {} + finder_params = { without_deleted: true } finder_params[:owned] = true if params[:owned].present? finder_params[:non_public] = true if params[:membership].present? finder_params[:starred] = true if params[:starred].present? diff --git a/lib/api/helpers/badges_helpers.rb b/lib/api/helpers/badges_helpers.rb index 1f8afbf3c90..46ce5b4e7b5 100644 --- a/lib/api/helpers/badges_helpers.rb +++ b/lib/api/helpers/badges_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module Helpers module BadgesHelpers diff --git a/lib/api/helpers/common_helpers.rb b/lib/api/helpers/common_helpers.rb index 9993caa5249..7551ca50a7f 100644 --- a/lib/api/helpers/common_helpers.rb +++ b/lib/api/helpers/common_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module Helpers module CommonHelpers diff --git a/lib/api/helpers/custom_attributes.rb b/lib/api/helpers/custom_attributes.rb index 10d652e33f5..88208226c40 100644 --- a/lib/api/helpers/custom_attributes.rb +++ b/lib/api/helpers/custom_attributes.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module Helpers module CustomAttributes @@ -12,6 +14,7 @@ module API desc: 'Filter with custom attributes' end + # rubocop: disable CodeReuse/ActiveRecord def with_custom_attributes(collection_or_resource, options = {}) options = options.merge( with_custom_attributes: params[:with_custom_attributes] && @@ -24,6 +27,7 @@ module API [collection_or_resource, options] end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/helpers/custom_validators.rb b/lib/api/helpers/custom_validators.rb index dd4f6c41131..23b1cd1ad45 100644 --- a/lib/api/helpers/custom_validators.rb +++ b/lib/api/helpers/custom_validators.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module Helpers module CustomValidators diff --git a/lib/api/helpers/headers_helpers.rb b/lib/api/helpers/headers_helpers.rb index c9c44e3c218..7553af9d156 100644 --- a/lib/api/helpers/headers_helpers.rb +++ b/lib/api/helpers/headers_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module Helpers module HeadersHelpers diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb index 83151be82ad..4eaaca96b49 100644 --- a/lib/api/helpers/internal_helpers.rb +++ b/lib/api/helpers/internal_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module Helpers module InternalHelpers diff --git a/lib/api/helpers/members_helpers.rb b/lib/api/helpers/members_helpers.rb index fed8846e505..73d58ee7f37 100644 --- a/lib/api/helpers/members_helpers.rb +++ b/lib/api/helpers/members_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # rubocop:disable GitlabSecurity/PublicSend module API @@ -17,6 +19,7 @@ module API .non_request end + # rubocop: disable CodeReuse/ActiveRecord def find_all_members_for_project(project) shared_group_ids = project.project_group_links.pluck(:group_id) project_group_ids = project.group&.self_and_ancestors&.pluck(:id) @@ -28,13 +31,16 @@ module API .where(project_authorizations: { project_id: project.id }) .where(source_id: source_ids) end + # rubocop: enable CodeReuse/ActiveRecord + # rubocop: disable CodeReuse/ActiveRecord def find_all_members_for_group(group) source_ids = group.self_and_ancestors.pluck(:id) Member.includes(:user) .where(source_id: source_ids) .where(source_type: 'Namespace') end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/helpers/notes_helpers.rb b/lib/api/helpers/notes_helpers.rb index 7b1f5c2584b..216b2c45741 100644 --- a/lib/api/helpers/notes_helpers.rb +++ b/lib/api/helpers/notes_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module Helpers module NotesHelpers diff --git a/lib/api/helpers/pagination.rb b/lib/api/helpers/pagination.rb index 3308212216e..d311cbb5f7e 100644 --- a/lib/api/helpers/pagination.rb +++ b/lib/api/helpers/pagination.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module Helpers module Pagination @@ -91,6 +93,7 @@ module API @request_context = request_context end + # rubocop: disable CodeReuse/ActiveRecord def paginate(relation) pagination = KeysetPaginationInfo.new(relation, request_context) @@ -112,6 +115,7 @@ module API paged_relation end + # rubocop: enable CodeReuse/ActiveRecord private @@ -183,6 +187,7 @@ module API private + # rubocop: disable CodeReuse/ActiveRecord def add_default_order(relation) if relation.is_a?(ActiveRecord::Relation) && relation.order_values.empty? relation = relation.order(:id) @@ -190,6 +195,7 @@ module API relation end + # rubocop: enable CodeReuse/ActiveRecord def add_pagination_headers(paginated_data) header 'X-Per-Page', paginated_data.limit_value.to_s diff --git a/lib/api/helpers/project_snapshots_helpers.rb b/lib/api/helpers/project_snapshots_helpers.rb index 94798a8cb51..1b5dc281e38 100644 --- a/lib/api/helpers/project_snapshots_helpers.rb +++ b/lib/api/helpers/project_snapshots_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module Helpers module ProjectSnapshotsHelpers diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb index 381d5e8968c..e6a72b949f9 100644 --- a/lib/api/helpers/projects_helpers.rb +++ b/lib/api/helpers/projects_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module Helpers module ProjectsHelpers @@ -26,6 +28,7 @@ module API optional :avatar, type: File, desc: 'Avatar image for project' optional :printing_merge_request_link_enabled, type: Boolean, desc: 'Show link to create/view merge request when pushing from the command line' optional :merge_method, type: String, values: %w(ff rebase_merge merge), desc: 'The merge method used when merging merge requests' + optional :initialize_with_readme, type: Boolean, desc: "Initialize a project with a README.md" end params :optional_project_params do diff --git a/lib/api/helpers/related_resources_helpers.rb b/lib/api/helpers/related_resources_helpers.rb index bc7333ca4b3..793ae11b41d 100644 --- a/lib/api/helpers/related_resources_helpers.rb +++ b/lib/api/helpers/related_resources_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module Helpers module RelatedResourcesHelpers diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb index 61eb88d3331..45d0343bc89 100644 --- a/lib/api/helpers/runner.rb +++ b/lib/api/helpers/runner.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module Helpers module Runner diff --git a/lib/api/internal.rb b/lib/api/internal.rb index 516f25db15b..6a264c4cc6d 100644 --- a/lib/api/internal.rb +++ b/lib/api/internal.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API # Internal access API class Internal < Grape::API @@ -6,8 +8,17 @@ module API helpers ::API::Helpers::InternalHelpers helpers ::Gitlab::Identifier + UNKNOWN_CHECK_RESULT_ERROR = 'Unknown check result'.freeze + + helpers do + def response_with_status(code: 200, success: true, message: nil, **extra_options) + status code + { status: success, message: message }.merge(extra_options).compact + end + end + namespace 'internal' do - # Check if git command is allowed to project + # Check if git command is allowed for project # # Params: # key_id - ssh key id for Git over SSH @@ -17,9 +28,8 @@ module API # project - project full_path (not path on disk) # action - git action (git-upload-pack or git-receive-pack) # changes - changes as "oldrev newrev ref", see Gitlab::ChangesList + # rubocop: disable CodeReuse/ActiveRecord post "/allowed" do - status 200 - # Stores some Git-specific env thread-safely env = parse_env Gitlab::Git::HookEnv.set(gl_repository, env) if project @@ -49,29 +59,49 @@ module API namespace_path: namespace_path, project_path: project_path, redirected_path: redirected_path) - begin - access_checker.check(params[:action], params[:changes]) - @project ||= access_checker.project - rescue Gitlab::GitAccess::UnauthorizedError, Gitlab::GitAccess::NotFoundError => e - break { status: false, message: e.message } - end + check_result = begin + result = access_checker.check(params[:action], params[:changes]) + @project ||= access_checker.project + result + rescue Gitlab::GitAccess::UnauthorizedError => e + break response_with_status(code: 401, success: false, message: e.message) + rescue Gitlab::GitAccess::NotFoundError => e + break response_with_status(code: 404, success: false, message: e.message) + end log_user_activity(actor) - { - status: true, - gl_repository: gl_repository, - gl_id: Gitlab::GlId.gl_id(user), - gl_username: user&.username, - - # This repository_path is a bogus value but gitlab-shell still requires - # its presence. https://gitlab.com/gitlab-org/gitlab-shell/issues/135 - repository_path: '/', + case check_result + when ::Gitlab::GitAccessResult::Success + payload = { + gl_repository: gl_repository, + gl_id: Gitlab::GlId.gl_id(user), + gl_username: user&.username, + git_config_options: [], + + # This repository_path is a bogus value but gitlab-shell still requires + # its presence. https://gitlab.com/gitlab-org/gitlab-shell/issues/135 + repository_path: '/', + + gitaly: gitaly_payload(params[:action]) + } + + # Custom option for git-receive-pack command + receive_max_input_size = Gitlab::CurrentSettings.receive_max_input_size.to_i + if receive_max_input_size > 0 + payload[:git_config_options] << "receive.maxInputSize=#{receive_max_input_size.megabytes}" + end - gitaly: gitaly_payload(params[:action]) - } + response_with_status(**payload) + when ::Gitlab::GitAccessResult::CustomAction + response_with_status(code: 300, message: check_result.message, payload: check_result.payload) + else + response_with_status(code: 500, success: false, message: UNKNOWN_CHECK_RESULT_ERROR) + end end + # rubocop: enable CodeReuse/ActiveRecord + # rubocop: disable CodeReuse/ActiveRecord post "/lfs_authenticate" do status 200 @@ -93,6 +123,7 @@ module API repository_http_path: project.http_url_to_repo } end + # rubocop: enable CodeReuse/ActiveRecord get "/merge_request_urls" do merge_request_urls @@ -101,6 +132,7 @@ module API # # Get a ssh key using the fingerprint # + # rubocop: disable CodeReuse/ActiveRecord get "/authorized_keys" do fingerprint = params.fetch(:fingerprint) do Gitlab::InsecureKeyFingerprint.new(params.fetch(:key)).fingerprint @@ -109,10 +141,12 @@ module API not_found!("Key") if key.nil? present key, with: Entities::SSHKey end + # rubocop: enable CodeReuse/ActiveRecord # # Discover user by ssh key, user id or username # + # rubocop: disable CodeReuse/ActiveRecord get "/discover" do if params[:key_id] key = Key.find(params[:key_id]) @@ -125,6 +159,7 @@ module API present user, with: Entities::UserSafe end + # rubocop: enable CodeReuse/ActiveRecord get "/check" do { @@ -151,6 +186,7 @@ module API end end + # rubocop: disable CodeReuse/ActiveRecord post '/two_factor_recovery_codes' do status 200 @@ -192,6 +228,7 @@ module API { success: true, recovery_codes: codes } end + # rubocop: enable CodeReuse/ActiveRecord post '/pre_receive' do status 200 diff --git a/lib/api/issues.rb b/lib/api/issues.rb index cedfd2fbaa0..25d78053c88 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Issues < Grape::API include PaginationParams @@ -7,6 +9,7 @@ module API helpers ::Gitlab::IssuableMetadata helpers do + # rubocop: disable CodeReuse/ActiveRecord def find_issues(args = {}) args = declared_params.merge(args) @@ -20,6 +23,7 @@ module API issues.reorder(args[:order_by] => args[:sort]) end + # rubocop: enable CodeReuse/ActiveRecord params :issues_params do optional :labels, type: String, desc: 'Comma-separated list of label names' @@ -207,6 +211,7 @@ module API at_least_one_of :title, :description, :assignee_ids, :assignee_id, :milestone_id, :discussion_locked, :labels, :created_at, :due_date, :confidential, :state_event end + # rubocop: disable CodeReuse/ActiveRecord put ':id/issues/:issue_iid' do Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42322') @@ -234,6 +239,7 @@ module API render_validation_error!(issue) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Move an existing issue' do success Entities::Issue @@ -242,6 +248,7 @@ module API requires :issue_iid, type: Integer, desc: 'The internal ID of a project issue' requires :to_project_id, type: Integer, desc: 'The ID of the new project' end + # rubocop: disable CodeReuse/ActiveRecord post ':id/issues/:issue_iid/move' do Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42323') @@ -258,11 +265,13 @@ module API render_api_error!(error.message, 400) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Delete a project issue' params do requires :issue_iid, type: Integer, desc: 'The internal ID of a project issue' end + # rubocop: disable CodeReuse/ActiveRecord delete ":id/issues/:issue_iid" do issue = user_project.issues.find_by(iid: params[:issue_iid]) not_found!('Issue') unless issue @@ -273,6 +282,7 @@ module API Issuable::DestroyService.new(user_project, current_user).execute(issue) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'List merge requests closing issue' do success Entities::MergeRequestBasic @@ -280,6 +290,7 @@ module API params do requires :issue_iid, type: Integer, desc: 'The internal ID of a project issue' end + # rubocop: disable CodeReuse/ActiveRecord get ':id/issues/:issue_iid/closed_by' do issue = find_project_issue(params[:issue_iid]) @@ -288,6 +299,7 @@ module API present paginate(merge_requests), with: Entities::MergeRequestBasic, current_user: current_user, project: user_project end + # rubocop: enable CodeReuse/ActiveRecord desc 'List participants for an issue' do success Entities::UserBasic diff --git a/lib/api/job_artifacts.rb b/lib/api/job_artifacts.rb index 32379d7c8ab..2229cbcd9d4 100644 --- a/lib/api/job_artifacts.rb +++ b/lib/api/job_artifacts.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class JobArtifacts < Grape::API before { authenticate_non_get! } @@ -21,6 +23,7 @@ module API requires :job, type: String, desc: 'The name for the job' end route_setting :authentication, job_token_allowed: true + # rubocop: disable CodeReuse/ActiveRecord get ':id/jobs/artifacts/:ref_name/download', requirements: { ref_name: /.+/ } do authorize_download_artifacts! @@ -30,6 +33,7 @@ module API present_carrierwave_file!(latest_build.artifacts_file) end + # rubocop: enable CodeReuse/ActiveRecord desc 'Download the artifacts archive from a job' do detail 'This feature was introduced in GitLab 8.5' diff --git a/lib/api/jobs.rb b/lib/api/jobs.rb index fc8c52085ab..fa992b9a440 100644 --- a/lib/api/jobs.rb +++ b/lib/api/jobs.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Jobs < Grape::API include PaginationParams @@ -34,6 +36,7 @@ module API use :optional_scope use :pagination end + # rubocop: disable CodeReuse/ActiveRecord get ':id/jobs' do builds = user_project.builds.order('id DESC') builds = filter_builds(builds, params[:scope]) @@ -41,6 +44,7 @@ module API builds = builds.preload(:user, :job_artifacts_archive, :job_artifacts, :runner, pipeline: :project) present paginate(builds), with: Entities::Job end + # rubocop: enable CodeReuse/ActiveRecord desc 'Get pipeline jobs' do success Entities::Job @@ -50,6 +54,7 @@ module API use :optional_scope use :pagination end + # rubocop: disable CodeReuse/ActiveRecord get ':id/pipelines/:pipeline_id/jobs' do pipeline = user_project.pipelines.find(params[:pipeline_id]) builds = pipeline.builds @@ -58,6 +63,7 @@ module API present paginate(builds), with: Entities::Job end + # rubocop: enable CodeReuse/ActiveRecord desc 'Get a specific job of a project' do success Entities::Job @@ -145,7 +151,7 @@ module API present build, with: Entities::Job end - desc 'Trigger a manual job' do + desc 'Trigger a actionable job (manual, scheduled, etc)' do success Entities::Job detail 'This feature was added in GitLab 8.11' end @@ -168,6 +174,7 @@ module API end helpers do + # rubocop: disable CodeReuse/ActiveRecord def filter_builds(builds, scope) return builds if scope.nil? || scope.empty? @@ -178,6 +185,7 @@ module API builds.where(status: available_statuses && scope) end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/keys.rb b/lib/api/keys.rb index fd93f797f72..d5280a0035d 100644 --- a/lib/api/keys.rb +++ b/lib/api/keys.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API # Keys API class Keys < Grape::API diff --git a/lib/api/labels.rb b/lib/api/labels.rb index 81eaf56e48e..28555454307 100644 --- a/lib/api/labels.rb +++ b/lib/api/labels.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Labels < Grape::API include PaginationParams @@ -27,6 +29,7 @@ module API optional :description, type: String, desc: 'The description of label to be created' optional :priority, type: Integer, desc: 'The priority of the label', allow_blank: true end + # rubocop: disable CodeReuse/ActiveRecord post ':id/labels' do authorize! :admin_label, user_project @@ -43,6 +46,7 @@ module API render_validation_error!(label) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Delete an existing label' do success Entities::Label @@ -50,6 +54,7 @@ module API params do requires :name, type: String, desc: 'The name of the label to be deleted' end + # rubocop: disable CodeReuse/ActiveRecord delete ':id/labels' do authorize! :admin_label, user_project @@ -58,6 +63,7 @@ module API destroy_conditionally!(label) end + # rubocop: enable CodeReuse/ActiveRecord desc 'Update an existing label. At least one optional parameter is required.' do success Entities::Label @@ -70,6 +76,7 @@ module API optional :priority, type: Integer, desc: 'The priority of the label', allow_blank: true at_least_one_of :new_name, :color, :description, :priority end + # rubocop: disable CodeReuse/ActiveRecord put ':id/labels' do authorize! :admin_label, user_project @@ -95,6 +102,7 @@ module API present label, with: Entities::Label, current_user: current_user, project: user_project end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/lint.rb b/lib/api/lint.rb index d202eaa4c49..0342a4b6654 100644 --- a/lib/api/lint.rb +++ b/lib/api/lint.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Lint < Grape::API namespace :ci do diff --git a/lib/api/markdown.rb b/lib/api/markdown.rb index 5d55224c1a7..de77bef43ce 100644 --- a/lib/api/markdown.rb +++ b/lib/api/markdown.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Markdown < Grape::API params do @@ -10,7 +12,8 @@ module API detail "This feature was introduced in GitLab 11.0." end post do - context = { only_path: false } + context = { only_path: false, current_user: current_user } + context[:pipeline] = params[:gfm] ? :full : :plain_markdown if params[:project] project = Project.find_by_full_path(params[:project]) @@ -22,9 +25,7 @@ module API context[:skip_project_check] = true end - context[:pipeline] = params[:gfm] ? :full : :plain_markdown - - { html: Banzai.render(params[:text], context) } + { html: Banzai.render_and_post_process(params[:text], context) } end end end diff --git a/lib/api/members.rb b/lib/api/members.rb index d23dd834c69..a8f67be3463 100644 --- a/lib/api/members.rb +++ b/lib/api/members.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Members < Grape::API include PaginationParams @@ -18,6 +20,7 @@ module API optional :query, type: String, desc: 'A query string to search for members' use :pagination end + # rubocop: disable CodeReuse/ActiveRecord get ":id/members" do source = find_source(source_type, params[:id]) @@ -27,6 +30,7 @@ module API present members, with: Entities::Member end + # rubocop: enable CodeReuse/ActiveRecord desc 'Gets a list of group or project members viewable by the authenticated user, including those who gained membership through ancestor group.' do success Entities::Member @@ -35,6 +39,7 @@ module API optional :query, type: String, desc: 'A query string to search for members' use :pagination end + # rubocop: disable CodeReuse/ActiveRecord get ":id/members/all" do source = find_source(source_type, params[:id]) @@ -44,6 +49,7 @@ module API present members, with: Entities::Member end + # rubocop: enable CodeReuse/ActiveRecord desc 'Gets a member of a group or project.' do success Entities::Member @@ -51,6 +57,7 @@ module API params do requires :user_id, type: Integer, desc: 'The user ID of the member' end + # rubocop: disable CodeReuse/ActiveRecord get ":id/members/:user_id" do source = find_source(source_type, params[:id]) @@ -59,6 +66,7 @@ module API present member, with: Entities::Member end + # rubocop: enable CodeReuse/ActiveRecord desc 'Adds a member to a group or project.' do success Entities::Member @@ -68,6 +76,7 @@ module API requires :access_level, type: Integer, desc: 'A valid access level (defaults: `30`, developer access level)' optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY' end + # rubocop: disable CodeReuse/ActiveRecord post ":id/members" do source = find_source(source_type, params[:id]) authorize_admin_source!(source_type, source) @@ -88,6 +97,7 @@ module API render_validation_error!(member) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Updates a member of a group or project.' do success Entities::Member @@ -97,6 +107,7 @@ module API requires :access_level, type: Integer, desc: 'A valid access level' optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY' end + # rubocop: disable CodeReuse/ActiveRecord put ":id/members/:user_id" do source = find_source(source_type, params.delete(:id)) authorize_admin_source!(source_type, source) @@ -113,11 +124,13 @@ module API render_validation_error!(updated_member) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Removes a user from a group or project.' params do requires :user_id, type: Integer, desc: 'The user ID of the member' end + # rubocop: disable CodeReuse/ActiveRecord delete ":id/members/:user_id" do source = find_source(source_type, params[:id]) member = source.members.find_by!(user_id: params[:user_id]) @@ -126,6 +139,7 @@ module API ::Members::DestroyService.new(current_user).execute(member) end end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/merge_request_diffs.rb b/lib/api/merge_request_diffs.rb index 95ef8f42954..e4fb890960a 100644 --- a/lib/api/merge_request_diffs.rb +++ b/lib/api/merge_request_diffs.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API # MergeRequestDiff API class MergeRequestDiffs < Grape::API diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index abad418771c..440d94ae186 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class MergeRequests < Grape::API include PaginationParams @@ -28,9 +30,9 @@ module API end helpers do + # rubocop: disable CodeReuse/ActiveRecord def find_merge_requests(args = {}) args = declared_params.merge(args) - args[:milestone_title] = args.delete(:milestone) args[:label_name] = args.delete(:labels) args[:scope] = args[:scope].underscore if args[:scope] @@ -45,6 +47,7 @@ module API merge_requests .preload(:notes, :author, :assignee, :milestone, :latest_merge_request_diff, :labels, :timelogs) end + # rubocop: enable CodeReuse/ActiveRecord def merge_request_pipelines_with_access authorize! :read_pipeline, user_project @@ -93,6 +96,7 @@ module API optional :source_branch, type: String, desc: 'Return merge requests with the given source branch' optional :target_branch, type: String, desc: 'Return merge requests with the given target branch' optional :search, type: String, desc: 'Search merge requests for text present in the title or description' + optional :wip, type: String, values: %w[yes no], desc: 'Search merge requests for WIP in the title' use :pagination end end @@ -233,6 +237,7 @@ module API params do requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request' optional :render_html, type: Boolean, desc: 'Returns the description and title rendered HTML' + optional :include_diverged_commits_count, type: Boolean, desc: 'Returns the commits count behind the target branch' end desc 'Get a single merge request' do success Entities::MergeRequest @@ -240,7 +245,7 @@ module API get ':id/merge_requests/:merge_request_iid' do merge_request = find_merge_request_with_access(params[:merge_request_iid]) - present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project, render_html: params[:render_html] + present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project, render_html: params[:render_html], include_diverged_commits_count: params[:include_diverged_commits_count] end desc 'Get the participants of a merge request' do diff --git a/lib/api/milestone_responses.rb b/lib/api/milestone_responses.rb index a8eb137e46a..a0ca39b69d4 100644 --- a/lib/api/milestone_responses.rb +++ b/lib/api/milestone_responses.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module MilestoneResponses extend ActiveSupport::Concern diff --git a/lib/api/namespaces.rb b/lib/api/namespaces.rb index 32b77aedba8..76639fbb031 100644 --- a/lib/api/namespaces.rb +++ b/lib/api/namespaces.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Namespaces < Grape::API include PaginationParams diff --git a/lib/api/notes.rb b/lib/api/notes.rb index 39923e6d5b5..9f323b87baf 100644 --- a/lib/api/notes.rb +++ b/lib/api/notes.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Notes < Grape::API include PaginationParams @@ -28,6 +30,7 @@ module API desc: 'Return notes sorted in `asc` or `desc` order.' use :pagination end + # rubocop: disable CodeReuse/ActiveRecord get ":id/#{noteables_str}/:noteable_id/notes" do noteable = find_noteable(parent_type, noteables_str, params[:noteable_id]) @@ -45,6 +48,7 @@ module API .reject { |n| n.cross_reference_not_visible_for?(current_user) } present notes, with: Entities::Note end + # rubocop: enable CodeReuse/ActiveRecord desc "Get a single #{noteable_type.to_s.downcase} note" do success Entities::Note diff --git a/lib/api/notification_settings.rb b/lib/api/notification_settings.rb index bf0d6b9e434..4d9a4629268 100644 --- a/lib/api/notification_settings.rb +++ b/lib/api/notification_settings.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API # notification_settings API class NotificationSettings < Grape::API diff --git a/lib/api/pages_domains.rb b/lib/api/pages_domains.rb index ba33993d852..c9ad47e0f0d 100644 --- a/lib/api/pages_domains.rb +++ b/lib/api/pages_domains.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class PagesDomains < Grape::API include PaginationParams @@ -13,9 +15,11 @@ module API end helpers do + # rubocop: disable CodeReuse/ActiveRecord def find_pages_domain! user_project.pages_domains.find_by(domain: params[:domain]) || not_found!('PagesDomain') end + # rubocop: enable CodeReuse/ActiveRecord def pages_domain @pages_domain ||= find_pages_domain! @@ -61,11 +65,13 @@ module API params do use :pagination end + # rubocop: disable CodeReuse/ActiveRecord get ":id/pages/domains" do authorize! :read_pages, user_project present paginate(user_project.pages_domains.order(:domain)), with: Entities::PagesDomain end + # rubocop: enable CodeReuse/ActiveRecord desc 'Get a single pages domain' do success Entities::PagesDomain diff --git a/lib/api/pagination_params.rb b/lib/api/pagination_params.rb index f566eb3ed2b..ae03595eb25 100644 --- a/lib/api/pagination_params.rb +++ b/lib/api/pagination_params.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API # Concern for declare pagination params. # diff --git a/lib/api/pipeline_schedules.rb b/lib/api/pipeline_schedules.rb index 37f32411296..ed0a38b9d70 100644 --- a/lib/api/pipeline_schedules.rb +++ b/lib/api/pipeline_schedules.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class PipelineSchedules < Grape::API include PaginationParams @@ -16,6 +18,7 @@ module API optional :scope, type: String, values: %w[active inactive], desc: 'The scope of pipeline schedules' end + # rubocop: disable CodeReuse/ActiveRecord get ':id/pipeline_schedules' do authorize! :read_pipeline_schedule, user_project @@ -23,6 +26,7 @@ module API .preload([:owner, :last_pipeline]) present paginate(schedules), with: Entities::PipelineSchedule end + # rubocop: enable CodeReuse/ActiveRecord desc 'Get a single pipeline schedule' do success Entities::PipelineScheduleDetails @@ -39,7 +43,7 @@ module API end params do requires :description, type: String, desc: 'The description of pipeline schedule' - requires :ref, type: String, desc: 'The branch/tag name will be triggered' + requires :ref, type: String, desc: 'The branch/tag name will be triggered', allow_blank: false requires :cron, type: String, desc: 'The cron' optional :cron_timezone, type: String, default: 'UTC', desc: 'The timezone' optional :active, type: Boolean, default: true, desc: 'The activation of pipeline schedule' @@ -161,6 +165,7 @@ module API end helpers do + # rubocop: disable CodeReuse/ActiveRecord def pipeline_schedule @pipeline_schedule ||= user_project @@ -172,7 +177,9 @@ module API end end end + # rubocop: enable CodeReuse/ActiveRecord + # rubocop: disable CodeReuse/ActiveRecord def pipeline_schedule_variable @pipeline_schedule_variable ||= pipeline_schedule.variables.find_by(key: params[:key]).tap do |pipeline_schedule_variable| @@ -181,6 +188,7 @@ module API end end end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/pipelines.rb b/lib/api/pipelines.rb index 5d33a13d035..1cfb982c04b 100644 --- a/lib/api/pipelines.rb +++ b/lib/api/pipelines.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Pipelines < Grape::API include PaginationParams @@ -43,6 +45,7 @@ module API requires :ref, type: String, desc: 'Reference' optional :variables, Array, desc: 'Array of variables available in the pipeline' end + # rubocop: disable CodeReuse/ActiveRecord post ':id/pipeline' do Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42124') @@ -63,6 +66,7 @@ module API render_validation_error!(new_pipeline) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Gets a specific pipeline for the project' do detail 'This feature was introduced in GitLab 8.11' diff --git a/lib/api/project_export.rb b/lib/api/project_export.rb index 15c57a2fc02..e34ed0bdb44 100644 --- a/lib/api/project_export.rb +++ b/lib/api/project_export.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class ProjectExport < Grape::API before do @@ -21,12 +23,8 @@ module API detail 'This feature was introduced in GitLab 10.6.' end get ':id/export/download' do - path = user_project.export_project_path - - if path - present_disk_file!(path, File.basename(path), 'application/gzip') - elsif user_project.export_project_object_exists? - present_carrierwave_file!(user_project.import_export_upload.export_file) + if user_project.export_file_exists? + present_carrierwave_file!(user_project.export_file) else render_api_error!('404 Not found or has expired', 404) end diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb index 4760a1c08d7..4af4c6ac593 100644 --- a/lib/api/project_hooks.rb +++ b/lib/api/project_hooks.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class ProjectHooks < Grape::API include PaginationParams @@ -20,6 +22,7 @@ module API optional :wiki_page_events, type: Boolean, desc: "Trigger hook on wiki events" optional :enable_ssl_verification, type: Boolean, desc: "Do SSL verification when triggering the hook" optional :token, type: String, desc: "Secret token to validate received payloads; this will not be returned in the response" + optional :push_events_branch_filter, type: String, desc: "Trigger hook on specified branch only" end end @@ -63,6 +66,7 @@ module API present hook, with: Entities::ProjectHook else error!("Invalid url given", 422) if hook.errors[:url].present? + error!("Invalid branch filter given", 422) if hook.errors[:push_events_branch_filter].present? not_found!("Project hook #{hook.errors.messages}") end @@ -84,6 +88,7 @@ module API present hook, with: Entities::ProjectHook else error!("Invalid url given", 422) if hook.errors[:url].present? + error!("Invalid branch filter given", 422) if hook.errors[:push_events_branch_filter].present? not_found!("Project hook #{hook.errors.messages}") end diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb index bc5152e539f..cbfa0c5bc1c 100644 --- a/lib/api/project_import.rb +++ b/lib/api/project_import.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class ProjectImport < Grape::API include PaginationParams diff --git a/lib/api/project_milestones.rb b/lib/api/project_milestones.rb index 72cf32d7717..c7137ba5217 100644 --- a/lib/api/project_milestones.rb +++ b/lib/api/project_milestones.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class ProjectMilestones < Grape::API include PaginationParams diff --git a/lib/api/project_snapshots.rb b/lib/api/project_snapshots.rb index 71005acc587..175fbb2ce92 100644 --- a/lib/api/project_snapshots.rb +++ b/lib/api/project_snapshots.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class ProjectSnapshots < Grape::API helpers ::API::Helpers::ProjectSnapshotsHelpers diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb index 0ada0ef4708..f3a1b73b153 100644 --- a/lib/api/project_snippets.rb +++ b/lib/api/project_snippets.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class ProjectSnippets < Grape::API include PaginationParams @@ -85,6 +87,7 @@ module API desc: 'The visibility of the snippet' at_least_one_of :title, :file_name, :code, :visibility_level end + # rubocop: disable CodeReuse/ActiveRecord put ":id/snippets/:snippet_id" do snippet = snippets_for_current_user.find_by(id: params.delete(:snippet_id)) not_found!('Snippet') unless snippet @@ -107,11 +110,13 @@ module API render_validation_error!(snippet) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Delete a project snippet' params do requires :snippet_id, type: Integer, desc: 'The ID of a project snippet' end + # rubocop: disable CodeReuse/ActiveRecord delete ":id/snippets/:snippet_id" do snippet = snippets_for_current_user.find_by(id: params[:snippet_id]) not_found!('Snippet') unless snippet @@ -120,11 +125,13 @@ module API destroy_conditionally!(snippet) end + # rubocop: enable CodeReuse/ActiveRecord desc 'Get a raw project snippet' params do requires :snippet_id, type: Integer, desc: 'The ID of a project snippet' end + # rubocop: disable CodeReuse/ActiveRecord get ":id/snippets/:snippet_id/raw" do snippet = snippets_for_current_user.find_by(id: params[:snippet_id]) not_found!('Snippet') unless snippet @@ -133,6 +140,7 @@ module API content_type 'text/plain' present snippet.content end + # rubocop: enable CodeReuse/ActiveRecord desc 'Get the user agent details for a project snippet' do success Entities::UserAgentDetail @@ -140,6 +148,7 @@ module API params do requires :snippet_id, type: Integer, desc: 'The ID of a project snippet' end + # rubocop: disable CodeReuse/ActiveRecord get ":id/snippets/:snippet_id/user_agent_detail" do authenticated_as_admin! @@ -149,6 +158,7 @@ module API present snippet.user_agent_detail, with: Entities::UserAgentDetail end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/project_templates.rb b/lib/api/project_templates.rb new file mode 100644 index 00000000000..d05ddad7466 --- /dev/null +++ b/lib/api/project_templates.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +module API + class ProjectTemplates < Grape::API + include PaginationParams + + TEMPLATE_TYPES = %w[dockerfiles gitignores gitlab_ci_ymls licenses].freeze + + before { authenticate_non_get! } + + params do + requires :id, type: String, desc: 'The ID of a project' + requires :type, type: String, values: TEMPLATE_TYPES, desc: 'The type (dockerfiles|gitignores|gitlab_ci_ymls|licenses) of the template' + end + resource :projects do + desc 'Get a list of templates available to this project' do + detail 'This endpoint was introduced in GitLab 11.4' + end + params do + use :pagination + end + get ':id/templates/:type' do + templates = TemplateFinder + .build(params[:type], user_project) + .execute + + present paginate(::Kaminari.paginate_array(templates)), with: Entities::TemplatesList + end + + desc 'Download a template available to this project' do + detail 'This endpoint was introduced in GitLab 11.4' + end + params do + requires :name, type: String, desc: 'The name of the template' + + optional :project, type: String, desc: 'The project name to use when expanding placeholders in the template. Only affects licenses' + optional :fullname, type: String, desc: 'The full name of the copyright holder to use when expanding placeholders in the template. Only affects licenses' + end + get ':id/templates/:type/:name', requirements: { name: /[\w\.-]+/ } do + template = TemplateFinder + .build(params[:type], user_project, name: params[:name]) + .execute + + not_found!('Template') unless template.present? + + template.resolve!( + project_name: params[:project].presence, + fullname: params[:fullname].presence || current_user&.name + ) + + if template.is_a?(::LicenseTemplate) + present template, with: Entities::License + else + present template, with: Entities::Template + end + end + end + end +end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 2801ae918c6..ae2d327e45b 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_dependency 'declarative_policy' module API @@ -198,6 +200,7 @@ module API use :optional_project_params use :create_params end + # rubocop: disable CodeReuse/ActiveRecord post "user/:user_id" do authenticated_as_admin! user = User.find_by(id: params.delete(:user_id)) @@ -214,6 +217,7 @@ module API render_validation_error!(project) end end + # rubocop: enable CodeReuse/ActiveRecord end params do @@ -283,6 +287,12 @@ module API present_projects forks end + desc 'Check pages access of this project' + get ':id/pages_access' do + authorize! :read_pages_content, user_project unless user_project.public_pages? + status 200 + end + desc 'Update an existing project' do success Entities::Project end @@ -444,6 +454,7 @@ module API params do requires :group_id, type: Integer, desc: 'The ID of the group' end + # rubocop: disable CodeReuse/ActiveRecord delete ":id/share/:group_id" do authorize! :admin_project, user_project @@ -452,6 +463,7 @@ module API destroy_conditionally!(link) end + # rubocop: enable CodeReuse/ActiveRecord desc 'Upload a file' params do diff --git a/lib/api/projects_relation_builder.rb b/lib/api/projects_relation_builder.rb index 9fd79c491c2..8edcfea7c93 100644 --- a/lib/api/projects_relation_builder.rb +++ b/lib/api/projects_relation_builder.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module ProjectsRelationBuilder extend ActiveSupport::Concern diff --git a/lib/api/protected_branches.rb b/lib/api/protected_branches.rb index a30eb46c220..47752f40e58 100644 --- a/lib/api/protected_branches.rb +++ b/lib/api/protected_branches.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class ProtectedBranches < Grape::API include PaginationParams @@ -16,11 +18,13 @@ module API params do use :pagination end + # rubocop: disable CodeReuse/ActiveRecord get ':id/protected_branches' do protected_branches = user_project.protected_branches.preload(:push_access_levels, :merge_access_levels) present paginate(protected_branches), with: Entities::ProtectedBranch, project: user_project end + # rubocop: enable CodeReuse/ActiveRecord desc 'Get a single protected branch' do success Entities::ProtectedBranch @@ -28,11 +32,13 @@ module API params do requires :name, type: String, desc: 'The name of the branch or wildcard' end + # rubocop: disable CodeReuse/ActiveRecord get ':id/protected_branches/:name', requirements: BRANCH_ENDPOINT_REQUIREMENTS do protected_branch = user_project.protected_branches.find_by!(name: params[:name]) present protected_branch, with: Entities::ProtectedBranch, project: user_project end + # rubocop: enable CodeReuse/ActiveRecord desc 'Protect a single branch or wildcard' do success Entities::ProtectedBranch @@ -40,12 +46,13 @@ module API params do requires :name, type: String, desc: 'The name of the protected branch' optional :push_access_level, type: Integer, - values: ProtectedRefAccess::ALLOWED_ACCESS_LEVELS, + values: ProtectedBranch::PushAccessLevel.allowed_access_levels, desc: 'Access levels allowed to push (defaults: `40`, maintainer access level)' optional :merge_access_level, type: Integer, - values: ProtectedRefAccess::ALLOWED_ACCESS_LEVELS, + values: ProtectedBranch::MergeAccessLevel.allowed_access_levels, desc: 'Access levels allowed to merge (defaults: `40`, maintainer access level)' end + # rubocop: disable CodeReuse/ActiveRecord post ':id/protected_branches' do protected_branch = user_project.protected_branches.find_by(name: params[:name]) if protected_branch @@ -62,11 +69,13 @@ module API render_api_error!(protected_branch.errors.full_messages, 422) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Unprotect a single branch' params do requires :name, type: String, desc: 'The name of the protected branch' end + # rubocop: disable CodeReuse/ActiveRecord delete ':id/protected_branches/:name', requirements: BRANCH_ENDPOINT_REQUIREMENTS do protected_branch = user_project.protected_branches.find_by!(name: params[:name]) @@ -75,6 +84,7 @@ module API destroy_service.execute(protected_branch) end end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/protected_tags.rb b/lib/api/protected_tags.rb index bf0a7184e1c..ed1c5f0cc05 100644 --- a/lib/api/protected_tags.rb +++ b/lib/api/protected_tags.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class ProtectedTags < Grape::API include PaginationParams @@ -17,11 +19,13 @@ module API params do use :pagination end + # rubocop: disable CodeReuse/ActiveRecord get ':id/protected_tags' do protected_tags = user_project.protected_tags.preload(:create_access_levels) present paginate(protected_tags), with: Entities::ProtectedTag, project: user_project end + # rubocop: enable CodeReuse/ActiveRecord desc 'Get a single protected tag' do detail 'This feature was introduced in GitLab 11.3.' @@ -30,11 +34,13 @@ module API params do requires :name, type: String, desc: 'The name of the tag or wildcard' end + # rubocop: disable CodeReuse/ActiveRecord get ':id/protected_tags/:name', requirements: TAG_ENDPOINT_REQUIREMENTS do protected_tag = user_project.protected_tags.find_by!(name: params[:name]) present protected_tag, with: Entities::ProtectedTag, project: user_project end + # rubocop: enable CodeReuse/ActiveRecord desc 'Protect a single tag or wildcard' do detail 'This feature was introduced in GitLab 11.3.' @@ -43,7 +49,7 @@ module API params do requires :name, type: String, desc: 'The name of the protected tag' optional :create_access_level, type: Integer, default: Gitlab::Access::MAINTAINER, - values: ProtectedRefAccess::ALLOWED_ACCESS_LEVELS, + values: ProtectedTag::CreateAccessLevel.allowed_access_levels, desc: 'Access levels allowed to create (defaults: `40`, maintainer access level)' end post ':id/protected_tags' do @@ -69,11 +75,13 @@ module API params do requires :name, type: String, desc: 'The name of the protected tag' end + # rubocop: disable CodeReuse/ActiveRecord delete ':id/protected_tags/:name', requirements: TAG_ENDPOINT_REQUIREMENTS do protected_tag = user_project.protected_tags.find_by!(name: params[:name]) destroy_conditionally!(protected_tag) end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb index 79736107bbb..5125f302fbb 100644 --- a/lib/api/repositories.rb +++ b/lib/api/repositories.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'mime/types' module API diff --git a/lib/api/resource_label_events.rb b/lib/api/resource_label_events.rb new file mode 100644 index 00000000000..b6fbe8c0235 --- /dev/null +++ b/lib/api/resource_label_events.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module API + class ResourceLabelEvents < Grape::API + include PaginationParams + helpers ::API::Helpers::NotesHelpers + + before { authenticate! } + + EVENTABLE_TYPES = [Issue, MergeRequest].freeze + + EVENTABLE_TYPES.each do |eventable_type| + parent_type = eventable_type.parent_class.to_s.underscore + eventables_str = eventable_type.to_s.underscore.pluralize + + params do + requires :id, type: String, desc: "The ID of a #{parent_type}" + end + resource parent_type.pluralize.to_sym, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do + desc "Get a list of #{eventable_type.to_s.downcase} resource label events" do + success Entities::ResourceLabelEvent + detail 'This feature was introduced in 11.3' + end + params do + requires :eventable_id, types: [Integer, String], desc: 'The ID of the eventable' + use :pagination + end + + # rubocop: disable CodeReuse/ActiveRecord + get ":id/#{eventables_str}/:eventable_id/resource_label_events" do + eventable = find_noteable(parent_type, eventables_str, params[:eventable_id]) + events = eventable.resource_label_events.includes(:label, :user) + + present paginate(events), with: Entities::ResourceLabelEvent + end + # rubocop: enable CodeReuse/ActiveRecord + + desc "Get a single #{eventable_type.to_s.downcase} resource label event" do + success Entities::ResourceLabelEvent + detail 'This feature was introduced in 11.3' + end + params do + requires :event_id, type: String, desc: 'The ID of a resource label event' + requires :eventable_id, types: [Integer, String], desc: 'The ID of the eventable' + end + get ":id/#{eventables_str}/:eventable_id/resource_label_events/:event_id" do + eventable = find_noteable(parent_type, eventables_str, params[:eventable_id]) + event = eventable.resource_label_events.find(params[:event_id]) + + present event, with: Entities::ResourceLabelEvent + end + end + end + end +end diff --git a/lib/api/runner.rb b/lib/api/runner.rb index c9931c2d603..d8768a54986 100644 --- a/lib/api/runner.rb +++ b/lib/api/runner.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Runner < Grape::API helpers ::API::Helpers::Runner @@ -17,6 +19,7 @@ module API optional :tag_list, type: Array[String], desc: %q(List of Runner's tags) optional :maximum_timeout, type: Integer, desc: 'Maximum timeout set when this Runner will handle the job' end + # rubocop: disable CodeReuse/ActiveRecord post '/' do attributes = attributes_for_keys([:description, :active, :locked, :run_untagged, :tag_list, :maximum_timeout]) .merge(get_runner_details_from_request) @@ -43,6 +46,7 @@ module API render_validation_error!(runner) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Deletes a registered Runner' do http_codes [[204, 'Runner was deleted'], [403, 'Forbidden']] diff --git a/lib/api/runners.rb b/lib/api/runners.rb index 51242341dba..ce70460af11 100644 --- a/lib/api/runners.rb +++ b/lib/api/runners.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Runners < Grape::API include PaginationParams @@ -9,12 +11,20 @@ module API success Entities::Runner end params do - optional :scope, type: String, values: %w[active paused online], + optional :scope, type: String, values: Ci::Runner::AVAILABLE_STATUSES, desc: 'The scope of specific runners to show' + optional :type, type: String, values: Ci::Runner::AVAILABLE_TYPES, + desc: 'The type of the runners to show' + optional :status, type: String, values: Ci::Runner::AVAILABLE_STATUSES, + desc: 'The status of the runners to show' use :pagination end get do - runners = filter_runners(current_user.ci_owned_runners, params[:scope], without: %w(specific shared)) + runners = current_user.ci_owned_runners + runners = filter_runners(runners, params[:scope], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES) + runners = filter_runners(runners, params[:type], allowed_scopes: Ci::Runner::AVAILABLE_TYPES) + runners = filter_runners(runners, params[:status], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES) + present paginate(runners), with: Entities::Runner end @@ -22,13 +32,22 @@ module API success Entities::Runner end params do - optional :scope, type: String, values: %w[active paused online specific shared], + optional :scope, type: String, values: Ci::Runner::AVAILABLE_SCOPES, desc: 'The scope of specific runners to show' + optional :type, type: String, values: Ci::Runner::AVAILABLE_TYPES, + desc: 'The type of the runners to show' + optional :status, type: String, values: Ci::Runner::AVAILABLE_STATUSES, + desc: 'The status of the runners to show' use :pagination end get 'all' do authenticated_as_admin! - runners = filter_runners(Ci::Runner.all, params[:scope]) + + runners = Ci::Runner.all + runners = filter_runners(runners, params[:scope]) + runners = filter_runners(runners, params[:type], allowed_scopes: Ci::Runner::AVAILABLE_TYPES) + runners = filter_runners(runners, params[:status], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES) + present paginate(runners), with: Entities::Runner end @@ -94,7 +113,7 @@ module API optional :status, type: String, desc: 'Status of the job', values: Ci::Build::AVAILABLE_STATUSES use :pagination end - get ':id/jobs' do + get ':id/jobs' do runner = get_runner(params[:id]) authenticate_list_runners_jobs!(runner) @@ -114,12 +133,20 @@ module API success Entities::Runner end params do - optional :scope, type: String, values: %w[active paused online specific shared], + optional :scope, type: String, values: Ci::Runner::AVAILABLE_SCOPES, desc: 'The scope of specific runners to show' + optional :type, type: String, values: Ci::Runner::AVAILABLE_TYPES, + desc: 'The type of the runners to show' + optional :status, type: String, values: Ci::Runner::AVAILABLE_STATUSES, + desc: 'The status of the runners to show' use :pagination end get ':id/runners' do - runners = filter_runners(Ci::Runner.owned_or_instance_wide(user_project.id), params[:scope]) + runners = Ci::Runner.owned_or_instance_wide(user_project.id) + runners = filter_runners(runners, params[:scope]) + runners = filter_runners(runners, params[:type], allowed_scopes: Ci::Runner::AVAILABLE_TYPES) + runners = filter_runners(runners, params[:status], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES) + present paginate(runners), with: Entities::Runner end @@ -146,6 +173,7 @@ module API params do requires :runner_id, type: Integer, desc: 'The ID of the runner' end + # rubocop: disable CodeReuse/ActiveRecord delete ':id/runners/:runner_id' do runner_project = user_project.runner_projects.find_by(runner_id: params[:runner_id]) not_found!('Runner') unless runner_project @@ -155,18 +183,14 @@ module API destroy_conditionally!(runner_project) end + # rubocop: enable CodeReuse/ActiveRecord end helpers do - def filter_runners(runners, scope, options = {}) + def filter_runners(runners, scope, allowed_scopes: ::Ci::Runner::AVAILABLE_SCOPES) return runners unless scope.present? - available_scopes = ::Ci::Runner::AVAILABLE_SCOPES - if options[:without] - available_scopes = available_scopes - options[:without] - end - - if (available_scopes & [scope]).empty? + unless allowed_scopes.include?(scope) render_api_error!('Scope contains invalid value', 400) end diff --git a/lib/api/scope.rb b/lib/api/scope.rb index d5165b2e482..707775e5d15 100644 --- a/lib/api/scope.rb +++ b/lib/api/scope.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Encapsulate a scope used for authorization, such as `api`, or `read_user` module API class Scope diff --git a/lib/api/search.rb b/lib/api/search.rb index 37fbabe419c..12d97dcfe7f 100644 --- a/lib/api/search.rb +++ b/lib/api/search.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Search < Grape::API include PaginationParams diff --git a/lib/api/services.rb b/lib/api/services.rb index d1a5ee7db35..0ae05ce08f1 100644 --- a/lib/api/services.rb +++ b/lib/api/services.rb @@ -821,11 +821,13 @@ module API TRIGGER_SERVICES.each do |service_slug, settings| helpers do + # rubocop: disable CodeReuse/ActiveRecord def slash_command_service(project, service_slug, params) project.services.active.where(template: false).find do |service| service.try(:token) == params[:token] && service.to_param == service_slug.underscore end end + # rubocop: enable CodeReuse/ActiveRecord end params do diff --git a/lib/api/settings.rb b/lib/api/settings.rb index 897010217dc..edbd134822c 100644 --- a/lib/api/settings.rb +++ b/lib/api/settings.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Settings < Grape::API before { authenticated_as_admin! } @@ -102,7 +104,7 @@ module API end optional :repository_checks_enabled, type: Boolean, desc: "GitLab will periodically run 'git fsck' in all project and wiki repositories to look for silent disk corruption issues." optional :repository_storages, type: Array[String], desc: 'Storage paths for new projects' - optional :require_two_factor_authentication, type: Boolean, desc: 'Require all users to setup Two-factor authentication' + optional :require_two_factor_authentication, type: Boolean, desc: 'Require all users to set up Two-factor authentication' given require_two_factor_authentication: ->(val) { val } do requires :two_factor_grace_period, type: Integer, desc: 'Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication' end @@ -117,11 +119,6 @@ module API given shared_runners_enabled: ->(val) { val } do requires :shared_runners_text, type: String, desc: 'Shared runners text ' end - optional :sidekiq_throttling_enabled, type: Boolean, desc: 'Enable Sidekiq Job Throttling' - given sidekiq_throttling_enabled: ->(val) { val } do - requires :sidekiq_throttling_factor, type: Float, desc: 'The factor by which the queues should be throttled. A value between 0.0 and 1.0, exclusive.' - requires :sidekiq_throttling_queues, type: Array[String], desc: 'Choose which queues you wish to throttle' - end optional :sign_in_text, type: String, desc: 'The sign in text of the GitLab application' optional :signin_enabled, type: Boolean, desc: 'Flag indicating if password authentication is enabled for the web interface' # support legacy names, can be removed in v5 optional :signup_enabled, type: Boolean, desc: 'Flag indicating if sign up is enabled' diff --git a/lib/api/sidekiq_metrics.rb b/lib/api/sidekiq_metrics.rb index 11f2b40269a..daa9598a204 100644 --- a/lib/api/sidekiq_metrics.rb +++ b/lib/api/sidekiq_metrics.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'sidekiq/api' module API diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb index b30305b4bc9..f1786c15f4f 100644 --- a/lib/api/snippets.rb +++ b/lib/api/snippets.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API # Snippets API class Snippets < Grape::API @@ -92,6 +94,7 @@ module API desc: 'The visibility of the snippet' at_least_one_of :title, :file_name, :content, :visibility end + # rubocop: disable CodeReuse/ActiveRecord put ':id' do snippet = snippets_for_current_user.find_by(id: params.delete(:id)) break not_found!('Snippet') unless snippet @@ -110,6 +113,7 @@ module API render_validation_error!(snippet) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Remove snippet' do detail 'This feature was introduced in GitLab 8.15.' @@ -118,6 +122,7 @@ module API params do requires :id, type: Integer, desc: 'The ID of a snippet' end + # rubocop: disable CodeReuse/ActiveRecord delete ':id' do snippet = snippets_for_current_user.find_by(id: params.delete(:id)) break not_found!('Snippet') unless snippet @@ -126,6 +131,7 @@ module API destroy_conditionally!(snippet) end + # rubocop: enable CodeReuse/ActiveRecord desc 'Get a raw snippet' do detail 'This feature was introduced in GitLab 8.15.' @@ -133,6 +139,7 @@ module API params do requires :id, type: Integer, desc: 'The ID of a snippet' end + # rubocop: disable CodeReuse/ActiveRecord get ":id/raw" do snippet = snippets_for_current_user.find_by(id: params.delete(:id)) break not_found!('Snippet') unless snippet @@ -141,6 +148,7 @@ module API content_type 'text/plain' present snippet.content end + # rubocop: enable CodeReuse/ActiveRecord desc 'Get the user agent details for a snippet' do success Entities::UserAgentDetail @@ -148,6 +156,7 @@ module API params do requires :id, type: Integer, desc: 'The ID of a snippet' end + # rubocop: disable CodeReuse/ActiveRecord get ":id/user_agent_detail" do authenticated_as_admin! @@ -157,6 +166,7 @@ module API present snippet.user_agent_detail, with: Entities::UserAgentDetail end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/subscriptions.rb b/lib/api/subscriptions.rb index b3e1e23031a..077e9373ac4 100644 --- a/lib/api/subscriptions.rb +++ b/lib/api/subscriptions.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Subscriptions < Grape::API before { authenticate! } diff --git a/lib/api/system_hooks.rb b/lib/api/system_hooks.rb index c7a460df46a..51fae0e54aa 100644 --- a/lib/api/system_hooks.rb +++ b/lib/api/system_hooks.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class SystemHooks < Grape::API include PaginationParams @@ -63,12 +65,14 @@ module API params do requires :id, type: Integer, desc: 'The ID of the system hook' end + # rubocop: disable CodeReuse/ActiveRecord delete ":id" do hook = SystemHook.find_by(id: params[:id]) not_found!('System hook') unless hook destroy_conditionally!(hook) end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/tags.rb b/lib/api/tags.rb index 5e0afc6a7e4..f739eacf9ba 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Tags < Grape::API include PaginationParams diff --git a/lib/api/templates.rb b/lib/api/templates.rb index 927baaea652..8dab19d50c2 100644 --- a/lib/api/templates.rb +++ b/lib/api/templates.rb @@ -1,18 +1,17 @@ +# frozen_string_literal: true + module API class Templates < Grape::API include PaginationParams GLOBAL_TEMPLATE_TYPES = { gitignores: { - klass: Gitlab::Template::GitignoreTemplate, gitlab_version: 8.8 }, gitlab_ci_ymls: { - klass: Gitlab::Template::GitlabCiYmlTemplate, gitlab_version: 8.9 }, dockerfiles: { - klass: Gitlab::Template::DockerfileTemplate, gitlab_version: 8.15 } }.freeze @@ -36,7 +35,7 @@ module API popular = declared(params)[:popular] popular = to_boolean(popular) if popular.present? - templates = LicenseTemplateFinder.new(popular: popular).execute + templates = TemplateFinder.build(:licenses, nil, popular: popular).execute present paginate(::Kaminari.paginate_array(templates)), with: ::API::Entities::License end @@ -49,8 +48,7 @@ module API requires :name, type: String, desc: 'The name of the template' end get "templates/licenses/:name", requirements: { name: /[\w\.-]+/ } do - templates = LicenseTemplateFinder.new.execute - template = templates.find { |template| template.key == params[:name] } + template = TemplateFinder.build(:licenses, nil, name: params[:name]).execute not_found!('License') unless template.present? @@ -63,7 +61,6 @@ module API end GLOBAL_TEMPLATE_TYPES.each do |template_type, properties| - klass = properties[:klass] gitlab_version = properties[:gitlab_version] desc 'Get the list of the available template' do @@ -74,7 +71,7 @@ module API use :pagination end get "templates/#{template_type}" do - templates = ::Kaminari.paginate_array(klass.all) + templates = ::Kaminari.paginate_array(TemplateFinder.build(template_type, nil).execute) present paginate(templates), with: Entities::TemplatesList end @@ -86,7 +83,8 @@ module API requires :name, type: String, desc: 'The name of the template' end get "templates/#{template_type}/:name" do - new_template = klass.find(declared(params)[:name]) + finder = TemplateFinder.build(template_type, nil, name: declared(params)[:name]) + new_template = finder.execute render_response(template_type, new_template) end diff --git a/lib/api/time_tracking_endpoints.rb b/lib/api/time_tracking_endpoints.rb index 2bb451dea89..93fe06bec27 100644 --- a/lib/api/time_tracking_endpoints.rb +++ b/lib/api/time_tracking_endpoints.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API module TimeTrackingEndpoints extend ActiveSupport::Concern diff --git a/lib/api/todos.rb b/lib/api/todos.rb index c6dbcf84e3a..ed2cf2cc31b 100644 --- a/lib/api/todos.rb +++ b/lib/api/todos.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Todos < Grape::API include PaginationParams diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb index b29e660c6e0..f784c857883 100644 --- a/lib/api/triggers.rb +++ b/lib/api/triggers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Triggers < Grape::API include PaginationParams @@ -10,7 +12,7 @@ module API success Entities::Pipeline end params do - requires :ref, type: String, desc: 'The commit sha or name of a branch or tag' + requires :ref, type: String, desc: 'The commit sha or name of a branch or tag', allow_blank: false 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 @@ -42,6 +44,7 @@ module API params do use :pagination end + # rubocop: disable CodeReuse/ActiveRecord get ':id/triggers' do authenticate! authorize! :admin_build, user_project @@ -50,6 +53,7 @@ module API present paginate(triggers), with: Entities::Trigger end + # rubocop: enable CodeReuse/ActiveRecord desc 'Get specific trigger of a project' do success Entities::Trigger diff --git a/lib/api/users.rb b/lib/api/users.rb index b0811bb4aad..501c5cf1df3 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Users < Grape::API include PaginationParams @@ -14,11 +16,14 @@ module API end helpers do + # rubocop: disable CodeReuse/ActiveRecord def find_user_by_id(params) id = params[:user_id] || params[:id] User.find_by(id: id) || not_found!('User') end + # rubocop: enable CodeReuse/ActiveRecord + # rubocop: disable CodeReuse/ActiveRecord def reorder_users(users) if params[:order_by] && params[:sort] users.reorder(params[:order_by] => params[:sort]) @@ -26,6 +31,7 @@ module API users end end + # rubocop: enable CodeReuse/ActiveRecord params :optional_attributes do optional :skype, type: String, desc: 'The Skype username' @@ -38,12 +44,12 @@ module API optional :provider, type: String, desc: 'The external provider' optional :bio, type: String, desc: 'The biography of the user' optional :location, type: String, desc: 'The location of the user' + optional :public_email, type: String, desc: 'The public email of the user' optional :admin, type: Boolean, desc: 'Flag indicating the user is an administrator' optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups' optional :external, type: Boolean, desc: 'Flag indicating the user is an external user' optional :avatar, type: File, desc: 'Avatar image for user' optional :private_profile, type: Boolean, desc: 'Flag indicating the user has a private profile' - optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user' all_or_none_of :extern_uid, :provider end @@ -75,6 +81,7 @@ module API use :pagination use :with_custom_attributes end + # rubocop: disable CodeReuse/ActiveRecord get do authenticated_as_admin! if params[:external].present? || (params[:extern_uid].present? && params[:provider].present?) @@ -102,6 +109,7 @@ module API present paginate(users), options end + # rubocop: enable CodeReuse/ActiveRecord desc 'Get a single user' do success Entities::User @@ -111,6 +119,7 @@ module API use :with_custom_attributes end + # rubocop: disable CodeReuse/ActiveRecord get ":id" do user = User.find_by(id: params[:id]) not_found!('User') unless user && can?(current_user, :read_user, user) @@ -120,6 +129,7 @@ module API present user, opts end + # rubocop: enable CodeReuse/ActiveRecord desc "Get the status of a user" params do @@ -145,6 +155,7 @@ module API requires :username, type: String, desc: 'The username of the user' use :optional_attributes end + # rubocop: disable CodeReuse/ActiveRecord post do authenticated_as_admin! @@ -165,6 +176,7 @@ module API render_validation_error!(user) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Update a user. Available only for admins.' do success Entities::UserPublic @@ -178,6 +190,7 @@ module API optional :username, type: String, desc: 'The username of the user' use :optional_attributes end + # rubocop: disable CodeReuse/ActiveRecord put ":id" do authenticated_as_admin! @@ -216,6 +229,7 @@ module API render_validation_error!(user) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Add an SSH key to a specified user. Available only for admins.' do success Entities::SSHKey @@ -225,6 +239,7 @@ module API requires :key, type: String, desc: 'The new SSH key' requires :title, type: String, desc: 'The title of the new SSH key' end + # rubocop: disable CodeReuse/ActiveRecord post ":id/keys" do authenticated_as_admin! @@ -239,22 +254,23 @@ module API render_validation_error!(key) end end + # rubocop: enable CodeReuse/ActiveRecord - desc 'Get the SSH keys of a specified user. Available only for admins.' do + desc 'Get the SSH keys of a specified user.' do success Entities::SSHKey end params do requires :id, type: Integer, desc: 'The ID of the user' use :pagination end + # rubocop: disable CodeReuse/ActiveRecord get ':id/keys' do - authenticated_as_admin! - user = User.find_by(id: params[:id]) - not_found!('User') unless user + not_found!('User') unless user && can?(current_user, :read_user, user) present paginate(user.keys), with: Entities::SSHKey end + # rubocop: enable CodeReuse/ActiveRecord desc 'Delete an existing SSH key from a specified user. Available only for admins.' do success Entities::SSHKey @@ -263,6 +279,7 @@ module API requires :id, type: Integer, desc: 'The ID of the user' requires :key_id, type: Integer, desc: 'The ID of the SSH key' end + # rubocop: disable CodeReuse/ActiveRecord delete ':id/keys/:key_id' do authenticated_as_admin! @@ -274,6 +291,7 @@ module API destroy_conditionally!(key) end + # rubocop: enable CodeReuse/ActiveRecord desc 'Add a GPG key to a specified user. Available only for admins.' do detail 'This feature was added in GitLab 10.0' @@ -283,6 +301,7 @@ module API requires :id, type: Integer, desc: 'The ID of the user' requires :key, type: String, desc: 'The new GPG key' end + # rubocop: disable CodeReuse/ActiveRecord post ':id/gpg_keys' do authenticated_as_admin! @@ -297,6 +316,7 @@ module API render_validation_error!(key) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Get the GPG keys of a specified user. Available only for admins.' do detail 'This feature was added in GitLab 10.0' @@ -306,6 +326,7 @@ module API requires :id, type: Integer, desc: 'The ID of the user' use :pagination end + # rubocop: disable CodeReuse/ActiveRecord get ':id/gpg_keys' do authenticated_as_admin! @@ -314,6 +335,7 @@ module API present paginate(user.gpg_keys), with: Entities::GPGKey end + # rubocop: enable CodeReuse/ActiveRecord desc 'Delete an existing GPG key from a specified user. Available only for admins.' do detail 'This feature was added in GitLab 10.0' @@ -322,6 +344,7 @@ module API requires :id, type: Integer, desc: 'The ID of the user' requires :key_id, type: Integer, desc: 'The ID of the GPG key' end + # rubocop: disable CodeReuse/ActiveRecord delete ':id/gpg_keys/:key_id' do authenticated_as_admin! @@ -334,6 +357,7 @@ module API status 204 key.destroy end + # rubocop: enable CodeReuse/ActiveRecord desc 'Revokes an existing GPG key from a specified user. Available only for admins.' do detail 'This feature was added in GitLab 10.0' @@ -342,6 +366,7 @@ module API requires :id, type: Integer, desc: 'The ID of the user' requires :key_id, type: Integer, desc: 'The ID of the GPG key' end + # rubocop: disable CodeReuse/ActiveRecord post ':id/gpg_keys/:key_id/revoke' do authenticated_as_admin! @@ -354,6 +379,7 @@ module API key.revoke status :accepted end + # rubocop: enable CodeReuse/ActiveRecord desc 'Add an email address to a specified user. Available only for admins.' do success Entities::Email @@ -361,7 +387,9 @@ module API params do requires :id, type: Integer, desc: 'The ID of the user' requires :email, type: String, desc: 'The email of the user' + optional :skip_confirmation, type: Boolean, desc: 'Skip confirmation of email and assume it is verified' end + # rubocop: disable CodeReuse/ActiveRecord post ":id/emails" do authenticated_as_admin! @@ -376,6 +404,7 @@ module API render_validation_error!(email) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Get the emails addresses of a specified user. Available only for admins.' do success Entities::Email @@ -384,6 +413,7 @@ module API requires :id, type: Integer, desc: 'The ID of the user' use :pagination end + # rubocop: disable CodeReuse/ActiveRecord get ':id/emails' do authenticated_as_admin! user = User.find_by(id: params[:id]) @@ -391,6 +421,7 @@ module API present paginate(user.emails), with: Entities::Email end + # rubocop: enable CodeReuse/ActiveRecord desc 'Delete an email address of a specified user. Available only for admins.' do success Entities::Email @@ -399,6 +430,7 @@ module API requires :id, type: Integer, desc: 'The ID of the user' requires :email_id, type: Integer, desc: 'The ID of the email' end + # rubocop: disable CodeReuse/ActiveRecord delete ':id/emails/:email_id' do authenticated_as_admin! user = User.find_by(id: params[:id]) @@ -411,6 +443,7 @@ module API Emails::DestroyService.new(current_user, user: user).execute(email) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Delete a user. Available only for admins.' do success Entities::Email @@ -419,6 +452,7 @@ module API requires :id, type: Integer, desc: 'The ID of the user' optional :hard_delete, type: Boolean, desc: "Whether to remove a user's contributions" end + # rubocop: disable CodeReuse/ActiveRecord delete ":id" do Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42279') @@ -431,11 +465,13 @@ module API user.delete_async(deleted_by: current_user, params: params) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Block a user. Available only for admins.' params do requires :id, type: Integer, desc: 'The ID of the user' end + # rubocop: disable CodeReuse/ActiveRecord post ':id/block' do authenticated_as_admin! user = User.find_by(id: params[:id]) @@ -447,11 +483,13 @@ module API forbidden!('LDAP blocked users cannot be modified by the API') end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Unblock a user. Available only for admins.' params do requires :id, type: Integer, desc: 'The ID of the user' end + # rubocop: disable CodeReuse/ActiveRecord post ':id/unblock' do authenticated_as_admin! user = User.find_by(id: params[:id]) @@ -463,6 +501,7 @@ module API user.activate end end + # rubocop: enable CodeReuse/ActiveRecord params do requires :user_id, type: Integer, desc: 'The ID of the user' @@ -475,9 +514,11 @@ module API PersonalAccessTokensFinder.new({ user: user, impersonation: true }.merge(options)) end + # rubocop: disable CodeReuse/ActiveRecord def find_impersonation_token finder.find_by(id: declared_params[:impersonation_token_id]) || not_found!('Impersonation Token') end + # rubocop: enable CodeReuse/ActiveRecord end before { authenticated_as_admin! } @@ -578,12 +619,14 @@ module API params do requires :key_id, type: Integer, desc: 'The ID of the SSH key' end + # rubocop: disable CodeReuse/ActiveRecord get "keys/:key_id" do key = current_user.keys.find_by(id: params[:key_id]) not_found!('Key') unless key present key, with: Entities::SSHKey end + # rubocop: enable CodeReuse/ActiveRecord desc 'Add a new SSH key to the currently authenticated user' do success Entities::SSHKey @@ -608,12 +651,14 @@ module API params do requires :key_id, type: Integer, desc: 'The ID of the SSH key' end + # rubocop: disable CodeReuse/ActiveRecord delete "keys/:key_id" do key = current_user.keys.find_by(id: params[:key_id]) not_found!('Key') unless key destroy_conditionally!(key) end + # rubocop: enable CodeReuse/ActiveRecord desc "Get the currently authenticated user's GPG keys" do detail 'This feature was added in GitLab 10.0' @@ -633,12 +678,14 @@ module API params do requires :key_id, type: Integer, desc: 'The ID of the GPG key' end + # rubocop: disable CodeReuse/ActiveRecord get 'gpg_keys/:key_id' do key = current_user.gpg_keys.find_by(id: params[:key_id]) not_found!('GPG Key') unless key present key, with: Entities::GPGKey end + # rubocop: enable CodeReuse/ActiveRecord desc 'Add a new GPG key to the currently authenticated user' do detail 'This feature was added in GitLab 10.0' @@ -663,6 +710,7 @@ module API params do requires :key_id, type: Integer, desc: 'The ID of the GPG key' end + # rubocop: disable CodeReuse/ActiveRecord post 'gpg_keys/:key_id/revoke' do key = current_user.gpg_keys.find_by(id: params[:key_id]) not_found!('GPG Key') unless key @@ -670,6 +718,7 @@ module API key.revoke status :accepted end + # rubocop: enable CodeReuse/ActiveRecord desc 'Delete a GPG key from the currently authenticated user' do detail 'This feature was added in GitLab 10.0' @@ -677,6 +726,7 @@ module API params do requires :key_id, type: Integer, desc: 'The ID of the SSH key' end + # rubocop: disable CodeReuse/ActiveRecord delete 'gpg_keys/:key_id' do key = current_user.gpg_keys.find_by(id: params[:key_id]) not_found!('GPG Key') unless key @@ -684,6 +734,7 @@ module API status 204 key.destroy end + # rubocop: enable CodeReuse/ActiveRecord desc "Get the currently authenticated user's email addresses" do success Entities::Email @@ -701,12 +752,14 @@ module API params do requires :email_id, type: Integer, desc: 'The ID of the email' end + # rubocop: disable CodeReuse/ActiveRecord get "emails/:email_id" do email = current_user.emails.find_by(id: params[:email_id]) not_found!('Email') unless email present email, with: Entities::Email end + # rubocop: enable CodeReuse/ActiveRecord desc 'Add new email address to the currently authenticated user' do success Entities::Email @@ -728,6 +781,7 @@ module API params do requires :email_id, type: Integer, desc: 'The ID of the email' end + # rubocop: disable CodeReuse/ActiveRecord delete "emails/:email_id" do email = current_user.emails.find_by(id: params[:email_id]) not_found!('Email') unless email @@ -736,12 +790,14 @@ module API Emails::DestroyService.new(current_user, user: current_user).execute(email) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Get a list of user activities' params do optional :from, type: DateTime, default: 6.months.ago, desc: 'Date string in the format YEAR-MONTH-DAY' use :pagination end + # rubocop: disable CodeReuse/ActiveRecord get "activities" do authenticated_as_admin! @@ -751,6 +807,7 @@ module API present paginate(activities), with: Entities::UserActivity end + # rubocop: enable CodeReuse/ActiveRecord desc 'Set the status of the current user' do success Entities::UserStatus diff --git a/lib/api/variables.rb b/lib/api/variables.rb index a34de9410e8..c844ba321ed 100644 --- a/lib/api/variables.rb +++ b/lib/api/variables.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Variables < Grape::API include PaginationParams @@ -27,6 +29,7 @@ module API params do requires :key, type: String, desc: 'The key of the variable' end + # rubocop: disable CodeReuse/ActiveRecord get ':id/variables/:key' do key = params[:key] variable = user_project.variables.find_by(key: key) @@ -35,6 +38,7 @@ module API present variable, with: Entities::Variable end + # rubocop: enable CodeReuse/ActiveRecord desc 'Create a new variable in a project' do success Entities::Variable @@ -64,6 +68,7 @@ module API optional :value, type: String, desc: 'The value of the variable' optional :protected, type: String, desc: 'Whether the variable is protected' end + # rubocop: disable CodeReuse/ActiveRecord put ':id/variables/:key' do variable = user_project.variables.find_by(key: params[:key]) @@ -77,6 +82,7 @@ module API render_validation_error!(variable) end end + # rubocop: enable CodeReuse/ActiveRecord desc 'Delete an existing variable from a project' do success Entities::Variable @@ -84,6 +90,7 @@ module API params do requires :key, type: String, desc: 'The key of the variable' end + # rubocop: disable CodeReuse/ActiveRecord delete ':id/variables/:key' do variable = user_project.variables.find_by(key: params[:key]) not_found!('Variable') unless variable @@ -92,6 +99,7 @@ module API status 204 variable.destroy end + # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/version.rb b/lib/api/version.rb index 3b10bfa6a7d..74cd857f447 100644 --- a/lib/api/version.rb +++ b/lib/api/version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module API class Version < Grape::API before { authenticate! } diff --git a/lib/api/wikis.rb b/lib/api/wikis.rb index b3fc4e876ad..6e1d4eb335f 100644 --- a/lib/api/wikis.rb +++ b/lib/api/wikis.rb @@ -1,6 +1,16 @@ +# frozen_string_literal: true + module API class Wikis < Grape::API helpers do + def commit_params(attrs) + { + file_name: attrs[:file][:filename], + file_content: File.read(attrs[:file][:tempfile]), + branch_name: attrs[:branch] + } + end + params :wiki_page_params do requires :content, type: String, desc: 'Content of a wiki page' requires :title, type: String, desc: 'Title of a wiki page' @@ -84,6 +94,29 @@ module API status 204 WikiPages::DestroyService.new(user_project, current_user).execute(wiki_page) end + + desc 'Upload an attachment to the wiki repository' do + detail 'This feature was introduced in GitLab 11.3.' + success Entities::WikiAttachment + end + params do + requires :file, type: File, desc: 'The attachment file to be uploaded' + optional :branch, type: String, desc: 'The name of the branch' + end + post ":id/wikis/attachments", requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do + authorize! :create_wiki, user_project + + result = ::Wikis::CreateAttachmentService.new(user_project, + current_user, + commit_params(declared_params(include_missing: false))).execute + + if result[:status] == :success + status(201) + present OpenStruct.new(result[:result]), with: Entities::WikiAttachment + else + render_api_error!(result[:message], 400) + end + end end end end |