diff options
Diffstat (limited to 'lib/api')
41 files changed, 489 insertions, 372 deletions
diff --git a/lib/api/api.rb b/lib/api/api.rb index bf8ddba6f0d..20f8c637274 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -6,7 +6,7 @@ module API LOG_FILENAME = Rails.root.join("log", "api_json.log") - NO_SLASH_URL_PART_REGEX = %r{[^/]+} + NO_SLASH_URL_PART_REGEX = %r{[^/]+}.freeze NAMESPACE_OR_PROJECT_REQUIREMENTS = { id: NO_SLASH_URL_PART_REGEX }.freeze COMMIT_ENDPOINT_REQUIREMENTS = NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(sha: NO_SLASH_URL_PART_REGEX).freeze USER_REQUIREMENTS = { user_id: NO_SLASH_URL_PART_REGEX }.freeze @@ -98,7 +98,6 @@ module API mount ::API::Boards mount ::API::Branches mount ::API::BroadcastMessages - mount ::API::CircuitBreakers mount ::API::Commits mount ::API::CommitStatuses mount ::API::ContainerRegistry @@ -134,6 +133,7 @@ module API mount ::API::Pipelines mount ::API::PipelineSchedules mount ::API::ProjectClusters + mount ::API::ProjectEvents mount ::API::ProjectExport mount ::API::ProjectImport mount ::API::ProjectHooks diff --git a/lib/api/branches.rb b/lib/api/branches.rb index 5c98b0ad56c..65d7f68bbf9 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -162,8 +162,8 @@ module API result = DeleteBranchService.new(user_project, current_user) .execute(params[:branch]) - if result[:status] != :success - render_api_error!(result[:message], result[:return_code]) + if result.error? + render_api_error!(result.message, result.http_status) end end end diff --git a/lib/api/circuit_breakers.rb b/lib/api/circuit_breakers.rb deleted file mode 100644 index da756daadcc..00000000000 --- a/lib/api/circuit_breakers.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -module API - class CircuitBreakers < Grape::API - before { authenticated_as_admin! } - - resource :circuit_breakers do - params do - requires :type, - type: String, - desc: "The type of circuitbreaker", - values: ['repository_storage'] - end - resource ':type' do - namespace '', requirements: { type: 'repository_storage' } do - desc 'Get all git storages' do - detail 'This feature was introduced in GitLab 9.5' - end - get do - present [] - end - - desc 'Get all failing git storages' do - detail 'This feature was introduced in GitLab 9.5' - end - get 'failing' do - present [] - end - - desc 'Reset all storage failures and open circuitbreaker' do - detail 'This feature was introduced in GitLab 9.5' - end - delete do - end - end - end - end - end -end diff --git a/lib/api/commits.rb b/lib/api/commits.rb index 65eb9bfb87e..80913f4ca07 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -96,17 +96,27 @@ module API end end optional :start_branch, type: String, desc: 'Name of the branch to start the new commit from' + optional :start_project, types: [Integer, String], desc: 'The ID or path of the project to start the 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' optional :force, type: Boolean, default: false, desc: 'When `true` overwrites the target branch with a new commit based on the `start_branch`' end post ':id/repository/commits' do + if params[:start_project] + start_project = find_project!(params[:start_project]) + + unless user_project.forked_from?(start_project) + forbidden!("Project is not included in the fork network for #{start_project.full_name}") + end + end + authorize_push_to_branch!(params[:branch]) attrs = declared_params attrs[:branch_name] = attrs.delete(:branch) attrs[:start_branch] ||= attrs[:branch_name] + attrs[:start_project] = start_project if start_project result = ::Files::MultiService.new(user_project, current_user, attrs).execute diff --git a/lib/api/discussions.rb b/lib/api/discussions.rb index 8afe6dda414..693172b7d08 100644 --- a/lib/api/discussions.rb +++ b/lib/api/discussions.rb @@ -134,9 +134,13 @@ module API post ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id/notes" do noteable = find_noteable(parent_type, noteables_str, params[:noteable_id]) notes = readable_discussion_notes(noteable, params[:discussion_id]) + first_note = notes.first break not_found!("Discussion") if notes.empty? - break bad_request!("Discussion is an individual note.") unless notes.first.part_of_discussion? + + unless first_note.part_of_discussion? || first_note.to_discussion.can_convert_to_discussion? + break bad_request!("Discussion can not be replied to.") + end opts = { note: params[:body], @@ -202,7 +206,7 @@ module API delete_note(noteable, params[:note_id]) end - if Noteable::RESOLVABLE_TYPES.include?(noteable_type.to_s) + if Noteable.resolvable_types.include?(noteable_type.to_s) desc "Resolve/unresolve an existing #{noteable_type.to_s.downcase} discussion" do success Entities::Discussion end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 4bdac278add..8840accf675 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -239,6 +239,7 @@ module API end end + expose :empty_repo?, as: :empty_repo expose :archived?, as: :archived expose :visibility expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group } @@ -264,6 +265,7 @@ module API expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) } expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] } + expose :ci_default_git_depth expose :public_builds, as: :public_jobs expose :ci_config_path, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) } expose :shared_with_groups do |project, options| @@ -286,6 +288,7 @@ module API # N+1 is solved then by using `subject.tags.map(&:name)` # MR describing the solution: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20555 super(projects_relation).preload(:group) + .preload(:ci_cd_settings) .preload(project_group_links: { group: :route }, fork_network: :root_project, fork_network_member: :forked_from_project, @@ -302,6 +305,7 @@ module API expose :commit_count expose :storage_size expose :repository_size + expose :wiki_size expose :lfs_objects_size expose :build_artifacts_size, as: :job_artifacts_size end @@ -354,6 +358,7 @@ module API with_options format_with: -> (value) { value.to_i } do expose :storage_size expose :repository_size + expose :wiki_size expose :lfs_objects_size expose :build_artifacts_size, as: :job_artifacts_size end @@ -542,10 +547,15 @@ module API class IssueBasic < ProjectEntity expose :closed_at expose :closed_by, using: Entities::UserBasic - expose :labels do |issue| - # Avoids an N+1 query since labels are preloaded - issue.labels.map(&:title).sort + + expose :labels do |issue, options| + if options[:with_labels_details] + ::API::Entities::LabelBasic.represent(issue.labels.sort_by(&:title)) + else + issue.labels.map(&:title).sort + end end + expose :milestone, using: Entities::Milestone expose :assignees, :author, using: Entities::UserBasic @@ -568,11 +578,21 @@ module API expose :time_stats, using: 'API::Entities::IssuableTimeStats' do |issue| issue end + + expose :task_completion_status end class Issue < IssueBasic include ::API::Helpers::RelatedResourcesHelpers + expose(:has_tasks) do |issue, _| + !issue.task_list_items.empty? + end + + expose :task_status, if: -> (issue, _) do + !issue.task_list_items.empty? + end + expose :_links do expose :self do |issue| expose_url(api_v4_project_issue_path(id: issue.project_id, issue_iid: issue.iid)) @@ -708,6 +728,8 @@ module API end expose :squash + + expose :task_completion_status end class MergeRequest < MergeRequestBasic @@ -878,7 +900,7 @@ module API expose :push_event_payload, as: :push_data, using: PushEventPayload, - if: -> (event, _) { event.push? } + if: -> (event, _) { event.push_action? } expose :author_username do |event, options| event.author&.username @@ -922,7 +944,15 @@ module API end class NamespaceBasic < Grape::Entity - expose :id, :name, :path, :kind, :full_path, :parent_id + expose :id, :name, :path, :kind, :full_path, :parent_id, :avatar_url + + expose :web_url do |namespace| + if namespace.user? + Gitlab::Routing.url_helpers.user_url(namespace.owner) + else + namespace.web_url + end + end end class Namespace < NamespaceBasic @@ -1148,22 +1178,33 @@ module API end end - class Release < TagRelease + class Release < Grape::Entity expose :name + expose :tag, as: :tag_name, if: lambda { |_, _| can_download_code? } + expose :description expose :description_html do |entity| MarkupHelper.markdown_field(entity, :description) end expose :created_at expose :author, using: Entities::UserBasic, if: -> (release, _) { release.author.present? } - expose :commit, using: Entities::Commit + expose :commit, using: Entities::Commit, if: lambda { |_, _| can_download_code? } expose :assets do - expose :assets_count, as: :count - expose :sources, using: Entities::Releases::Source + expose :assets_count, as: :count do |release, _| + assets_to_exclude = can_download_code? ? [] : [:sources] + release.assets_count(except: assets_to_exclude) + end + expose :sources, using: Entities::Releases::Source, if: lambda { |_, _| can_download_code? } expose :links, using: Entities::Releases::Link do |release, options| release.links.sorted end end + + private + + def can_download_code? + Ability.allowed?(options[:current_user], :download_code, object.project) + end end class Tag < Grape::Entity @@ -1234,7 +1275,7 @@ module API end class JobBasic < Grape::Entity - expose :id, :status, :stage, :name, :ref, :tag, :coverage + expose :id, :status, :stage, :name, :ref, :tag, :coverage, :allow_failure expose :created_at, :started_at, :finished_at expose :duration expose :user, with: User @@ -1269,8 +1310,9 @@ module API end class Variable < Grape::Entity - expose :key, :value + expose :variable_type, :key, :value expose :protected?, as: :protected, if: -> (entity, _) { entity.respond_to?(:protected?) } + expose :masked?, as: :masked, if: -> (entity, _) { entity.respond_to?(:masked?) } end class Pipeline < PipelineBasic diff --git a/lib/api/events.rb b/lib/api/events.rb index b98aa9f31e1..e4c017fab42 100644 --- a/lib/api/events.rb +++ b/lib/api/events.rb @@ -4,34 +4,11 @@ module API class Events < Grape::API include PaginationParams include APIGuard + helpers ::API::Helpers::EventsHelpers - helpers do - params :event_filter_params do - optional :action, type: String, values: Event.actions, desc: 'Event action to filter on' - optional :target_type, type: String, values: Event.target_types, desc: 'Event target type to filter on' - optional :before, type: Date, desc: 'Include only events created before this date' - optional :after, type: Date, desc: 'Include only events created after this date' - end - - params :sort_params do - optional :sort, type: String, values: %w[asc desc], default: 'desc', - desc: 'Return events sorted in ascending and descending order' - end - - def present_events(events) - events = paginate(events) - - present events, with: Entities::Event - end - - def find_events(source) - EventsFinder.new(params.merge(source: source, current_user: current_user, with_associations: true)).execute - end - end + allow_access_with_scope :read_user, if: -> (request) { request.get? } resource :events do - allow_access_with_scope :read_user, if: -> (request) { request.get? } - desc "List currently authenticated user's events" do detail 'This feature was introduced in GitLab 9.3.' success Entities::Event @@ -55,8 +32,6 @@ module API requires :id, type: String, desc: 'The ID or Username of the user' end resource :users do - allow_access_with_scope :read_user, if: -> (request) { request.get? } - desc 'Get the contribution events of a specified user' do detail 'This feature was introduced in GitLab 8.13.' success Entities::Event @@ -76,25 +51,5 @@ module API present_events(events) end end - - params do - requires :id, type: String, desc: 'The ID of a project' - end - resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do - desc "List a Project's visible events" do - success Entities::Event - end - params do - use :pagination - use :event_filter_params - use :sort_params - end - - get ":id/events" do - events = find_events(user_project) - - present_events(events) - end - end end end diff --git a/lib/api/group_variables.rb b/lib/api/group_variables.rb index 3f048e0dc56..47fcbabb4d4 100644 --- a/lib/api/group_variables.rb +++ b/lib/api/group_variables.rb @@ -47,6 +47,7 @@ module API requires :key, type: String, desc: 'The key of the variable' requires :value, type: String, desc: 'The value of the variable' optional :protected, type: String, desc: 'Whether the variable is protected' + optional :variable_type, type: String, values: Ci::GroupVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var' end post ':id/variables' do variable_params = declared_params(include_missing: false) @@ -67,6 +68,7 @@ module API optional :key, type: String, desc: 'The key of the variable' optional :value, type: String, desc: 'The value of the variable' optional :protected, type: String, desc: 'Whether the variable is protected' + optional :variable_type, type: String, values: Ci::GroupVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file' end # rubocop: disable CodeReuse/ActiveRecord put ':id/variables/:key' do diff --git a/lib/api/groups.rb b/lib/api/groups.rb index ad16f26f5cc..ec1020c7c78 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -7,35 +7,9 @@ module API before { authenticate_non_get! } - helpers do - params :optional_params_ce do - optional :description, type: String, desc: 'The description of the group' - optional :visibility, type: String, - values: Gitlab::VisibilityLevel.string_values, - default: Gitlab::VisibilityLevel.string_level( - Gitlab::CurrentSettings.current_application_settings.default_group_visibility), - desc: 'The visibility of the group' - optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group' - optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access' - optional :share_with_group_lock, type: Boolean, desc: 'Prevent sharing a project with another group within this group' - end - - if Gitlab.ee? - params :optional_params_ee do - optional :membership_lock, type: Boolean, desc: 'Prevent adding new members to project membership within this group' - optional :ldap_cn, type: String, desc: 'LDAP Common Name' - optional :ldap_access, type: Integer, desc: 'A valid access level' - optional :shared_runners_minutes_limit, type: Integer, desc: '(admin-only) Pipeline minutes quota for this group' - optional :extra_shared_runners_minutes_limit, type: Integer, desc: '(admin-only) Extra pipeline minutes quota for this group' - all_or_none_of :ldap_cn, :ldap_access - end - end - - params :optional_params do - use :optional_params_ce - use :optional_params_ee if Gitlab.ee? - end + helpers Helpers::GroupsHelpers + helpers do params :statistics_params do optional :statistics, type: Boolean, default: false, desc: 'Include project statistics' end @@ -176,10 +150,7 @@ module API optional :name, type: String, desc: 'The name of the group' optional :path, type: String, desc: 'The path of the group' use :optional_params - - if Gitlab.ee? - optional :file_template_project_id, type: Integer, desc: 'The ID of a project to use for custom templates in this group' - end + use :optional_update_params_ee end put ':id' do group = find_group!(params[:id]) diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 8a21d44b4bf..00bcf6b055b 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -67,10 +67,6 @@ module API initial_current_user != current_user end - def user_namespace - @user_namespace ||= find_namespace!(params[:id]) - end - def user_group @group ||= find_group!(params[:id]) end @@ -449,7 +445,7 @@ module API end def present_carrierwave_file!(file, supports_direct_download: true) - return not_found! unless file.exists? + return not_found! unless file&.exists? if file.file_storage? present_disk_file!(file.path, file.filename) diff --git a/lib/api/helpers/events_helpers.rb b/lib/api/helpers/events_helpers.rb new file mode 100644 index 00000000000..bf3b76bb92d --- /dev/null +++ b/lib/api/helpers/events_helpers.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module API + module Helpers + module EventsHelpers + extend Grape::API::Helpers + + params :event_filter_params do + optional :action, type: String, values: Event.actions, desc: 'Event action to filter on' + optional :target_type, type: String, values: Event.target_types, desc: 'Event target type to filter on' + optional :before, type: Date, desc: 'Include only events created before this date' + optional :after, type: Date, desc: 'Include only events created after this date' + end + + params :sort_params do + optional :sort, type: String, values: %w[asc desc], default: 'desc', + desc: 'Return events sorted in ascending and descending order' + end + + def present_events(events) + events = paginate(events) + + present events, with: Entities::Event + end + + def find_events(source) + EventsFinder.new(params.merge(source: source, current_user: current_user, with_associations: true)).execute + end + end + end +end diff --git a/lib/api/helpers/groups_helpers.rb b/lib/api/helpers/groups_helpers.rb new file mode 100644 index 00000000000..2c33d79f6c8 --- /dev/null +++ b/lib/api/helpers/groups_helpers.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module API + module Helpers + module GroupsHelpers + extend ActiveSupport::Concern + extend Grape::API::Helpers + + params :optional_params_ce do + optional :description, type: String, desc: 'The description of the group' + optional :visibility, type: String, + values: Gitlab::VisibilityLevel.string_values, + default: Gitlab::VisibilityLevel.string_level( + Gitlab::CurrentSettings.current_application_settings.default_group_visibility), + desc: 'The visibility of the group' + optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group' + optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access' + optional :share_with_group_lock, type: Boolean, desc: 'Prevent sharing a project with another group within this group' + end + + params :optional_params_ee do + end + + params :optional_update_params_ee do + end + + params :optional_params do + use :optional_params_ce + use :optional_params_ee + end + end + end +end diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb index 71c30ec99a5..c318f5b9127 100644 --- a/lib/api/helpers/internal_helpers.rb +++ b/lib/api/helpers/internal_helpers.rb @@ -46,6 +46,8 @@ module API def process_mr_push_options(push_options, project, user, changes) output = {} + Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/61359') + service = ::MergeRequests::PushOptionsHandlerService.new( project, user, diff --git a/lib/api/helpers/issues_helpers.rb b/lib/api/helpers/issues_helpers.rb index f6762910b0c..5b7199fddb0 100644 --- a/lib/api/helpers/issues_helpers.rb +++ b/lib/api/helpers/issues_helpers.rb @@ -3,6 +3,14 @@ module API module Helpers module IssuesHelpers + extend Grape::API::Helpers + + params :optional_issue_params_ee do + end + + params :optional_issues_params_ee do + end + def self.update_params_at_least_one_of [ :assignee_id, @@ -18,6 +26,39 @@ module API :title ] end + + def issue_finder(args = {}) + args = declared_params.merge(args) + + args.delete(:id) + args[:milestone_title] ||= args.delete(:milestone) + args[:label_name] ||= args.delete(:labels) + args[:scope] = args[:scope].underscore if args[:scope] + + IssuesFinder.new(current_user, args) + end + + def find_issues(args = {}) + finder = issue_finder(args) + issues = finder.execute.with_api_entity_associations + + issues.reorder(order_options_with_tie_breaker) # rubocop: disable CodeReuse/ActiveRecord + end + + def issues_statistics(args = {}) + finder = issue_finder(args) + counter = Gitlab::IssuablesCountForState.new(finder) + + { + statistics: { + counts: { + all: counter[:all], + closed: counter[:closed], + opened: counter[:opened] + } + } + } + end end end end diff --git a/lib/api/helpers/members_helpers.rb b/lib/api/helpers/members_helpers.rb index 73d58ee7f37..1395ffadab9 100644 --- a/lib/api/helpers/members_helpers.rb +++ b/lib/api/helpers/members_helpers.rb @@ -19,28 +19,13 @@ 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) - source_ids = [project.id, project_group_ids, shared_group_ids] - .flatten - .compact - Member.includes(:user) - .joins(user: :project_authorizations) - .where(project_authorizations: { project_id: project.id }) - .where(source_id: source_ids) + MembersFinder.new(project, current_user).execute(include_invited_groups_members: true) 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') + GroupMembersFinder.new(group).execute end - # rubocop: enable CodeReuse/ActiveRecord end end end diff --git a/lib/api/helpers/pagination.rb b/lib/api/helpers/pagination.rb index 94b58a64d26..2a9b17ad22a 100644 --- a/lib/api/helpers/pagination.rb +++ b/lib/api/helpers/pagination.rb @@ -23,7 +23,7 @@ module API def base_request_uri @base_request_uri ||= URI.parse(request.url).tap do |uri| uri.host = Gitlab.config.gitlab.host - uri.port = nil + uri.port = Gitlab.config.gitlab.port end end diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb index aaf32dafca4..f242f1fea0e 100644 --- a/lib/api/helpers/projects_helpers.rb +++ b/lib/api/helpers/projects_helpers.rb @@ -4,48 +4,46 @@ module API module Helpers module ProjectsHelpers extend ActiveSupport::Concern + extend Grape::API::Helpers - included do - helpers do - params :optional_project_params_ce do - optional :description, type: String, desc: 'The description of the project' - optional :ci_config_path, type: String, desc: 'The path to CI config file. Defaults to `.gitlab-ci.yml`' - optional :issues_enabled, type: Boolean, desc: 'Flag indication if the issue tracker is enabled' - optional :merge_requests_enabled, type: Boolean, desc: 'Flag indication if merge requests are enabled' - optional :wiki_enabled, type: Boolean, desc: 'Flag indication if the wiki is enabled' - optional :jobs_enabled, type: Boolean, desc: 'Flag indication if jobs are enabled' - optional :snippets_enabled, type: Boolean, desc: 'Flag indication if snippets are enabled' - optional :shared_runners_enabled, type: Boolean, desc: 'Flag indication if shared runners are enabled for that project' - optional :resolve_outdated_diff_discussions, type: Boolean, desc: 'Automatically resolve merge request diffs discussions on lines changed with a push' - optional :container_registry_enabled, type: Boolean, desc: 'Flag indication if the container registry is enabled for that project' - optional :lfs_enabled, type: Boolean, desc: 'Flag indication if Git LFS is enabled for that project' - optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the project.' - optional :public_builds, type: Boolean, desc: 'Perform public builds' - optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access' - optional :only_allow_merge_if_pipeline_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed' - optional :only_allow_merge_if_all_discussions_are_resolved, type: Boolean, desc: 'Only allow to merge if all discussions are resolved' - optional :tag_list, type: Array[String], desc: 'The list of tags for a project' - 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" - optional :external_authorization_classification_label, type: String, desc: 'The classification label for the project' - end + params :optional_project_params_ce do + optional :description, type: String, desc: 'The description of the project' + optional :ci_config_path, type: String, desc: 'The path to CI config file. Defaults to `.gitlab-ci.yml`' + optional :issues_enabled, type: Boolean, desc: 'Flag indication if the issue tracker is enabled' + optional :merge_requests_enabled, type: Boolean, desc: 'Flag indication if merge requests are enabled' + optional :wiki_enabled, type: Boolean, desc: 'Flag indication if the wiki is enabled' + optional :jobs_enabled, type: Boolean, desc: 'Flag indication if jobs are enabled' + optional :snippets_enabled, type: Boolean, desc: 'Flag indication if snippets are enabled' + optional :shared_runners_enabled, type: Boolean, desc: 'Flag indication if shared runners are enabled for that project' + optional :resolve_outdated_diff_discussions, type: Boolean, desc: 'Automatically resolve merge request diffs discussions on lines changed with a push' + optional :container_registry_enabled, type: Boolean, desc: 'Flag indication if the container registry is enabled for that project' + optional :lfs_enabled, type: Boolean, desc: 'Flag indication if Git LFS is enabled for that project' + optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the project.' + optional :public_builds, type: Boolean, desc: 'Perform public builds' + optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access' + optional :only_allow_merge_if_pipeline_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed' + optional :only_allow_merge_if_all_discussions_are_resolved, type: Boolean, desc: 'Only allow to merge if all discussions are resolved' + optional :tag_list, type: Array[String], desc: 'The list of tags for a project' + 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" + optional :external_authorization_classification_label, type: String, desc: 'The classification label for the project' + optional :ci_default_git_depth, type: Integer, desc: 'Default number of revisions for shallow cloning' + end + + params :optional_project_params_ee do + end - if Gitlab.ee? - params :optional_project_params_ee do - optional :repository_storage, type: String, desc: 'Which storage shard the repository is on. Available only to admins' - optional :approvals_before_merge, type: Integer, desc: 'How many approvers should approve merge request by default' - optional :mirror, type: Boolean, desc: 'Enables pull mirroring in a project' - optional :mirror_trigger_builds, type: Boolean, desc: 'Pull mirroring triggers builds' - end - end + params :optional_project_params do + use :optional_project_params_ce + use :optional_project_params_ee + end + + params :optional_filter_params_ee do + end - params :optional_project_params do - use :optional_project_params_ce - use :optional_project_params_ee if Gitlab.ee? - end - end + params :optional_update_params_ee do end def self.update_params_at_least_one_of diff --git a/lib/api/helpers/protected_branches_helpers.rb b/lib/api/helpers/protected_branches_helpers.rb new file mode 100644 index 00000000000..0fc6841d79a --- /dev/null +++ b/lib/api/helpers/protected_branches_helpers.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module API + module Helpers + module ProtectedBranchesHelpers + extend ActiveSupport::Concern + extend Grape::API::Helpers + + params :optional_params_ee do + end + end + end +end diff --git a/lib/api/helpers/related_resources_helpers.rb b/lib/api/helpers/related_resources_helpers.rb index 793ae11b41d..9cdde25fe4e 100644 --- a/lib/api/helpers/related_resources_helpers.rb +++ b/lib/api/helpers/related_resources_helpers.rb @@ -13,6 +13,10 @@ module API available?(:merge_requests, project, options[:current_user]) end + def expose_path(path) + Gitlab::Utils.append_path(Gitlab.config.gitlab.relative_url_root, path) + end + def expose_url(path) url_options = Gitlab::Application.routes.default_url_options protocol, host, port, script_name = url_options.values_at(:protocol, :host, :port, :script_name) diff --git a/lib/api/helpers/services_helpers.rb b/lib/api/helpers/services_helpers.rb index 953be7f3798..44c577204b8 100644 --- a/lib/api/helpers/services_helpers.rb +++ b/lib/api/helpers/services_helpers.rb @@ -563,6 +563,12 @@ module API name: :notify_only_broken_pipelines, type: Boolean, desc: 'Notify only broken pipelines' + }, + { + required: false, + name: :notify_only_default_branch, + type: Boolean, + desc: 'Send notifications only for the default branch' } ], 'pivotaltracker' => [ diff --git a/lib/api/helpers/settings_helpers.rb b/lib/api/helpers/settings_helpers.rb new file mode 100644 index 00000000000..6441bb579ff --- /dev/null +++ b/lib/api/helpers/settings_helpers.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module API + module Helpers + module SettingsHelpers + extend ActiveSupport::Concern + extend Grape::API::Helpers + + params :optional_params_ee do + end + + def self.optional_attributes + [*::ApplicationSettingsHelper.visible_attributes, + *::ApplicationSettingsHelper.external_authorization_service_attributes, + :performance_bar_allowed_group_id].freeze + end + end + end +end diff --git a/lib/api/helpers/users_helpers.rb b/lib/api/helpers/users_helpers.rb new file mode 100644 index 00000000000..56fd3c6602d --- /dev/null +++ b/lib/api/helpers/users_helpers.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module API + module Helpers + module UsersHelpers + extend ActiveSupport::Concern + extend Grape::API::Helpers + + params :optional_params_ee do + end + + params :optional_index_params_ee do + end + end + end +end diff --git a/lib/api/helpers/variables_helpers.rb b/lib/api/helpers/variables_helpers.rb new file mode 100644 index 00000000000..78a92d0f5a6 --- /dev/null +++ b/lib/api/helpers/variables_helpers.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module API + module Helpers + module VariablesHelpers + extend ActiveSupport::Concern + extend Grape::API::Helpers + + params :optional_params_ee do + end + end + end +end diff --git a/lib/api/internal.rb b/lib/api/internal.rb index 00f0bbab231..224aaaaf006 100644 --- a/lib/api/internal.rb +++ b/lib/api/internal.rb @@ -264,10 +264,8 @@ module API PostReceive.perform_async(params[:gl_repository], params[:identifier], params[:changes], push_options.as_json) - if Feature.enabled?(:mr_push_options, default_enabled: true) - mr_options = push_options.get(:merge_request) - output.merge!(process_mr_push_options(mr_options, project, user, params[:changes])) if mr_options.present? - end + mr_options = push_options.get(:merge_request) + output.merge!(process_mr_push_options(mr_options, project, user, params[:changes])) if mr_options.present? broadcast_message = BroadcastMessage.current&.last&.message reference_counter_decreased = Gitlab::ReferenceCounter.new(params[:gl_repository]).decrease diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 000c00ea9f9..039ebf92187 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -3,44 +3,15 @@ module API class Issues < Grape::API include PaginationParams + helpers Helpers::IssuesHelpers + helpers ::Gitlab::IssuableMetadata before { authenticate_non_get! } - helpers ::Gitlab::IssuableMetadata - helpers do - # rubocop: disable CodeReuse/ActiveRecord - def find_issues(args = {}) - args = declared_params.merge(args) - - args.delete(:id) - args[:milestone_title] = args.delete(:milestone) - args[:label_name] = args.delete(:labels) - args[:scope] = args[:scope].underscore if args[:scope] - - issues = IssuesFinder.new(current_user, args).execute - .with_api_entity_associations - issues.reorder(order_options_with_tie_breaker) - end - # rubocop: enable CodeReuse/ActiveRecord - - if Gitlab.ee? - params :issues_params_ee do - optional :weight, types: [Integer, String], integer_none_any: true, desc: 'The weight of the issue' - end - - params :issue_params_ee do - optional :weight, type: Integer, desc: 'The weight of the issue' - end - end - - params :issues_params do + params :issues_stats_params do optional :labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names' optional :milestone, type: String, desc: 'Milestone title' - optional :order_by, type: String, values: %w[created_at updated_at], default: 'created_at', - desc: 'Return issues ordered by `created_at` or `updated_at` fields.' - optional :sort, type: String, values: %w[asc desc], default: 'desc', - desc: 'Return issues sorted in `asc` or `desc` order.' optional :milestone, type: String, desc: 'Return issues for a specific milestone' optional :iids, type: Array[Integer], desc: 'The IID array of issues' optional :search, type: String, desc: 'Search issues for text present in the title, description, or any combination of these' @@ -49,16 +20,37 @@ module API optional :created_before, type: DateTime, desc: 'Return issues created before the specified time' optional :updated_after, type: DateTime, desc: 'Return issues updated after the specified time' optional :updated_before, type: DateTime, desc: 'Return issues updated before the specified time' + optional :author_id, type: Integer, desc: 'Return issues which are authored by the user with the given ID' + optional :author_username, type: String, desc: 'Return issues which are authored by the user with the given username' + mutually_exclusive :author_id, :author_username + optional :assignee_id, types: [Integer, String], integer_none_any: true, desc: 'Return issues which are assigned to the user with the given ID' + optional :assignee_username, type: Array[String], check_assignees_count: true, + coerce_with: Validations::CheckAssigneesCount.coerce, + desc: 'Return issues which are assigned to the user with the given username' + mutually_exclusive :assignee_id, :assignee_username + optional :scope, type: String, values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all], desc: 'Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`' optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji' optional :confidential, type: Boolean, desc: 'Filter confidential or public issues' - use :pagination - use :issues_params_ee if Gitlab.ee? + use :optional_issues_params_ee + end + + params :issues_params do + optional :with_labels_details, type: Boolean, desc: 'Return more label data than just lable title', default: false + optional :state, type: String, values: %w[opened closed all], default: 'all', + desc: 'Return opened, closed, or all issues' + optional :order_by, type: String, values: %w[created_at updated_at], default: 'created_at', + desc: 'Return issues ordered by `created_at` or `updated_at` fields.' + optional :sort, type: String, values: %w[asc desc], default: 'desc', + desc: 'Return issues sorted in `asc` or `desc` order.' + + use :issues_stats_params + use :pagination end params :issue_params do @@ -71,17 +63,27 @@ module API optional :confidential, type: Boolean, desc: 'Boolean parameter if the issue should be confidential' optional :discussion_locked, type: Boolean, desc: " Boolean parameter indicating if the issue's discussion is locked" - use :issue_params_ee if Gitlab.ee? + use :optional_issue_params_ee end end + desc "Get currently authenticated user's issues statistics" + params do + use :issues_stats_params + optional :scope, type: String, values: %w[created_by_me assigned_to_me all], default: 'created_by_me', + desc: 'Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`' + end + get '/issues_statistics' do + authenticate! unless params[:scope] == 'all' + + present issues_statistics, with: Grape::Presenters::Presenter + end + resource :issues do desc "Get currently authenticated user's issues" do - success Entities::IssueBasic + success Entities::Issue end params do - optional :state, type: String, values: %w[opened closed all], default: 'all', - desc: 'Return opened, closed, or all issues' use :issues_params optional :scope, type: String, values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all], default: 'created_by_me', desc: 'Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`' @@ -91,7 +93,8 @@ module API issues = paginate(find_issues) options = { - with: Entities::IssueBasic, + with: Entities::Issue, + with_labels_details: declared_params[:with_labels_details], current_user: current_user, issuable_metadata: issuable_meta_data(issues, 'Issue') } @@ -105,11 +108,9 @@ module API end resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Get a list of group issues' do - success Entities::IssueBasic + success Entities::Issue end params do - optional :state, type: String, values: %w[opened closed all], default: 'all', - desc: 'Return opened, closed, or all issues' use :issues_params end get ":id/issues" do @@ -118,13 +119,24 @@ module API issues = paginate(find_issues(group_id: group.id, include_subgroups: true)) options = { - with: Entities::IssueBasic, + with: Entities::Issue, + with_labels_details: declared_params[:with_labels_details], current_user: current_user, issuable_metadata: issuable_meta_data(issues, 'Issue') } present issues, options end + + desc 'Get statistics for the list of group issues' + params do + use :issues_stats_params + end + get ":id/issues_statistics" do + group = find_group!(params[:id]) + + present issues_statistics(group_id: group.id, include_subgroups: true), with: Grape::Presenters::Presenter + end end params do @@ -134,11 +146,9 @@ module API include TimeTrackingEndpoints desc 'Get a list of project issues' do - success Entities::IssueBasic + success Entities::Issue end params do - optional :state, type: String, values: %w[opened closed all], default: 'all', - desc: 'Return opened, closed, or all issues' use :issues_params end get ":id/issues" do @@ -147,7 +157,8 @@ module API issues = paginate(find_issues(project_id: project.id)) options = { - with: Entities::IssueBasic, + with: Entities::Issue, + with_labels_details: declared_params[:with_labels_details], current_user: current_user, project: user_project, issuable_metadata: issuable_meta_data(issues, 'Issue') @@ -156,6 +167,16 @@ module API present issues, options end + desc 'Get statistics for the list of project issues' + params do + use :issues_stats_params + end + get ":id/issues_statistics" do + project = find_project!(params[:id]) + + present issues_statistics(project_id: project.id), with: Grape::Presenters::Presenter + end + desc 'Get a single project issue' do success Entities::Issue end @@ -192,6 +213,7 @@ module API params.delete(:iid) unless current_user.can?(:set_issue_iid, user_project) issue_params = declared_params(include_missing: false) + issue_params[:system_note_timestamp] = params[:created_at] issue_params = convert_parameters_from_legacy_format(issue_params) @@ -230,14 +252,9 @@ module API issue = user_project.issues.find_by!(iid: params.delete(:issue_iid)) authorize! :update_issue, issue - # Setting updated_at only allowed for admins and owners as well - if params[:updated_at].present? - if current_user.admin? || user_project.owner == current_user || current_user.owned_groups.include?(user_project.owner) - issue.system_note_timestamp = params[:updated_at] - else - params.delete(:updated_at) - end - end + # Setting updated_at is allowed only for admins and owners + params.delete(:updated_at) unless current_user.can?(:set_issue_updated_at, user_project) + issue.system_note_timestamp = params[:updated_at] update_params = declared_params(include_missing: false).merge(request: request, api: true) diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index ce85772e4ed..955624404f1 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -386,9 +386,8 @@ module API ) if merge_when_pipeline_succeeds && merge_request.head_pipeline && merge_request.head_pipeline.active? - ::MergeRequests::MergeWhenPipelineSucceedsService - .new(merge_request.target_project, current_user, merge_params) - .execute(merge_request) + AutoMergeService.new(merge_request.target_project, current_user, merge_params) + .execute(merge_request, AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS) else ::MergeRequests::MergeService .new(merge_request.target_project, current_user, merge_params) @@ -429,11 +428,9 @@ module API post ':id/merge_requests/:merge_request_iid/cancel_merge_when_pipeline_succeeds' do merge_request = find_project_merge_request(params[:merge_request_iid]) - unauthorized! unless merge_request.can_cancel_merge_when_pipeline_succeeds?(current_user) + unauthorized! unless merge_request.can_cancel_auto_merge?(current_user) - ::MergeRequests::MergeWhenPipelineSucceedsService - .new(merge_request.target_project, current_user) - .cancel(merge_request) + AutoMergeService.new(merge_request.target_project, current_user).cancel(merge_request) end desc 'Rebase the merge request against its target branch' do diff --git a/lib/api/namespaces.rb b/lib/api/namespaces.rb index 3cc09f6ac3f..77ecb3e7cde 100644 --- a/lib/api/namespaces.rb +++ b/lib/api/namespaces.rb @@ -44,6 +44,8 @@ module API requires :id, type: String, desc: "Namespace's ID or path" end get ':id', requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do + user_namespace = find_namespace!(params[:id]) + present user_namespace, with: Entities::Namespace, current_user: current_user end end diff --git a/lib/api/pipeline_schedules.rb b/lib/api/pipeline_schedules.rb index c86b50d3736..1d1ef1afc6b 100644 --- a/lib/api/pipeline_schedules.rb +++ b/lib/api/pipeline_schedules.rb @@ -118,6 +118,7 @@ module API requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id' requires :key, type: String, desc: 'The key of the variable' requires :value, type: String, desc: 'The value of the variable' + optional :variable_type, type: String, values: Ci::PipelineScheduleVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var' end post ':id/pipeline_schedules/:pipeline_schedule_id/variables' do authorize! :update_pipeline_schedule, pipeline_schedule @@ -138,6 +139,7 @@ module API requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id' requires :key, type: String, desc: 'The key of the variable' optional :value, type: String, desc: 'The value of the variable' + optional :variable_type, type: String, values: Ci::PipelineScheduleVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file' end put ':id/pipeline_schedules/:pipeline_schedule_id/variables/:key' do authorize! :update_pipeline_schedule, pipeline_schedule diff --git a/lib/api/project_clusters.rb b/lib/api/project_clusters.rb index b62ec887183..dcc8d94fb79 100644 --- a/lib/api/project_clusters.rb +++ b/lib/api/project_clusters.rb @@ -54,6 +54,7 @@ module API requires :name, type: String, desc: 'Cluster name' optional :enabled, type: Boolean, default: true, desc: 'Determines if cluster is active or not, defaults to true' optional :domain, type: String, desc: 'Cluster base domain' + optional :managed, type: Boolean, default: true, desc: 'Determines if GitLab will manage namespaces and service accounts for this cluster, defaults to true' requires :platform_kubernetes_attributes, type: Hash, desc: %q(Platform Kubernetes data) do requires :api_url, type: String, allow_blank: false, desc: 'URL to access the Kubernetes API' requires :token, type: String, desc: 'Token to authenticate against Kubernetes' diff --git a/lib/api/project_events.rb b/lib/api/project_events.rb new file mode 100644 index 00000000000..734311e1142 --- /dev/null +++ b/lib/api/project_events.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module API + class ProjectEvents < Grape::API + include PaginationParams + include APIGuard + helpers ::API::Helpers::EventsHelpers + + params do + requires :id, type: String, desc: 'The ID of a project' + end + resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do + desc "List a Project's visible events" do + success Entities::Event + end + params do + use :pagination + use :event_filter_params + use :sort_params + end + + get ":id/events" do + events = find_events(user_project) + + present_events(events) + end + end + end +end diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb index c64ec2fcc95..71891e43dcc 100644 --- a/lib/api/project_import.rb +++ b/lib/api/project_import.rb @@ -3,7 +3,8 @@ module API class ProjectImport < Grape::API include PaginationParams - include Helpers::ProjectsHelpers + + helpers Helpers::ProjectsHelpers helpers do def import_params diff --git a/lib/api/projects.rb b/lib/api/projects.rb index cb0106592f5..1e14c77b5be 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -6,27 +6,12 @@ module API class Projects < Grape::API include PaginationParams include Helpers::CustomAttributes - include Helpers::ProjectsHelpers + + helpers Helpers::ProjectsHelpers before { authenticate_non_get! } helpers do - if Gitlab.ee? - params :optional_filter_params_ee do - optional :wiki_checksum_failed, type: Grape::API::Boolean, default: false, desc: 'Limit by projects where wiki checksum is failed' - optional :repository_checksum_failed, type: Grape::API::Boolean, default: false, desc: 'Limit by projects where repository checksum is failed' - end - - params :optional_update_params_ee do - optional :mirror_user_id, type: Integer, desc: 'User responsible for all the activity surrounding a pull mirror event' - optional :only_mirror_protected_branches, type: Grape::API::Boolean, desc: 'Only mirror protected branches' - optional :mirror_overwrites_diverged_branches, type: Grape::API::Boolean, desc: 'Pull mirror overwrites diverged branches' - optional :import_url, type: String, desc: 'URL from which the project is imported' - optional :packages_enabled, type: Grape::API::Boolean, desc: 'Enable project packages feature' - optional :fallback_approvals_required, type: Integer, desc: 'Overall approvals required when no rule is present' - end - end - # EE::API::Projects would override this method def apply_filters(projects) projects = projects.with_issues_available_for_user(current_user) if params[:with_issues_enabled] @@ -77,7 +62,7 @@ module API optional :with_programming_language, type: String, desc: 'Limit to repositories which use the given programming language' optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user' - use :optional_filter_params_ee if Gitlab.ee? + use :optional_filter_params_ee end params :create_params do @@ -296,7 +281,7 @@ module API optional :path, type: String, desc: 'The path of the repository' use :optional_project_params - use :optional_update_params_ee if Gitlab.ee? + use :optional_update_params_ee at_least_one_of(*Helpers::ProjectsHelpers.update_params_at_least_one_of) end diff --git a/lib/api/protected_branches.rb b/lib/api/protected_branches.rb index f8cce1ed784..33dea25289a 100644 --- a/lib/api/protected_branches.rb +++ b/lib/api/protected_branches.rb @@ -8,6 +8,8 @@ module API before { authorize_admin_project } + helpers Helpers::ProtectedBranchesHelpers + params do requires :id, type: String, desc: 'The ID of a project' end @@ -52,29 +54,7 @@ module API values: ProtectedBranch::MergeAccessLevel.allowed_access_levels, desc: 'Access levels allowed to merge (defaults: `40`, maintainer access level)' - if Gitlab.ee? - optional :unprotect_access_level, type: Integer, - values: ProtectedBranch::UnprotectAccessLevel.allowed_access_levels, - desc: 'Access levels allowed to unprotect (defaults: `40`, maintainer access level)' - - optional :allowed_to_push, type: Array, desc: 'An array of users/groups allowed to push' do - optional :access_level, type: Integer, values: ProtectedBranch::PushAccessLevel.allowed_access_levels - optional :user_id, type: Integer - optional :group_id, type: Integer - end - - optional :allowed_to_merge, type: Array, desc: 'An array of users/groups allowed to merge' do - optional :access_level, type: Integer, values: ProtectedBranch::MergeAccessLevel.allowed_access_levels - optional :user_id, type: Integer - optional :group_id, type: Integer - end - - optional :allowed_to_unprotect, type: Array, desc: 'An array of users/groups allowed to unprotect' do - optional :access_level, type: Integer, values: ProtectedBranch::UnprotectAccessLevel.allowed_access_levels - optional :user_id, type: Integer - optional :group_id, type: Integer - end - end + use :optional_params_ee end # rubocop: disable CodeReuse/ActiveRecord post ':id/protected_branches' do diff --git a/lib/api/releases.rb b/lib/api/releases.rb index cb85028f22c..6b17f4317db 100644 --- a/lib/api/releases.rb +++ b/lib/api/releases.rb @@ -23,7 +23,7 @@ module API get ':id/releases' do releases = ::ReleasesFinder.new(user_project, current_user).execute - present paginate(releases), with: Entities::Release + present paginate(releases), with: Entities::Release, current_user: current_user end desc 'Get a single project release' do @@ -34,9 +34,9 @@ module API requires :tag_name, type: String, desc: 'The name of the tag', as: :tag end get ':id/releases/:tag_name', requirements: RELEASE_ENDPOINT_REQUIREMETS do - authorize_read_release! + authorize_download_code! - present release, with: Entities::Release + present release, with: Entities::Release, current_user: current_user end desc 'Create a new release' do @@ -63,7 +63,7 @@ module API .execute if result[:status] == :success - present result[:release], with: Entities::Release + present result[:release], with: Entities::Release, current_user: current_user else render_api_error!(result[:message], result[:http_status]) end @@ -86,7 +86,7 @@ module API .execute if result[:status] == :success - present result[:release], with: Entities::Release + present result[:release], with: Entities::Release, current_user: current_user else render_api_error!(result[:message], result[:http_status]) end @@ -107,7 +107,7 @@ module API .execute if result[:status] == :success - present result[:release], with: Entities::Release + present result[:release], with: Entities::Release, current_user: current_user else render_api_error!(result[:message], result[:http_status]) end @@ -135,6 +135,10 @@ module API authorize! :destroy_release, release end + def authorize_download_code! + authorize! :download_code, release + end + def release @release ||= user_project.releases.find_by_tag(params[:tag]) end diff --git a/lib/api/runner.rb b/lib/api/runner.rb index c60d25b88cb..fdf4904e9f5 100644 --- a/lib/api/runner.rb +++ b/lib/api/runner.rb @@ -15,12 +15,14 @@ module API optional :info, type: Hash, desc: %q(Runner's metadata) optional :active, type: Boolean, desc: 'Should Runner be active' optional :locked, type: Boolean, desc: 'Should Runner be locked for current project' + optional :access_level, type: String, values: Ci::Runner.access_levels.keys, + desc: 'The access_level of the runner' optional :run_untagged, type: Boolean, desc: 'Should Runner handle untagged jobs' 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 post '/' do - attributes = attributes_for_keys([:description, :active, :locked, :run_untagged, :tag_list, :maximum_timeout]) + attributes = attributes_for_keys([:description, :active, :locked, :run_untagged, :tag_list, :access_level, :maximum_timeout]) .merge(get_runner_details_from_request) attributes = @@ -96,6 +98,7 @@ module API optional :certificate, type: String, desc: %q(Session's certificate) optional :authorization, type: String, desc: %q(Session's authorization) end + optional :job_age, type: Integer, desc: %q(Job should be older than passed age in seconds to be ran on runner) end post '/request' do authenticate_runner! diff --git a/lib/api/search.rb b/lib/api/search.rb index 60095300ea1..1cab1a97186 100644 --- a/lib/api/search.rb +++ b/lib/api/search.rb @@ -112,12 +112,13 @@ module API type: String, desc: 'The scope of the search', values: Helpers::SearchHelpers.project_search_scopes + optional :ref, type: String, desc: 'The name of a repository branch or tag. If not given, the default branch is used' use :pagination end get ':id/(-/)search' do check_users_search_allowed! - present search(project_id: user_project.id), with: entity + present search({ project_id: user_project.id, repository_ref: params[:ref] }), with: entity end end end diff --git a/lib/api/settings.rb b/lib/api/settings.rb index b064747e5fc..6767ef882cb 100644 --- a/lib/api/settings.rb +++ b/lib/api/settings.rb @@ -4,6 +4,8 @@ module API class Settings < Grape::API before { authenticated_as_admin! } + helpers Helpers::SettingsHelpers + helpers do def current_settings @current_setting ||= @@ -136,48 +138,10 @@ module API desc: "Restrictions on the complexity of uploaded #{type.upcase} keys. A value of #{ApplicationSetting::FORBIDDEN_KEY_VALUE} disables all #{type.upcase} keys." end - if Gitlab.ee? - optional :elasticsearch_aws, type: Boolean, desc: 'Enable support for AWS hosted elasticsearch' - - given elasticsearch_aws: ->(val) { val } do - optional :elasticsearch_aws_access_key, type: String, desc: 'AWS IAM access key' - requires :elasticsearch_aws_region, type: String, desc: 'The AWS region the elasticsearch domain is configured' - optional :elasticsearch_aws_secret_access_key, type: String, desc: 'AWS IAM secret access key' - end - - optional :elasticsearch_indexing, type: Boolean, desc: 'Enable Elasticsearch indexing' - - given elasticsearch_indexing: ->(val) { val } do - optional :elasticsearch_search, type: Boolean, desc: 'Enable Elasticsearch search' - requires :elasticsearch_url, type: String, desc: 'The url to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., "http://localhost:9200, http://localhost:9201")' - end - - optional :email_additional_text, type: String, desc: 'Additional text added to the bottom of every email for legal/auditing/compliance reasons' - optional :help_text, type: String, desc: 'GitLab server administrator information' - optional :repository_size_limit, type: Integer, desc: 'Size limit per repository (MB)' - optional :file_template_project_id, type: Integer, desc: 'ID of project where instance-level file templates are stored.' - optional :repository_storages, type: Array[String], desc: 'A list of names of enabled storage paths, taken from `gitlab.yml`. New projects will be created in one of these stores, chosen at random.' - optional :snowplow_enabled, type: Boolean, desc: 'Enable Snowplow' - - given snowplow_enabled: ->(val) { val } do - requires :snowplow_collector_uri, type: String, desc: 'Snowplow Collector URI' - optional :snowplow_cookie_domain, type: String, desc: 'Snowplow cookie domain' - optional :snowplow_site_id, type: String, desc: 'Snowplow Site/Application ID' - end - - optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.' - end - - optional_attributes = [*::ApplicationSettingsHelper.visible_attributes, - *::ApplicationSettingsHelper.external_authorization_service_attributes, - :performance_bar_allowed_group_id] - - if Gitlab.ee? - optional_attributes += EE::ApplicationSettingsHelper.possible_licensed_attributes - end + use :optional_params_ee - optional(*optional_attributes) - at_least_one_of(*optional_attributes) + optional(*Helpers::SettingsHelpers.optional_attributes) + at_least_one_of(*Helpers::SettingsHelpers.optional_attributes) end put "application/settings" do attrs = declared_params(include_missing: false) diff --git a/lib/api/users.rb b/lib/api/users.rb index 2f23e33bd4a..6afeebb6890 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -15,6 +15,8 @@ module API authenticate_non_get! end + helpers Helpers::UsersHelpers + helpers do # rubocop: disable CodeReuse/ActiveRecord def find_user_by_id(params) @@ -52,10 +54,7 @@ module API optional :private_profile, type: Boolean, desc: 'Flag indicating the user has a private profile' all_or_none_of :extern_uid, :provider - if Gitlab.ee? - optional :shared_runners_minutes_limit, type: Integer, desc: 'Pipeline minutes quota for this user' - optional :extra_shared_runners_minutes_limit, type: Integer, desc: '(admin-only) Extra pipeline minutes quota for this user' - end + use :optional_params_ee end params :sort_params do @@ -85,10 +84,7 @@ module API use :sort_params use :pagination use :with_custom_attributes - - if Gitlab.ee? - optional :skip_ldap, type: Boolean, default: false, desc: 'Skip LDAP users' - end + use :optional_index_params_ee end # rubocop: disable CodeReuse/ActiveRecord get do diff --git a/lib/api/validations/check_assignees_count.rb b/lib/api/validations/check_assignees_count.rb new file mode 100644 index 00000000000..836ec936b31 --- /dev/null +++ b/lib/api/validations/check_assignees_count.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module API + module Validations + class CheckAssigneesCount < Grape::Validations::Base + def self.coerce + lambda do |value| + case value + when String, Array + Array.wrap(value) + else + [] + end + end + end + + def validate_param!(attr_name, params) + return if param_allowed?(attr_name, params) + + raise Grape::Exceptions::Validation, + params: [@scope.full_name(attr_name)], + message: "allows one value, but found #{params[attr_name].size}: #{params[attr_name].join(", ")}" + end + + private + + def param_allowed?(attr_name, params) + params[attr_name].size <= 1 + end + end + end +end diff --git a/lib/api/variables.rb b/lib/api/variables.rb index 3489ba827e4..af1d7936556 100644 --- a/lib/api/variables.rb +++ b/lib/api/variables.rb @@ -7,6 +7,8 @@ module API before { authenticate! } before { authorize! :admin_build, user_project } + helpers Helpers::VariablesHelpers + helpers do def filter_variable_parameters(params) # This method exists so that EE can more easily filter out certain @@ -54,11 +56,11 @@ module API params do requires :key, type: String, desc: 'The key of the variable' requires :value, type: String, desc: 'The value of the variable' - optional :protected, type: String, desc: 'Whether the variable is protected' + optional :protected, type: Boolean, desc: 'Whether the variable is protected' + optional :masked, type: Boolean, desc: 'Whether the variable is masked' + optional :variable_type, type: String, values: Ci::Variable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var' - if Gitlab.ee? - optional :environment_scope, type: String, desc: 'The environment_scope of the variable' - end + use :optional_params_ee end post ':id/variables' do variable_params = declared_params(include_missing: false) @@ -79,11 +81,11 @@ module API params do optional :key, type: String, desc: 'The key of the variable' optional :value, type: String, desc: 'The value of the variable' - optional :protected, type: String, desc: 'Whether the variable is protected' + optional :protected, type: Boolean, desc: 'Whether the variable is protected' + optional :masked, type: Boolean, desc: 'Whether the variable is masked' + optional :variable_type, type: String, values: Ci::Variable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file' - if Gitlab.ee? - optional :environment_scope, type: String, desc: 'The environment_scope of the variable' - end + use :optional_params_ee end # rubocop: disable CodeReuse/ActiveRecord put ':id/variables/:key' do diff --git a/lib/api/wikis.rb b/lib/api/wikis.rb index 994074ddc67..5724adb2c40 100644 --- a/lib/api/wikis.rb +++ b/lib/api/wikis.rb @@ -33,7 +33,8 @@ module API authorize! :read_wiki, user_project entity = params[:with_content] ? Entities::WikiPage : Entities::WikiPageBasic - present user_project.wiki.pages, with: entity + + present user_project.wiki.list_pages(load_content: params[:with_content]), with: entity end desc 'Get a wiki page' do |