Commit 11a82cb4 authored by Igor Drozdov's avatar Igor Drozdov

Merge branch 'sh-update-grape-gem' into 'master'

Upgrade Grape v1.1.0 to v1.3.2

Closes #195960

See merge request gitlab-org/gitlab!27276
parents 7bb3e6e0 27c7252b
......@@ -284,6 +284,18 @@ Gitlab/Union:
- 'spec/**/*'
- 'ee/spec/**/*'
API/GrapeAPIInstance:
Enabled: true
Include:
- 'lib/**/api/**/*.rb'
- 'ee/**/api/**/*.rb'
API/GrapeArrayMissingCoerce:
Enabled: true
Include:
- 'lib/**/api/**/*.rb'
- 'ee/**/api/**/*.rb'
Cop/SidekiqOptionsQueue:
Enabled: true
Exclude:
......
......@@ -19,7 +19,7 @@ gem 'default_value_for', '~> 3.3.0'
gem 'pg', '~> 1.1'
gem 'rugged', '~> 0.28'
gem 'grape-path-helpers', '~> 1.2'
gem 'grape-path-helpers', '~> 1.3'
gem 'faraday', '~> 0.12'
gem 'marginalia', '~> 1.8.0'
......@@ -82,7 +82,7 @@ gem 'gitlab_omniauth-ldap', '~> 2.1.1', require: 'omniauth-ldap'
gem 'net-ldap'
# API
gem 'grape', '~> 1.1.0'
gem 'grape', '~> 1.3.2'
gem 'grape-entity', '~> 0.7.1'
gem 'rack-cors', '~> 1.0.6', require: 'rack/cors'
......
......@@ -103,10 +103,6 @@ GEM
aws-sdk-core (= 2.11.374)
aws-sigv4 (1.1.0)
aws-eventstream (~> 1.0, >= 1.0.2)
axiom-types (0.1.1)
descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1)
babosa (1.0.2)
base32 (0.3.2)
batch-loader (1.4.0)
......@@ -165,8 +161,6 @@ GEM
nap
open4 (~> 1.3)
coderay (1.1.2)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
colored2 (3.1.2)
commonmarker (0.20.1)
ruby-enum (~> 0.5)
......@@ -222,8 +216,6 @@ GEM
ruby-statistics (>= 2.1)
thor (>= 0.19, < 2)
unicode_plot (>= 0.0.4, < 1.0.0)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
device_detector (1.0.0)
devise (4.7.1)
bcrypt (~> 3.0)
......@@ -250,6 +242,28 @@ GEM
doorkeeper-openid_connect (1.6.3)
doorkeeper (>= 5.0, < 5.2)
json-jwt (~> 1.6)
dry-configurable (0.11.5)
concurrent-ruby (~> 1.0)
dry-core (~> 0.4, >= 0.4.7)
dry-equalizer (~> 0.2)
dry-container (0.7.2)
concurrent-ruby (~> 1.0)
dry-configurable (~> 0.1, >= 0.1.3)
dry-core (0.4.9)
concurrent-ruby (~> 1.0)
dry-equalizer (0.3.0)
dry-inflector (0.2.0)
dry-logic (1.0.6)
concurrent-ruby (~> 1.0)
dry-core (~> 0.2)
dry-equalizer (~> 0.2)
dry-types (1.4.0)
concurrent-ruby (~> 1.0)
dry-container (~> 0.3)
dry-core (~> 0.4, >= 0.4.4)
dry-equalizer (~> 0.3)
dry-inflector (~> 0.1, >= 0.1.2)
dry-logic (~> 1.0, >= 1.0.2)
ed25519 (1.2.4)
elasticsearch (6.8.0)
elasticsearch-api (= 6.8.0)
......@@ -439,19 +453,19 @@ GEM
signet (~> 0.7)
gpgme (2.0.20)
mini_portile2 (~> 2.3)
grape (1.1.0)
grape (1.3.2)
activesupport
builder
dry-types (>= 1.1)
mustermann-grape (~> 1.0.0)
rack (>= 1.3.0)
rack-accept
virtus (>= 1.0.0)
grape-entity (0.7.1)
activesupport (>= 4.0)
multi_json (>= 1.3.2)
grape-path-helpers (1.2.0)
grape-path-helpers (1.3.0)
activesupport
grape (~> 1.0)
grape (~> 1.3)
rake (~> 12)
grape_logging (1.8.3)
grape
......@@ -645,9 +659,10 @@ GEM
multi_xml (0.6.0)
multipart-post (2.1.1)
murmurhash3 (0.1.6)
mustermann (1.0.3)
mustermann-grape (1.0.0)
mustermann (~> 1.0.0)
mustermann (1.1.1)
ruby2_keywords (~> 0.0.1)
mustermann-grape (1.0.1)
mustermann (>= 1.0.0)
nakayoshi_fork (0.0.4)
nap (1.1.0)
nenv (0.3.0)
......@@ -961,6 +976,7 @@ GEM
ruby-saml (1.7.2)
nokogiri (>= 1.5.10)
ruby-statistics (2.1.2)
ruby2_keywords (0.0.2)
ruby_dep (1.5.0)
ruby_parser (3.13.1)
sexp_processor (~> 4.9)
......@@ -1119,11 +1135,6 @@ GEM
activerecord (>= 3.0)
activesupport (>= 3.0)
version_sorter (2.2.4)
virtus (1.0.5)
axiom-types (~> 0.1)
coercible (~> 1.0)
descendants_tracker (~> 0.0, >= 0.0.3)
equalizer (~> 0.0, >= 0.0.9)
vmstat (2.3.0)
warden (1.2.8)
rack (>= 2.0.6)
......@@ -1254,9 +1265,9 @@ DEPENDENCIES
google-api-client (~> 0.23)
google-protobuf (~> 3.8.0)
gpgme (~> 2.0.19)
grape (~> 1.1.0)
grape (~> 1.3.2)
grape-entity (~> 0.7.1)
grape-path-helpers (~> 1.2)
grape-path-helpers (~> 1.3)
grape_logging (~> 1.7)
graphiql-rails (~> 1.4.10)
graphql (~> 1.10.5)
......
---
title: Upgrade Grape v1.1.0 to v1.3.2
merge_request: 27276
author:
type: other
......@@ -98,6 +98,14 @@ For instance:
Model.create(foo: params[:foo])
```
## Array types
With Grape v1.3+, Array types must be defined with a `coerce_with`
block, or parameters will fail to validate when passed a string from an
API request. See the [Grape upgrading
documentation](https://github.com/ruby-grape/grape/blob/master/UPGRADING.md#ensure-that-array-types-have-explicit-coercions)
for more details.
## Using HTTP status helpers
For non-200 HTTP responses, use the provided helpers in `lib/api/helpers.rb` to ensure correct behavior (`not_found!`, `no_content!` etc.). These will `throw` inside Grape and abort the execution of your endpoint.
......
......@@ -513,12 +513,12 @@ do that, so we'll follow regular object-oriented practices that we define the
interface first here.
For example, suppose we have a few more optional parameters for EE. We can move the
paramters out of the `Grape::API` class to a helper module, so we can inject it
parameters out of the `Grape::API::Instance` class to a helper module, so we can inject it
before it would be used in the class.
```ruby
module API
class Projects < Grape::API
class Projects < Grape::API::Instance
helpers Helpers::ProjectsHelpers
end
end
......@@ -579,7 +579,7 @@ class definition to make it easy and clear:
```ruby
module API
class JobArtifacts < Grape::API
class JobArtifacts < Grape::API::Instance
# EE::API::JobArtifacts would override the following helpers
helpers do
def authorize_download_artifacts!
......@@ -623,7 +623,7 @@ route. Something like this:
```ruby
module API
class MergeRequests < Grape::API
class MergeRequests < Grape::API::Instance
helpers do
# EE::API::MergeRequests would override the following helpers
def update_merge_request_ee(merge_request)
......@@ -692,7 +692,7 @@ least argument. We would approach this as follows:
```ruby
# api/merge_requests/parameters.rb
module API
class MergeRequests < Grape::API
class MergeRequests < Grape::API::Instance
module Parameters
def self.update_params_at_least_one_of
%i[
......@@ -708,7 +708,7 @@ API::MergeRequests::Parameters.prepend_if_ee('EE::API::MergeRequests::Parameters
# api/merge_requests.rb
module API
class MergeRequests < Grape::API
class MergeRequests < Grape::API::Instance
params do
at_least_one_of(*Parameters.update_params_at_least_one_of)
end
......
......@@ -2,7 +2,7 @@
module API
module Analytics
class CodeReviewAnalytics < Grape::API
class CodeReviewAnalytics < Grape::API::Instance
include PaginationParams
helpers ::Gitlab::IssuableMetadata
......
......@@ -2,7 +2,7 @@
module API
module Analytics
class GroupActivityAnalytics < Grape::API
class GroupActivityAnalytics < Grape::API::Instance
DESCRIPTION_DETAIL =
'This feature is gated by the `:group_activity_analytics`'\
' feature flag, introduced in GitLab 12.9.'
......
# frozen_string_literal: true
module API
class AuditEvents < ::Grape::API
class AuditEvents < ::Grape::API::Instance
include ::API::PaginationParams
before do
......
......@@ -2,7 +2,7 @@
# PHP composer support (https://getcomposer.org/)
module API
class ComposerPackages < Grape::API
class ComposerPackages < Grape::API::Instance
helpers ::API::Helpers::PackagesManagerClientsHelpers
helpers ::API::Helpers::RelatedResourcesHelpers
helpers ::API::Helpers::Packages::BasicAuthHelpers
......
......@@ -9,7 +9,7 @@
#
# Technical debt: https://gitlab.com/gitlab-org/gitlab/issues/35798
module API
class ConanPackages < Grape::API
class ConanPackages < Grape::API::Instance
helpers ::API::Helpers::PackagesManagerClientsHelpers
PACKAGE_REQUIREMENTS = {
......
# frozen_string_literal: true
module API
class Dependencies < Grape::API
class Dependencies < Grape::API::Instance
helpers do
def dependencies_by(params)
pipeline = ::Security::ReportFetchService.new(user_project, ::Ci::JobArtifact.dependency_list_reports).pipeline
......@@ -28,6 +28,7 @@ module API
params do
optional :package_manager,
type: Array[String],
coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
desc: "Returns dependencies belonging to specified package managers: #{::Security::DependencyListService::FILTER_PACKAGE_MANAGERS_VALUES.join(', ')}.",
values: ::Security::DependencyListService::FILTER_PACKAGE_MANAGERS_VALUES
end
......
# frozen_string_literal: true
module API
class DependencyProxy < Grape::API
class DependencyProxy < Grape::API::Instance
helpers ::API::Helpers::PackagesHelpers
helpers do
......
# frozen_string_literal: true
module API
class ElasticsearchIndexedNamespaces < Grape::API
class ElasticsearchIndexedNamespaces < Grape::API::Instance
before { authenticated_as_admin! }
resource :elasticsearch_indexed_namespaces do
......
# frozen_string_literal: true
module API
class EpicIssues < Grape::API
class EpicIssues < Grape::API::Instance
before do
authenticate!
authorize_epics_feature!
......
# frozen_string_literal: true
module API
class EpicLinks < Grape::API
class EpicLinks < Grape::API::Instance
include ::Gitlab::Utils::StrongMemoize
before do
......
# frozen_string_literal: true
module API
class Epics < Grape::API
class Epics < Grape::API::Instance
include PaginationParams
before do
......@@ -29,7 +29,7 @@ module API
optional :state, type: String, values: %w[opened closed all], default: 'all',
desc: 'Return opened, closed, or all epics'
optional :author_id, type: Integer, desc: 'Return epics which are authored by the user with the given ID'
optional :labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names'
optional :labels, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of label names'
optional :with_labels_details, type: Boolean, desc: 'Return titles of labels and other details', default: false
optional :created_after, type: DateTime, desc: 'Return epics created after the specified time'
optional :created_before, type: DateTime, desc: 'Return epics created before the specified time'
......@@ -70,7 +70,7 @@ module API
optional :start_date_is_fixed, type: Boolean, desc: 'Indicates start date should be sourced from start_date_fixed field not the issue milestones'
optional :end_date, as: :due_date_fixed, type: String, desc: 'The due date of an epic'
optional :due_date_is_fixed, type: Boolean, desc: 'Indicates due date should be sourced from due_date_fixed field not the issue milestones'
optional :labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names'
optional :labels, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of label names'
optional :parent_id, type: Integer, desc: 'The id of a parent epic'
end
post ':id/(-/)epics' do
......@@ -96,7 +96,7 @@ module API
optional :start_date_is_fixed, type: Boolean, desc: 'Indicates start date should be sourced from start_date_fixed field not the issue milestones'
optional :end_date, as: :due_date_fixed, type: String, desc: 'The due date of an epic'
optional :due_date_is_fixed, type: Boolean, desc: 'Indicates due date should be sourced from due_date_fixed field not the issue milestones'
optional :labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names'
optional :labels, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of label names'
optional :state_event, type: String, values: %w[reopen close], desc: 'State event for an epic'
at_least_one_of :title, :description, :start_date_fixed, :start_date_is_fixed, :due_date_fixed, :due_date_is_fixed, :labels, :state_event, :confidential
end
......
# frozen_string_literal: true
module API
class FeatureFlagScopes < Grape::API
class FeatureFlagScopes < Grape::API::Instance
include PaginationParams
ENVIRONMENT_SCOPE_ENDPOINT_REQUIREMENTS = FeatureFlags::FEATURE_FLAG_ENDPOINT_REQUIREMENTS
......
# frozen_string_literal: true
module API
class FeatureFlags < Grape::API
class FeatureFlags < Grape::API::Instance
include PaginationParams
FEATURE_FLAG_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS
......
# frozen_string_literal: true
module API
class FeatureFlagsUserLists < Grape::API
class FeatureFlagsUserLists < Grape::API::Instance
include PaginationParams
error_formatter :json, -> (message, _backtrace, _options, _env, _original_exception) {
......
......@@ -3,7 +3,7 @@
require 'base64'
module API
class Geo < Grape::API
class Geo < Grape::API::Instance
resource :geo do
helpers do
def sanitized_node_status_params
......
# frozen_string_literal: true
module API
class GeoNodes < Grape::API
class GeoNodes < Grape::API::Instance
include PaginationParams
include APIGuard
include ::Gitlab::Utils::StrongMemoize
......
# frozen_string_literal: true
module API
class GeoReplication < Grape::API
class GeoReplication < Grape::API::Instance
include PaginationParams
include APIGuard
include ::Gitlab::Utils::StrongMemoize
......
# frozen_string_literal: true
module API
class GroupHooks < Grape::API
class GroupHooks < Grape::API::Instance
include ::API::PaginationParams
before { authenticate! }
......
# frozen_string_literal: true
module API
class GroupPackages < Grape::API
class GroupPackages < Grape::API::Instance
include PaginationParams
before do
......
......@@ -5,24 +5,22 @@ module API
module ProjectApprovalRulesHelpers
extend Grape::API::Helpers
ARRAY_COERCION_LAMBDA = ->(val) { val.empty? ? [] : Array.wrap(val) }
params :create_project_approval_rule do
requires :name, type: String, desc: 'The name of the approval rule'
requires :approvals_required, type: Integer, desc: 'The number of required approvals for this rule'
optional :rule_type, type: String, desc: 'The type of approval rule'
optional :users, as: :user_ids, type: Array, coerce_with: ARRAY_COERCION_LAMBDA, desc: 'The user ids for this rule'
optional :groups, as: :group_ids, type: Array, coerce_with: ARRAY_COERCION_LAMBDA, desc: 'The group ids for this rule'
optional :protected_branch_ids, type: Array, coerce_with: ARRAY_COERCION_LAMBDA, desc: 'The protected branch ids for this rule'
optional :users, as: :user_ids, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The user ids for this rule'
optional :groups, as: :group_ids, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The group ids for this rule'
optional :protected_branch_ids, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The protected branch ids for this rule'
end
params :update_project_approval_rule do
requires :approval_rule_id, type: Integer, desc: 'The ID of an approval_rule'
optional :name, type: String, desc: 'The name of the approval rule'
optional :approvals_required, type: Integer, desc: 'The number of required approvals for this rule'
optional :users, as: :user_ids, type: Array, coerce_with: ARRAY_COERCION_LAMBDA, desc: 'The user ids for this rule'
optional :groups, as: :group_ids, type: Array, coerce_with: ARRAY_COERCION_LAMBDA, desc: 'The group ids for this rule'
optional :protected_branch_ids, type: Array, coerce_with: ARRAY_COERCION_LAMBDA, desc: 'The protected branch ids for this rule'
optional :users, as: :user_ids, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The user ids for this rule'
optional :groups, as: :group_ids, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The group ids for this rule'
optional :protected_branch_ids, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The protected branch ids for this rule'
optional :remove_hidden_groups, type: Boolean, desc: 'Whether hidden groups should be removed'
end
......
# frozen_string_literal: true
module API
class IssueLinks < Grape::API
class IssueLinks < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
# frozen_string_literal: true
module API
class Ldap < Grape::API
class Ldap < Grape::API::Instance
# Admin users by default should be able to access these API endpoints.
# However, non-admin users can access these endpoints if the "Allow group
# owners to manage LDAP-related group settings" is enabled, and they own a
......
# frozen_string_literal: true
module API
class LdapGroupLinks < Grape::API
class LdapGroupLinks < Grape::API::Instance
before { authenticate! }
params do
......
# frozen_string_literal: true
module API
class License < Grape::API
class License < Grape::API::Instance
before { authenticated_as_admin! }
resource :license do
......
# frozen_string_literal: true
module API
class ManagedLicenses < Grape::API
class ManagedLicenses < Grape::API::Instance
include PaginationParams
before { authenticate! unless route.settings[:skip_authentication] }
......
# frozen_string_literal: true
module API
class MavenPackages < Grape::API
class MavenPackages < Grape::API::Instance
MAVEN_ENDPOINT_REQUIREMENTS = {
file_name: API::NO_SLASH_URL_PART_REGEX
}.freeze
......
# frozen_string_literal: true
module API
class MergeRequestApprovalRules < ::Grape::API
class MergeRequestApprovalRules < ::Grape::API::Instance
before { authenticate_non_get! }
ARRAY_COERCION_LAMBDA = ->(val) { val.empty? ? [] : Array.wrap(val) }
helpers do
def find_merge_request_approval_rule(merge_request, id)
merge_request.approval_rules.find_by_id!(id)
......@@ -34,8 +32,8 @@ module API
requires :name, type: String, desc: 'The name of the approval rule'
requires :approvals_required, type: Integer, desc: 'The number of required approvals for this rule'
optional :approval_project_rule_id, type: Integer, desc: 'The ID of a project-level approval rule'
optional :user_ids, type: Array, coerce_with: ARRAY_COERCION_LAMBDA, desc: 'The user ids for this rule'
optional :group_ids, type: Array, coerce_with: ARRAY_COERCION_LAMBDA, desc: 'The group ids for this rule'
optional :user_ids, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The user ids for this rule'
optional :group_ids, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The group ids for this rule'
end
post do
merge_request = find_merge_request_with_access(params[:merge_request_iid], :update_approvers)
......@@ -56,8 +54,8 @@ module API
requires :approval_rule_id, type: Integer, desc: 'The ID of an approval rule'
optional :name, type: String, desc: 'The name of the approval rule'
optional :approvals_required, type: Integer, desc: 'The number of required approvals for this rule'
optional :user_ids, type: Array, coerce_with: ARRAY_COERCION_LAMBDA, desc: 'The user ids for this rule'
optional :group_ids, type: Array, coerce_with: ARRAY_COERCION_LAMBDA, desc: 'The group ids for this rule'
optional :user_ids, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The user ids for this rule'
optional :group_ids, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The group ids for this rule'
optional :remove_hidden_groups, type: Boolean, desc: 'Whether hidden groups should be removed'
end
put do
......
# frozen_string_literal: true
module API
class MergeRequestApprovals < ::Grape::API
class MergeRequestApprovals < ::Grape::API::Instance
before { authenticate_non_get! }
ARRAY_COERCION_LAMBDA = ->(val) { val.empty? ? [] : Array.wrap(val) }
helpers do
def present_approval(merge_request)
present merge_request.approval_state, with: ::EE::API::Entities::ApprovalState, current_user: current_user
......@@ -109,8 +107,10 @@ module API
success ::EE::API::Entities::ApprovalState
end
params do
requires :approver_ids, type: Array[String], coerce_with: ARRAY_COERCION_LAMBDA, desc: 'Array of User IDs to set as approvers.'
requires :approver_group_ids, type: Array[String], coerce_with: ARRAY_COERCION_LAMBDA, desc: 'Array of Group IDs to set as approvers.'
requires :approver_ids, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce,
desc: 'Array of User IDs to set as approvers.'
requires :approver_group_ids, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce,
desc: 'Array of Group IDs to set as approvers.'
end
put 'approvers' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/8883')
......
# frozen_string_literal: true
module API
class MergeTrains < ::Grape::API
class MergeTrains < ::Grape::API::Instance
include PaginationParams
before do
......
# frozen_string_literal: true
module API
class NpmPackages < Grape::API
class NpmPackages < Grape::API::Instance
helpers ::API::Helpers::PackagesHelpers
helpers ::API::Helpers::Packages::DependencyProxyHelpers
......
......@@ -6,7 +6,7 @@
# called by the NuGet package manager client when users run commands
# like `nuget install` or `nuget push`.
module API
class NugetPackages < Grape::API
class NugetPackages < Grape::API::Instance
helpers ::API::Helpers::PackagesManagerClientsHelpers
helpers ::API::Helpers::Packages::BasicAuthHelpers
......
# frozen_string_literal: true
module API
class PackageFiles < Grape::API
class PackageFiles < Grape::API::Instance
include PaginationParams
before do
......
# frozen_string_literal: true
module API
class ProjectAliases < Grape::API
class ProjectAliases < Grape::API::Instance
include PaginationParams
before { check_feature_availability }
......
# frozen_string_literal: true
module API
class ProjectApprovalRules < ::Grape::API
class ProjectApprovalRules < ::Grape::API::Instance
before { authenticate! }
helpers ::API::Helpers::ProjectApprovalRulesHelpers
......
# frozen_string_literal: true
module API
class ProjectApprovalSettings < ::Grape::API
class ProjectApprovalSettings < ::Grape::API::Instance
before { authenticate! }
helpers ::API::Helpers::ProjectApprovalRulesHelpers
......
# frozen_string_literal: true
module API
class ProjectApprovals < ::Grape::API
class ProjectApprovals < ::Grape::API::Instance
before { authenticate! }
before { authorize! :update_approvers, user_project }
ARRAY_COERCION_LAMBDA = ->(val) { val.empty? ? [] : Array.wrap(val) }
helpers do
def filter_forbidden_param!(permission, param)
unless can?(current_user, permission, user_project)
......@@ -67,8 +65,8 @@ module API
success EE::API::Entities::ApprovalSettings
end
params do
requires :approver_ids, type: Array[String], coerce_with: ARRAY_COERCION_LAMBDA, desc: 'Array of User IDs to set as approvers.'
requires :approver_group_ids, type: Array[String], coerce_with: ARRAY_COERCION_LAMBDA, desc: 'Array of Group IDs to set as approvers.'
requires :approver_ids, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Array of User IDs to set as approvers.'
requires :approver_group_ids, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Array of Group IDs to set as approvers.'
end
put ':id/approvers' do
result = ::Projects::UpdateService.new(user_project, current_user, declared(params, include_parent_namespaces: false).merge(remove_old_approvers: true)).execute
......
......@@ -3,7 +3,7 @@
require_dependency 'declarative_policy'
module API
class ProjectMirror < Grape::API
class ProjectMirror < Grape::API::Instance
helpers do
def github_webhook_signature
@github_webhook_signature ||= headers['X-Hub-Signature']
......
# frozen_string_literal: true
module API
class ProjectPackages < Grape::API
class ProjectPackages < Grape::API::Instance
include PaginationParams
before do
......
# frozen_string_literal: true
module API
class ProjectPushRule < Grape::API
class ProjectPushRule < Grape::API::Instance
before { authenticate! }
before { authorize_admin_project }
before { check_project_feature_available!(:push_rules) }
......
# frozen_string_literal: true
module API
class ProtectedEnvironments < Grape::API
class ProtectedEnvironments < Grape::API::Instance
include PaginationParams
ENVIRONMENT_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(name: API::NO_SLASH_URL_PART_REGEX)
......
......@@ -6,7 +6,7 @@
# called by the PyPI package manager client when users run commands
# like `pip install` or `twine upload`.
module API
class PypiPackages < Grape::API
class PypiPackages < Grape::API::Instance
helpers ::API::Helpers::PackagesManagerClientsHelpers
helpers ::API::Helpers::RelatedResourcesHelpers
helpers ::API::Helpers::Packages::BasicAuthHelpers
......
# frozen_string_literal: true
module API
class Scim < Grape::API
class Scim < Grape::API::Instance
include ::Gitlab::Utils::StrongMemoize
prefix 'api/scim'
......
# frozen_string_literal: true
module API
class Unleash < Grape::API
class Unleash < Grape::API::Instance
include PaginationParams
namespace :feature_flags do
......
......@@ -7,7 +7,7 @@
#
module API
module V3
class Github < Grape::API
class Github < Grape::API::Instance
JIRA_DEV_PANEL_FEATURE = :jira_dev_panel_integration.freeze
NO_SLASH_URL_PART_REGEX = %r{[^/]+}.freeze
ENDPOINT_REQUIREMENTS = {
......
# frozen_string_literal: true
module API
class VisualReviewDiscussions < Grape::API
class VisualReviewDiscussions < Grape::API::Instance
include PaginationParams
helpers ::API::Helpers::NotesHelpers
helpers ::RendersNotes
......
# frozen_string_literal: true
module API
class Vulnerabilities < Grape::API
class Vulnerabilities < Grape::API::Instance
include ::API::Helpers::VulnerabilitiesHooks
include PaginationParams
......
# frozen_string_literal: true
module API
class VulnerabilityExports < Grape::API
class VulnerabilityExports < Grape::API::Instance
include ::API::Helpers::VulnerabilitiesHooks
include ::Gitlab::Utils::StrongMemoize
......
# frozen_string_literal: true
module API
class VulnerabilityFindings < Grape::API
class VulnerabilityFindings < Grape::API::Instance
include PaginationParams
include ::Gitlab::Utils::StrongMemoize
......@@ -33,19 +33,23 @@ module API
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
params do
optional :report_type, type: Array[String], desc: 'The type of report vulnerability belongs to',
optional :report_type, type: Array[String],
coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
desc: 'The type of report vulnerability belongs to',
values: ::Vulnerabilities::Occurrence.report_types.keys,
default: ::Vulnerabilities::Occurrence.report_types.keys
optional :scope, type: String, desc: 'Return vulnerabilities for the given scope: `dismissed` or `all`',
default: 'dismissed', values: %w[all dismissed]
optional :severity,
type: Array[String],
coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
desc: 'Returns vulnerabilities belonging to specified severity level: '\
'`info`, `unknown`, `low`, `medium`, `high`, or `critical`. Defaults to all',
values: ::Vulnerabilities::Occurrence.severities.keys,
default: ::Vulnerabilities::Occurrence.severities.keys
optional :confidence,
type: Array[String],
coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
desc: 'Returns vulnerabilities belonging to specified confidence level: '\
'`undefined`, `ignore`, `unknown`, `experimental`, `low`, `medium`, `high`, or `confirmed`. '\
'Defaults to all',
......
# frozen_string_literal: true
module API
class VulnerabilityIssueLinks < Grape::API
class VulnerabilityIssueLinks < Grape::API::Instance
include ::API::Helpers::VulnerabilitiesHooks
helpers ::API::Helpers::VulnerabilitiesHelpers
......
......@@ -2,7 +2,7 @@
module EE
module API
class Boards < ::Grape::API
class Boards < ::Grape::API::Instance
include ::API::PaginationParams
include ::API::BoardsResponses
......
......@@ -2,7 +2,7 @@
module EE
module API
class GroupBoards < ::Grape::API
class GroupBoards < ::Grape::API::Instance
include ::API::PaginationParams
include ::API::BoardsResponses
......
......@@ -25,8 +25,8 @@ module EE
end
given elasticsearch_limit_indexing: ->(val) { val } do
optional :elasticsearch_namespace_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::LabelsList.coerce, desc: 'The namespace ids to index with Elasticsearch.'
optional :elasticsearch_project_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::LabelsList.coerce, desc: 'The project ids to index with Elasticsearch.'
optional :elasticsearch_namespace_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The namespace ids to index with Elasticsearch.'
optional :elasticsearch_project_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The project ids to index with Elasticsearch.'
end
optional :email_additional_text, type: String, desc: 'Additional text added to the bottom of every email for legal/auditing/compliance reasons'
......@@ -35,7 +35,7 @@ module EE
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 :repository_storages, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, 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 :usage_ping_enabled, type: Grape::API::Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.'
optional :updating_name_disabled_for_users, type: Grape::API::Boolean, desc: 'Flag indicating if users are permitted to update their profile name'
optional :disable_overriding_approvers_per_merge_request, type: Grape::API::Boolean, desc: 'Disable Users ability to overwrite approvers in merge requests.'
......
......@@ -6,7 +6,7 @@ describe EE::API::Helpers do
include Rack::Test::Methods
let(:helper) do
Class.new(Grape::API) do
Class.new(Grape::API::Instance) do
helpers EE::API::Helpers
helpers API::APIGuard::HelperMethods
helpers API::Helpers
......
......@@ -143,7 +143,9 @@ describe API::MergeRequestApprovalRules do
let(:current_user) { user }
let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/approval_rules" }
let(:approver) { create(:user) }
let(:other_approver) { create(:user) }
let(:group) { create(:group) }
let(:other_group) { create(:group) }
let(:approval_project_rule_id) { nil }
let(:user_ids) { [] }
let(:group_ids) { [] }
......@@ -166,7 +168,9 @@ describe API::MergeRequestApprovalRules do
before do
project.update!(disable_overriding_approvers_per_merge_request: false)
project.add_developer(approver)
project.add_developer(other_approver)
group.add_developer(approver)
other_group.add_developer(other_approver)
action
end
......@@ -179,7 +183,7 @@ describe API::MergeRequestApprovalRules do
expect(rule['name']).to eq(params[:name])
expect(rule['approvals_required']).to eq(params[:approvals_required])
expect(rule['rule_type']).to eq('regular')
expect(rule['rule_type']).to eq('any_approver')
expect(rule['contains_hidden_groups']).to eq(false)
expect(rule['source_rule']).to be_nil
expect(rule['eligible_approvers']).to be_empty
......@@ -188,24 +192,24 @@ describe API::MergeRequestApprovalRules do
end
context 'users are passed' do
let(:user_ids) { [approver.id] }
let(:user_ids) { "#{approver.id},#{other_approver.id}" }
it 'includes users' do
rule = json_response
expect(rule['eligible_approvers']).to match([hash_including('id' => approver.id)])
expect(rule['users']).to match([hash_including('id' => approver.id)])
expect(rule['eligible_approvers'].map { |approver| approver['id'] }).to contain_exactly(approver.id, other_approver.id)
expect(rule['users'].map { |user| user['id'] }).to contain_exactly(approver.id, other_approver.id)
end
end
context 'groups are passed' do
let(:group_ids) { [group.id] }
let(:group_ids) { "#{group.id},#{other_group.id}" }
it 'includes groups' do
rule = json_response
expect(rule['eligible_approvers']).to match([hash_including('id' => approver.id)])
expect(rule['groups']).to match([hash_including('id' => group.id)])
expect(rule['eligible_approvers'].map { |approver| approver['id'] }).to contain_exactly(approver.id, other_approver.id)
expect(rule['groups'].map { |group| group['id'] }).to contain_exactly(group.id, other_group.id)
end
end
......@@ -257,6 +261,8 @@ describe API::MergeRequestApprovalRules do
let(:user_ids) { [] }
let(:group_ids) { [] }
let(:remove_hidden_groups) { nil }
let(:other_approver) { create(:user) }
let(:other_group) { create(:group) }
let(:params) do
{
......@@ -277,8 +283,10 @@ describe API::MergeRequestApprovalRules do
project.update!(disable_overriding_approvers_per_merge_request: false)
project.add_developer(existing_approver)
project.add_developer(new_approver)
project.add_developer(other_approver)
existing_group.add_developer(existing_approver)
new_group.add_developer(new_approver)
other_group.add_developer(other_approver)
action
end
......@@ -302,24 +310,24 @@ describe API::MergeRequestApprovalRules do
end
context 'users are passed' do
let(:user_ids) { [new_approver.id] }
let(:user_ids) { "#{new_approver.id},#{existing_approver.id}" }
it 'changes users' do
rule = json_response
expect(rule['eligible_approvers']).to match([hash_including('id' => new_approver.id)])
expect(rule['users']).to match([hash_including('id' => new_approver.id)])
expect(rule['eligible_approvers'].map { |approver| approver['id'] }).to contain_exactly(new_approver.id, existing_approver.id)
expect(rule['users'].map { |user| user['id'] }).to contain_exactly(new_approver.id, existing_approver.id)
end
end
context 'groups are passed' do
let(:group_ids) { [new_group.id] }
let(:group_ids) { "#{new_group.id},#{other_group.id}" }
it 'changes groups' do
rule = json_response
expect(rule['eligible_approvers']).to match([hash_including('id' => new_approver.id)])
expect(rule['groups']).to match([hash_including('id' => new_group.id)])
expect(rule['eligible_approvers'].map { |approver| approver['id'] }).to contain_exactly(new_approver.id, other_approver.id)
expect(rule['groups'].map { |group| group['id'] }).to contain_exactly(new_group.id, other_group.id)
end
end
......
......@@ -321,7 +321,7 @@ describe API::MergeRequestApprovals do
it 'does not allow overriding approvers' do
expect do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/approvers", current_user),
params: { approver_ids: [approver.id], approver_group_ids: [group.id] }
params: { approver_ids: approver.id.to_s, approver_group_ids: group.id.to_s }
end.to not_change { merge_request.approvers.count }.and not_change { merge_request.approver_groups.count }
end
end
......@@ -334,12 +334,12 @@ describe API::MergeRequestApprovals do
it 'allows overriding approvers' do
expect do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/approvers", current_user),
params: { approver_ids: [approver.id], approver_group_ids: [group.id] }
end.to change { merge_request.approvers.count }.from(0).to(1)
params: { approver_ids: "#{approver.id},#{user2.id}", approver_group_ids: "#{group.id}" }
end.to change { merge_request.approvers.count }.from(0).to(2)
.and change { merge_request.approver_groups.count }.from(0).to(1)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['approvers'][0]['user']['username']).to eq(approver.username)
expect(json_response['approvers'].map { |approver| approver['user'] }.map { |user| user['username'] }).to contain_exactly(approver.username, user2.username)
expect(json_response['approver_groups'][0]['group']['name']).to eq(group.name)
end
......@@ -349,7 +349,7 @@ describe API::MergeRequestApprovals do
expect do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/approvers", current_user),
params: { approver_ids: [], approver_group_ids: [] }.to_json, headers: { CONTENT_TYPE: 'application/json' }
params: { approver_ids: '', approver_group_ids: '' }.to_json, headers: { CONTENT_TYPE: 'application/json' }
end.to change { merge_request.approvers.count }.from(1).to(0)
.and change { merge_request.approver_groups.count }.from(1).to(0)
......
......@@ -9,6 +9,7 @@ describe API::ProjectApprovalRules do
let_it_be(:admin) { create(:user, :admin) }
let_it_be(:project) { create(:project, :public, :repository, creator: user, namespace: user.namespace, only_allow_merge_if_pipeline_succeeds: false) }
let_it_be(:approver) { create(:user) }
let_it_be(:other_approver) { create(:user) }
describe 'GET /projects/:id/approval_rules' do
let(:url) { "/projects/#{project.id}/approval_rules" }
......
......@@ -9,6 +9,7 @@ describe API::ProjectApprovalSettings do
let_it_be(:admin) { create(:user, :admin) }
let_it_be(:project) { create(:project, :public, :repository, creator: user, namespace: user.namespace, only_allow_merge_if_pipeline_succeeds: false) }
let_it_be(:approver) { create(:user) }
let_it_be(:other_approver) { create(:user) }
describe 'GET /projects/:id/approval_settings' do
let(:url) { "/projects/#{project.id}/approval_settings" }
......
......@@ -79,6 +79,7 @@ RSpec.shared_examples 'an API endpoint for updating project approval rule' do
shared_examples 'a user with access' do
before do
project.add_developer(approver)
project.add_developer(other_approver)
end
context 'when protected_branch_ids param is present' do
......@@ -117,10 +118,10 @@ RSpec.shared_examples 'an API endpoint for updating project approval rule' do
it 'sets approvers' do
expect do
put api(url, current_user), params: { users: [approver.id] }
end.to change { approval_rule.users.count }.from(0).to(1)
put api(url, current_user), params: { users: "#{approver.id},#{other_approver.id}" }
end.to change { approval_rule.users.count }.from(0).to(2)
expect(approval_rule.users).to contain_exactly(approver)
expect(approval_rule.users).to contain_exactly(approver, other_approver)
expect(approval_rule.groups).to be_empty
expect(response).to have_gitlab_http_status(:ok)
......
# frozen_string_literal: true
module API
class AccessRequests < Grape::API
class AccessRequests < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
......@@ -2,7 +2,7 @@
module API
module Admin
class Sidekiq < Grape::API
class Sidekiq < Grape::API::Instance
before { authenticated_as_admin! }
namespace 'admin' do
......
# frozen_string_literal: true
module API
class API < Grape::API
class API < Grape::API::Instance
include APIGuard
LOG_FILENAME = Rails.root.join("log", "api_json.log")
......
......@@ -148,7 +148,16 @@ module API
{ scope: e.scopes })
end
response.finish
finished_response = nil
response.finish do |rack_response|
# Grape expects a Rack::Response
# (https://github.com/ruby-grape/grape/commit/c117bff7d22971675f4b34367d3a98bc31c8fc02),
# and we need to retrieve it here:
# https://github.com/nov/rack-oauth2/blob/40c9a99fd80486ccb8de0e4869ae384547c0d703/lib/rack/oauth2/server/abstract/error.rb#L28
finished_response = rack_response
end
finished_response
end
end
end
......
# frozen_string_literal: true
module API
class Appearance < Grape::API
class Appearance < Grape::API::Instance
before { authenticated_as_admin! }
helpers do
......
......@@ -2,7 +2,7 @@
module API
# External applications API
class Applications < Grape::API
class Applications < Grape::API::Instance
before { authenticated_as_admin! }
resource :applications do
......
# frozen_string_literal: true
module API
class Avatar < Grape::API
class Avatar < Grape::API::Instance
resource :avatar do
desc 'Return avatar url for a user' do
success Entities::Avatar
......
# frozen_string_literal: true
module API
class AwardEmoji < Grape::API
class AwardEmoji < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
# frozen_string_literal: true
module API
class Badges < Grape::API
class Badges < Grape::API::Instance
include PaginationParams
before { authenticate_non_get! }
......
# frozen_string_literal: true
module API
class Boards < Grape::API
class Boards < Grape::API::Instance
include BoardsResponses
include PaginationParams
......
......@@ -3,7 +3,7 @@
require 'mime/types'
module API
class Branches < Grape::API
class Branches < Grape::API::Instance
include PaginationParams
BRANCH_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(branch: API::NO_SLASH_URL_PART_REGEX)
......
# frozen_string_literal: true
module API
class BroadcastMessages < Grape::API
class BroadcastMessages < Grape::API::Instance
include PaginationParams
resource :broadcast_messages do
......
......@@ -3,7 +3,7 @@
require 'mime/types'
module API
class CommitStatuses < Grape::API
class CommitStatuses < Grape::API::Instance
params do
requires :id, type: String, desc: 'The ID of a project'
end
......
......@@ -3,7 +3,7 @@
require 'mime/types'
module API
class Commits < Grape::API
class Commits < Grape::API::Instance
include PaginationParams
before do
......
# frozen_string_literal: true
module API
class ContainerRegistryEvent < Grape::API
class ContainerRegistryEvent < Grape::API::Instance
DOCKER_DISTRIBUTION_EVENTS_V1_JSON = 'application/vnd.docker.distribution.events.v1+json'
before { authenticate_registry_notification! }
......
# frozen_string_literal: true
module API
class DeployKeys < Grape::API
class DeployKeys < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
# frozen_string_literal: true
module API
class DeployTokens < Grape::API
class DeployTokens < Grape::API::Instance
include PaginationParams
helpers do
......@@ -54,7 +54,7 @@ module API
params do
requires :name, type: String, desc: "New deploy token's name"
requires :scopes, type: Array[String], values: ::DeployToken::AVAILABLE_SCOPES.map(&:to_s),
requires :scopes, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, values: ::DeployToken::AVAILABLE_SCOPES.map(&:to_s),
desc: 'Indicates the deploy token scopes. Must be at least one of "read_repository", "read_registry", or "write_registry".'
optional :expires_at, type: DateTime, desc: 'Expiration date for the deploy token. Does not expire if no value is provided.'
optional :username, type: String, desc: 'Username for deploy token. Default is `gitlab+deploy-token-{n}`'
......@@ -117,7 +117,7 @@ module API
params do
requires :name, type: String, desc: 'The name of the deploy token'
requires :scopes, type: Array[String], values: ::DeployToken::AVAILABLE_SCOPES.map(&:to_s),
requires :scopes, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, values: ::DeployToken::AVAILABLE_SCOPES.map(&:to_s),
desc: 'Indicates the deploy token scopes. Must be at least one of "read_repository", "read_registry", or "write_registry".'
optional :expires_at, type: DateTime, desc: 'Expiration date for the deploy token. Does not expire if no value is provided.'
optional :username, type: String, desc: 'Username for deploy token. Default is `gitlab+deploy-token-{n}`'
......
......@@ -2,7 +2,7 @@
module API
# Deployments RESTful API endpoints
class Deployments < Grape::API
class Deployments < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
# frozen_string_literal: true
module API
class Discussions < Grape::API
class Discussions < Grape::API::Instance
include PaginationParams
helpers ::API::Helpers::NotesHelpers
helpers ::RendersNotes
......
......@@ -2,7 +2,7 @@
module API
# Environments RESTfull API endpoints
class Environments < Grape::API
class Environments < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
# frozen_string_literal: true
module API
class ErrorTracking < Grape::API
class ErrorTracking < Grape::API::Instance
before { authenticate! }
params do
......
# frozen_string_literal: true
module API
class Events < Grape::API
class Events < Grape::API::Instance
include PaginationParams
include APIGuard
helpers ::API::Helpers::EventsHelpers
......
# frozen_string_literal: true
module API
class Features < Grape::API
class Features < Grape::API::Instance
before { authenticated_as_admin! }
helpers do
......
# frozen_string_literal: true
module API
class Files < Grape::API
class Files < Grape::API::Instance
include APIGuard
FILE_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(file_path: API::NO_SLASH_URL_PART_REGEX)
......
# frozen_string_literal: true
module API
class GroupBoards < Grape::API
class GroupBoards < Grape::API::Instance
include BoardsResponses
include PaginationParams
......
# frozen_string_literal: true
module API
class GroupClusters < Grape::API
class GroupClusters < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
# frozen_string_literal: true
module API
class GroupContainerRepositories < Grape::API
class GroupContainerRepositories < Grape::API::Instance
include PaginationParams
before { authorize_read_group_container_images! }
......
# frozen_string_literal: true
module API
class GroupExport < Grape::API
class GroupExport < Grape::API::Instance
before do
not_found! unless Feature.enabled?(:group_import_export, user_group, default_enabled: true)
......
# frozen_string_literal: true
module API
class GroupImport < Grape::API
class GroupImport < Grape::API::Instance
MAXIMUM_FILE_SIZE = 50.megabytes.freeze
helpers do
......
# frozen_string_literal: true
module API
class GroupLabels < Grape::API
class GroupLabels < Grape::API::Instance
include PaginationParams
helpers ::API::Helpers::LabelHelpers
......
# frozen_string_literal: true
module API
class GroupMilestones < Grape::API
class GroupMilestones < Grape::API::Instance
include MilestoneResponses
include PaginationParams
......
# frozen_string_literal: true
module API
class GroupVariables < Grape::API
class GroupVariables < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
# frozen_string_literal: true
module API
class Groups < Grape::API
class Groups < Grape::API::Instance
include PaginationParams
include Helpers::CustomAttributes
......@@ -16,7 +16,7 @@ module API
params :group_list_params do
use :statistics_params
optional :skip_groups, type: Array[Integer], desc: 'Array of group ids to exclude from list'
optional :skip_groups, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Array of group ids to exclude from list'
optional :all_available, type: Boolean, desc: 'Show all group that you have access to'
optional :search, type: String, desc: 'Search for a specific group'
optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user'
......
......@@ -24,7 +24,7 @@ module API
optional :milestone, type: String, desc: 'Return merge requests for a specific milestone'
optional :labels,
type: Array[String],
coerce_with: Validations::Types::LabelsList.coerce,
coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
desc: 'Comma-separated list of label names'
optional :with_labels_details, type: Boolean, desc: 'Return titles of labels and other details', default: false
optional :created_after, type: DateTime, desc: 'Return merge requests created after the specified time'
......
......@@ -44,7 +44,7 @@ module API
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 :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The list of tags for a project'
# TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960
optional :avatar, type: File, desc: 'Avatar image for project' # rubocop:disable Scalability/FileUploads
optional :printing_merge_request_link_enabled, type: Boolean, desc: 'Show link to create/view merge request when pushing from the command line'
......
# frozen_string_literal: true
module API
class ImportGithub < Grape::API
class ImportGithub < Grape::API::Instance
rescue_from Octokit::Unauthorized, with: :provider_unauthorized
helpers do
......
......@@ -3,7 +3,7 @@
module API
# Internal access API
module Internal
class Base < Grape::API
class Base < Grape::API::Instance
before { authenticate_by_gitlab_shell_token! }
before do
......
......@@ -3,7 +3,7 @@
module API
# Pages Internal API
module Internal
class Pages < Grape::API
class Pages < Grape::API::Instance
before do
authenticate_gitlab_pages_request!
end
......
# frozen_string_literal: true
module API
class Issues < Grape::API
class Issues < Grape::API::Instance
include PaginationParams
helpers Helpers::IssuesHelpers
helpers Helpers::RateLimiter
......@@ -11,9 +11,9 @@ module API
helpers do
params :negatable_issue_filter_params do
optional :labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names'
optional :labels, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of label names'
optional :milestone, type: String, desc: 'Milestone title'
optional :iids, type: Array[Integer], desc: 'The IID array of issues'
optional :iids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, 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'
optional :in, type: String, desc: '`title`, `description`, or a string joining them with comma'
......@@ -63,10 +63,10 @@ module API
params :issue_params do
optional :description, type: String, desc: 'The description of an issue'
optional :assignee_ids, type: Array[Integer], desc: 'The array of user IDs to assign issue'
optional :assignee_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The array of user IDs to assign issue'
optional :assignee_id, type: Integer, desc: '[Deprecated] The ID of a user to assign issue'
optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign issue'
optional :labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names'
optional :labels, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of label names'
optional :due_date, type: String, desc: 'Date string in the format YEAR-MONTH-DAY'
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"
......
# frozen_string_literal: true
module API
class JobArtifacts < Grape::API
class JobArtifacts < Grape::API::Instance
before { authenticate_non_get! }
# EE::API::JobArtifacts would override the following helpers
......
# frozen_string_literal: true
module API
class Jobs < Grape::API
class Jobs < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
......@@ -2,7 +2,7 @@
module API
# Keys API
class Keys < Grape::API
class Keys < Grape::API::Instance
before { authenticate! }
resource :keys do
......
# frozen_string_literal: true
module API
class Labels < Grape::API
class Labels < Grape::API::Instance
include PaginationParams
helpers ::API::Helpers::LabelHelpers
......
# frozen_string_literal: true
module API
class Lint < Grape::API
class Lint < Grape::API::Instance
namespace :ci do
desc 'Validation of .gitlab-ci.yml content'
params do
......
# frozen_string_literal: true
module API
class LsifData < Grape::API
class LsifData < Grape::API::Instance
MAX_FILE_SIZE = 10.megabytes
before do
......
# frozen_string_literal: true
module API
class Markdown < Grape::API
class Markdown < Grape::API::Instance
params do
requires :text, type: String, desc: "The markdown text to render"
optional :gfm, type: Boolean, desc: "Render text using GitLab Flavored Markdown"
......
# frozen_string_literal: true
module API
class Members < Grape::API
class Members < Grape::API::Instance
include PaginationParams
before { authenticate! }
......@@ -18,7 +18,7 @@ module API
end
params do
optional :query, type: String, desc: 'A query string to search for members'
optional :user_ids, type: Array[Integer], desc: 'Array of user ids to look up for membership'
optional :user_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Array of user ids to look up for membership'
optional :show_seat_info, type: Boolean, desc: 'Show seat information for members'
use :optional_filter_params_ee
use :pagination
......@@ -37,7 +37,7 @@ module API
end
params do
optional :query, type: String, desc: 'A query string to search for members'
optional :user_ids, type: Array[Integer], desc: 'Array of user ids to look up for membership'
optional :user_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Array of user ids to look up for membership'
optional :show_seat_info, type: Boolean, desc: 'Show seat information for members'
use :pagination
end
......
......@@ -2,7 +2,7 @@
module API
# MergeRequestDiff API
class MergeRequestDiffs < Grape::API
class MergeRequestDiffs < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
# frozen_string_literal: true
module API
class MergeRequests < Grape::API
class MergeRequests < Grape::API::Instance
include PaginationParams
CONTEXT_COMMITS_POST_LIMIT = 20
......@@ -177,9 +177,9 @@ module API
params :optional_params do
optional :description, type: String, desc: 'The description of the merge request'
optional :assignee_id, type: Integer, desc: 'The ID of a user to assign the merge request'
optional :assignee_ids, type: Array[Integer], desc: 'The array of user IDs to assign issue'
optional :assignee_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The array of user IDs to assign issue'
optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign the merge request'
optional :labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names'
optional :labels, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of label names'
optional :remove_source_branch, type: Boolean, desc: 'Remove source branch when merging'
optional :allow_collaboration, type: Boolean, desc: 'Allow commits from members who can merge to the target branch'
optional :allow_maintainer_to_push, type: Boolean, as: :allow_collaboration, desc: '[deprecated] See allow_collaboration'
......@@ -194,7 +194,7 @@ module API
end
params do
use :merge_requests_params
optional :iids, type: Array[Integer], desc: 'The IID array of merge requests'
optional :iids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The IID array of merge requests'
end
get ":id/merge_requests" do
authorize! :read_merge_request, user_project
......
......@@ -3,7 +3,7 @@
module API
module Metrics
module Dashboard
class Annotations < Grape::API
class Annotations < Grape::API::Instance
desc 'Create a new monitoring dashboard annotation' do
success Entities::Metrics::Dashboard::Annotation
end
......
......@@ -15,7 +15,7 @@ module API
params :list_params do
optional :state, type: String, values: %w[active closed all], default: 'all',
desc: 'Return "active", "closed", or "all" milestones'
optional :iids, type: Array[Integer], desc: 'The IIDs of the milestones'
optional :iids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The IIDs of the milestones'
optional :title, type: String, desc: 'The title of the milestones'
optional :search, type: String, desc: 'The search criteria for the title or description of the milestone'
use :pagination
......
# frozen_string_literal: true
module API
class Namespaces < Grape::API
class Namespaces < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
# frozen_string_literal: true
module API
class Notes < Grape::API
class Notes < Grape::API::Instance
include PaginationParams
helpers ::API::Helpers::NotesHelpers
......
......@@ -2,7 +2,7 @@
module API
# notification_settings API
class NotificationSettings < Grape::API
class NotificationSettings < Grape::API::Instance
before { authenticate! }
helpers ::API::Helpers::MembersHelpers
......
# frozen_string_literal: true
module API
class Pages < Grape::API
class Pages < Grape::API::Instance
before do
require_pages_config_enabled!
authenticated_with_can_read_all_resources!
......
# frozen_string_literal: true
module API
class PagesDomains < Grape::API
class PagesDomains < Grape::API::Instance
include PaginationParams
PAGES_DOMAINS_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(domain: API::NO_SLASH_URL_PART_REGEX)
......
......@@ -4,7 +4,7 @@ module API
# Concern for declare pagination params.
#
# @example
# class CustomApiResource < Grape::API
# class CustomApiResource < Grape::API::Instance
# include PaginationParams
#
# params do
......
# frozen_string_literal: true
module API
class PipelineSchedules < Grape::API
class PipelineSchedules < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
# frozen_string_literal: true
module API
class Pipelines < Grape::API
class Pipelines < Grape::API::Instance
include PaginationParams
before { authenticate_non_get! }
......
# frozen_string_literal: true
module API
class ProjectClusters < Grape::API
class ProjectClusters < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
# frozen_string_literal: true
module API
class ProjectContainerRepositories < Grape::API
class ProjectContainerRepositories < Grape::API::Instance
include PaginationParams
REPOSITORY_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(
......
# frozen_string_literal: true
module API
class ProjectEvents < Grape::API
class ProjectEvents < Grape::API::Instance
include PaginationParams
include APIGuard
helpers ::API::Helpers::EventsHelpers
......
# frozen_string_literal: true
module API
class ProjectExport < Grape::API
class ProjectExport < Grape::API::Instance
helpers Helpers::RateLimiter
before do
......
# frozen_string_literal: true
module API
class ProjectHooks < Grape::API
class ProjectHooks < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
# frozen_string_literal: true
module API
class ProjectImport < Grape::API
class ProjectImport < Grape::API::Instance
include PaginationParams
MAXIMUM_FILE_SIZE = 50.megabytes
......
# frozen_string_literal: true
module API
class ProjectMilestones < Grape::API
class ProjectMilestones < Grape::API::Instance
include PaginationParams
include MilestoneResponses
......
# frozen_string_literal: true
module API
class ProjectSnapshots < Grape::API
class ProjectSnapshots < Grape::API::Instance
helpers ::API::Helpers::ProjectSnapshotsHelpers
before { authorize_read_git_snapshot! }
......
# frozen_string_literal: true
module API
class ProjectSnippets < Grape::API
class ProjectSnippets < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
# frozen_string_literal: true
module API
class ProjectStatistics < Grape::API
class ProjectStatistics < Grape::API::Instance
before do
authenticate!
authorize! :daily_statistics, user_project
......
# frozen_string_literal: true
module API
class ProjectTemplates < Grape::API
class ProjectTemplates < Grape::API::Instance
include PaginationParams
TEMPLATE_TYPES = %w[dockerfiles gitignores gitlab_ci_ymls licenses].freeze
......
......@@ -3,7 +3,7 @@
require_dependency 'declarative_policy'
module API
class Projects < Grape::API
class Projects < Grape::API::Instance
include PaginationParams
include Helpers::CustomAttributes
......@@ -520,7 +520,7 @@ module API
end
params do
optional :search, type: String, desc: 'Return list of users matching the search criteria'
optional :skip_users, type: Array[Integer], desc: 'Filter out users with the specified IDs'
optional :skip_users, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Filter out users with the specified IDs'
use :pagination
end
get ':id/users' do
......
# frozen_string_literal: true
module API
class ProtectedBranches < Grape::API
class ProtectedBranches < Grape::API::Instance
include PaginationParams
BRANCH_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(name: API::NO_SLASH_URL_PART_REGEX)
......
# frozen_string_literal: true
module API
class ProtectedTags < Grape::API
class ProtectedTags < Grape::API::Instance
include PaginationParams
TAG_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(name: API::NO_SLASH_URL_PART_REGEX)
......
......@@ -2,7 +2,7 @@
module API
module Release
class Links < Grape::API
class Links < Grape::API::Instance
include PaginationParams
RELEASE_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS
......
# frozen_string_literal: true
module API
class Releases < Grape::API
class Releases < Grape::API::Instance
include PaginationParams
RELEASE_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS
......
# frozen_string_literal: true
module API
class RemoteMirrors < Grape::API
class RemoteMirrors < Grape::API::Instance
include PaginationParams
before do
......
......@@ -3,7 +3,7 @@
require 'mime/types'
module API
class Repositories < Grape::API
class Repositories < Grape::API::Instance
include PaginationParams
before { authorize! :download_code, user_project }
......@@ -139,7 +139,7 @@ module API
success Entities::Commit
end
params do
requires :refs, type: Array[String]
requires :refs, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce
end
get ':id/repository/merge_base' do
refs = params[:refs]
......
# frozen_string_literal: true
module API
class ResourceLabelEvents < Grape::API
class ResourceLabelEvents < Grape::API::Instance
include PaginationParams
helpers ::API::Helpers::NotesHelpers
......
# frozen_string_literal: true
module API
class Runner < Grape::API
class Runner < Grape::API::Instance
helpers ::API::Helpers::Runner
resource :runners do
......@@ -18,7 +18,7 @@ module API
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 :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, 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
......
# frozen_string_literal: true
module API
class Runners < Grape::API
class Runners < Grape::API::Instance
include PaginationParams
before { authenticate! }
......@@ -17,7 +17,7 @@ module API
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'
optional :tag_list, type: Array[String], desc: 'The tags of the runners to show'
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The tags of the runners to show'
use :pagination
end
get do
......@@ -40,7 +40,7 @@ module API
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'
optional :tag_list, type: Array[String], desc: 'The tags of the runners to show'
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The tags of the runners to show'
use :pagination
end
get 'all' do
......@@ -75,7 +75,7 @@ module API
requires :id, type: Integer, desc: 'The ID of the runner'
optional :description, type: String, desc: 'The description of the runner'
optional :active, type: Boolean, desc: 'The state of a runner'
optional :tag_list, type: Array[String], desc: 'The list of tags for a runner'
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The list of tags for a runner'
optional :run_untagged, type: Boolean, desc: 'Flag indicating the runner can execute untagged jobs'
optional :locked, type: Boolean, desc: 'Flag indicating the runner is locked'
optional :access_level, type: String, values: Ci::Runner.access_levels.keys,
......@@ -145,7 +145,7 @@ module API
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'
optional :tag_list, type: Array[String], desc: 'The tags of the runners to show'
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The tags of the runners to show'
use :pagination
end
get ':id/runners' do
......@@ -208,7 +208,7 @@ module API
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'
optional :tag_list, type: Array[String], desc: 'The tags of the runners to show'
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The tags of the runners to show'
use :pagination
end
get ':id/runners' do
......
# frozen_string_literal: true
module API
class Search < Grape::API
class Search < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
# frozen_string_literal: true
module API
class Services < Grape::API
class Services < Grape::API::Instance
services = Helpers::ServicesHelpers.services
service_classes = Helpers::ServicesHelpers.service_classes
......
# frozen_string_literal: true
module API
class Settings < Grape::API
class Settings < Grape::API::Instance
before { authenticated_as_admin! }
helpers Helpers::SettingsHelpers
......@@ -49,7 +49,7 @@ module API
optional :default_project_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default project visibility'
optional :default_projects_limit, type: Integer, desc: 'The maximum number of personal projects'
optional :default_snippet_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default snippet visibility'
optional :disabled_oauth_sign_in_sources, type: Array[String], desc: 'Disable certain OAuth sign-in sources'
optional :disabled_oauth_sign_in_sources, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Disable certain OAuth sign-in sources'
optional :domain_blacklist_enabled, type: Boolean, desc: 'Enable domain blacklist for sign ups'
optional :domain_blacklist, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com'
optional :domain_whitelist, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com'
......@@ -79,7 +79,8 @@ module API
requires :housekeeping_incremental_repack_period, type: Integer, desc: "Number of Git pushes after which an incremental 'git repack' is run."
end
optional :html_emails_enabled, type: Boolean, desc: 'By default GitLab sends emails in HTML and plain text formats so mail clients can choose what format to use. Disable this option if you only want to send emails in plain text format.'
optional :import_sources, type: Array[String], values: %w[github bitbucket bitbucket_server gitlab google_code fogbugz git gitlab_project gitea manifest phabricator],
optional :import_sources, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
values: %w[github bitbucket bitbucket_server gitlab google_code fogbugz git gitlab_project gitea manifest phabricator],
desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com'
optional :max_artifacts_size, type: Integer, desc: "Set the maximum file size for each job's artifacts"
optional :max_attachment_size, type: Integer, desc: 'Maximum attachment size in MB'
......@@ -121,12 +122,12 @@ module API
requires :recaptcha_private_key, type: String, desc: 'Generate private key at http://www.google.com/recaptcha'
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 :repository_storages, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Storage paths for new projects'
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
optional :restricted_visibility_levels, type: Array[String], desc: 'Selected levels cannot be used by non-admin users for groups, projects or snippets. If the public level is restricted, user profiles are only visible to logged in users.'
optional :restricted_visibility_levels, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Selected levels cannot be used by non-admin users for groups, projects or snippets. If the public level is restricted, user profiles are only visible to logged in users.'
optional :send_user_confirmation_email, type: Boolean, desc: 'Send confirmation email on sign-up'
optional :session_expire_delay, type: Integer, desc: 'Session duration in minutes. GitLab restart is required to apply changes.'
optional :shared_runners_enabled, type: Boolean, desc: 'Enable shared runners for new projects'
......
......@@ -3,7 +3,7 @@
require 'sidekiq/api'
module API
class SidekiqMetrics < Grape::API
class SidekiqMetrics < Grape::API::Instance
before { authenticated_as_admin! }
helpers do
......
......@@ -2,7 +2,7 @@
module API
# Snippets API
class Snippets < Grape::API
class Snippets < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
# frozen_string_literal: true
module API
class Statistics < Grape::API
class Statistics < Grape::API::Instance
before { authenticated_as_admin! }
COUNTED_ITEMS = [Project, User, Group, ForkNetworkMember, ForkNetwork, Issue,
......
# frozen_string_literal: true
module API
class Submodules < Grape::API
class Submodules < Grape::API::Instance
before { authenticate! }
helpers do
......
# frozen_string_literal: true
module API
class Subscriptions < Grape::API
class Subscriptions < Grape::API::Instance
helpers ::API::Helpers::LabelHelpers
before { authenticate! }
......
# frozen_string_literal: true
module API
class Suggestions < Grape::API
class Suggestions < Grape::API::Instance
before { authenticate! }
resource :suggestions do
......
# frozen_string_literal: true
module API
class SystemHooks < Grape::API
class SystemHooks < Grape::API::Instance
include PaginationParams
before do
......
# frozen_string_literal: true
module API
class Tags < Grape::API
class Tags < Grape::API::Instance
include PaginationParams
TAG_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(tag_name: API::NO_SLASH_URL_PART_REGEX)
......
# frozen_string_literal: true
module API
class Templates < Grape::API
class Templates < Grape::API::Instance
include PaginationParams
GLOBAL_TEMPLATE_TYPES = {
......
......@@ -4,7 +4,7 @@ require_dependency 'api/validations/validators/limit'
module API
module Terraform
class State < Grape::API
class State < Grape::API::Instance
include ::Gitlab::Utils::StrongMemoize
default_format :json
......
# frozen_string_literal: true
module API
class Todos < Grape::API
class Todos < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
# frozen_string_literal: true
module API
class Triggers < Grape::API
class Triggers < Grape::API::Instance
include PaginationParams
HTTP_GITLAB_EVENT_HEADER = "HTTP_#{WebHookService::GITLAB_EVENT_HEADER}".underscore.upcase
......
# frozen_string_literal: true
module API
class UserCounts < Grape::API
class UserCounts < Grape::API::Instance
resource :user_counts do
desc 'Return the user specific counts' do
detail 'Open MR Count'
......
# frozen_string_literal: true
module API
class Users < Grape::API
class Users < Grape::API::Instance
include PaginationParams
include APIGuard
include Helpers::CustomAttributes
......
......@@ -10,7 +10,7 @@ module API
when String
value.split(',').map(&:strip)
when Array
value.map { |v| v.to_s.split(',').map(&:strip) }.flatten
value.flat_map { |v| v.to_s.split(',').map(&:strip) }
else
[]
end
......
......@@ -3,19 +3,10 @@
module API
module Validations
module Types
class LabelsList
class CommaSeparatedToIntegerArray < CommaSeparatedToArray
def self.coerce
lambda do |value|
case value
when String
value.split(',').map(&:strip)
when Array
value.flat_map { |v| v.to_s.split(',').map(&:strip) }
when LabelsList
value
else
[]
end
super.call(value).map(&:to_i)
end
end
end
......
# frozen_string_literal: true
# This module overrides the Grape type validator defined in
# https://github.com/ruby-grape/grape/blob/master/lib/grape/validations/types/file.rb
module API
module Validations
module Types
class SafeFile < ::Grape::Validations::Types::File
def value_coerced?(value)
super && value[:tempfile].is_a?(Tempfile)
end
end
end
end
end
......@@ -3,15 +3,14 @@
module API
module Validations
module Types
class WorkhorseFile < Virtus::Attribute
def coerce(input)
# Processing of multipart file objects
# is already taken care of by Gitlab::Middleware::Multipart.
# Nothing to do here.
input
class WorkhorseFile
def self.parse(value)
raise "#{value.class} is not an UploadedFile type" unless parsed?(value)
value
end
def value_coerced?(value)
def self.parsed?(value)
value.is_a?(::UploadedFile)
end
end
......
# frozen_string_literal: true
module API
class Variables < Grape::API
class Variables < Grape::API::Instance
include PaginationParams
before { authenticate! }
......
# frozen_string_literal: true
module API
class Version < Grape::API
class Version < Grape::API::Instance
helpers ::API::Helpers::GraphqlHelpers
include APIGuard
......
# frozen_string_literal: true
module API
class Wikis < Grape::API
class Wikis < Grape::API::Instance
helpers do
def commit_params(attrs)
# In order to avoid service disruption this can work with an old workhorse without the acceleration
......@@ -117,7 +117,7 @@ module API
success Entities::WikiAttachment
end
params do
requires :file, types: [::API::Validations::Types::SafeFile, ::API::Validations::Types::WorkhorseFile], desc: 'The attachment file to be uploaded'
requires :file, types: [Rack::Multipart::UploadedFile, ::API::Validations::Types::WorkhorseFile], desc: 'The attachment file to be uploaded'
optional :branch, type: String, desc: 'The name of the branch'
end
post ":id/wikis/attachments" do
......
# frozen_string_literal: true
module RuboCop
module Cop
module API
class GrapeAPIInstance < RuboCop::Cop::Cop
# This cop checks that APIs subclass Grape::API::Instance with Grape v1.3+.
#
# @example
#
# # bad
# module API
# class Projects < Grape::API
# end
# end
#
# # good
# module API
# class Projects < Grape::API::Instance
# end
# end
MSG = 'Inherit from Grape::API::Instance instead of Grape::API. ' \
'For more details check the https://gitlab.com/gitlab-org/gitlab/-/issues/215230.'
def_node_matcher :grape_api_definition, <<~PATTERN
(class
(const _ _)
(const
(const nil? :Grape) :API)
...
)
PATTERN
def on_class(node)
grape_api_definition(node) do
add_offense(node.children[1])
end
end
end
end
end
end
# frozen_string_literal: true
module RuboCop
module Cop
module API
class GrapeArrayMissingCoerce < RuboCop::Cop::Cop
# This cop checks that Grape API parameters using an Array type
# implement a coerce_with method:
#
# https://github.com/ruby-grape/grape/blob/master/UPGRADING.md#ensure-that-array-types-have-explicit-coercions
#
# @example
#
# # bad
# requires :values, type: Array[String]
#
# # good
# requires :values, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce
#
# end
MSG = 'This Grape parameter defines an Array but is missing a coerce_with definition. ' \
'For more details, see https://github.com/ruby-grape/grape/blob/master/UPGRADING.md#ensure-that-array-types-have-explicit-coercions'
def_node_matcher :grape_api_instance?, <<~PATTERN
(class
(const _ _)
(const
(const
(const nil? :Grape) :API) :Instance)
...
)
PATTERN
def_node_matcher :grape_api_param_block?, <<~PATTERN
(send _ {:requires :optional}
(sym _)
$_)
PATTERN
def_node_matcher :grape_type_def?, <<~PATTERN
(sym :type)
PATTERN
def_node_matcher :grape_array_type?, <<~PATTERN
(send
(const nil? :Array) :[]
(const nil? _))
PATTERN
def_node_matcher :grape_coerce_with?, <<~PATTERN
(sym :coerce_with)
PATTERN
def on_class(node)
@grape_api ||= grape_api_instance?(node)
end
def on_send(node)
return unless @grape_api
match = grape_api_param_block?(node)
return unless match.is_a?(RuboCop::AST::HashNode)
is_array_type = false
has_coerce_method = false
match.each_pair do |first, second|
has_coerce_method ||= grape_coerce_with?(first)
if grape_type_def?(first) && grape_array_type?(second)
is_array_type = true
end
end
if is_array_type && !has_coerce_method
add_offense(node)
end
end
end
end
end
end
......@@ -60,14 +60,14 @@ describe API::Settings, 'Settings' do
default_projects_limit: 3,
default_project_creation: 2,
password_authentication_enabled_for_web: false,
repository_storages: ['custom'],
repository_storages: 'custom',
plantuml_enabled: true,
plantuml_url: 'http://plantuml.example.com',
sourcegraph_enabled: true,
sourcegraph_url: 'https://sourcegraph.com',
sourcegraph_public_only: false,
default_snippet_visibility: 'internal',
restricted_visibility_levels: ['public'],
restricted_visibility_levels: 'public',
default_artifacts_expire_in: '2 days',
help_page_text: 'custom help text',
help_page_hide_commercial_content: true,
......@@ -89,7 +89,9 @@ describe API::Settings, 'Settings' do
push_event_hooks_limit: 2,
push_event_activities_limit: 2,
snippet_size_limit: 5,
issues_create_limit: 300
issues_create_limit: 300,
disabled_oauth_sign_in_sources: 'unknown',
import_sources: 'github,bitbucket'
}
expect(response).to have_gitlab_http_status(:ok)
......@@ -127,6 +129,8 @@ describe API::Settings, 'Settings' do
expect(json_response['push_event_activities_limit']).to eq(2)
expect(json_response['snippet_size_limit']).to eq(5)
expect(json_response['issues_create_limit']).to eq(300)
expect(json_response['disabled_oauth_sign_in_sources']).to eq([])
expect(json_response['import_sources']).to match_array(%w(github bitbucket))
end
end
......
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../support/helpers/expect_offense'
require_relative '../../../../rubocop/cop/api/grape_api_instance'
describe RuboCop::Cop::API::GrapeAPIInstance do
include CopHelper
include ExpectOffense
subject(:cop) { described_class.new }
it 'adds an offense when inheriting from Grape::API' do
inspect_source(<<~CODE.strip_indent)
class SomeAPI < Grape::API
end
CODE
expect(cop.offenses.size).to eq(1)
end
it 'does not add an offense when inheriting from Grape::API::Instance' do
inspect_source(<<~CODE.strip_indent)
class SomeAPI < Grape::API::Instance
end
CODE
expect(cop.offenses.size).to be_zero
end
end
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../support/helpers/expect_offense'
require_relative '../../../../rubocop/cop/api/grape_array_missing_coerce'
describe RuboCop::Cop::API::GrapeArrayMissingCoerce do
include CopHelper
include ExpectOffense
subject(:cop) { described_class.new }
it 'adds an offense with a required parameter' do
inspect_source(<<~CODE.strip_indent)
class SomeAPI < Grape::API::Instance
params do
requires :values, type: Array[String]
end
end
CODE
expect(cop.offenses.size).to eq(1)
end
it 'adds an offense with an optional parameter' do
inspect_source(<<~CODE.strip_indent)
class SomeAPI < Grape::API::Instance
params do
optional :values, type: Array[String]
end
end
CODE
expect(cop.offenses.size).to eq(1)
end
it 'does not add an offense' do
inspect_source(<<~CODE.strip_indent)
class SomeAPI < Grape::API::Instance
params do
requires :values, type: Array[String], coerce_with: ->(val) { val.split(',').map(&:strip) }
requires :milestone, type: String, desc: 'Milestone title'
optional :assignee_id, types: [Integer, String], integer_none_any: true,
desc: 'Return issues which are assigned to the user with the given ID'
end
end
CODE
expect(cop.offenses.size).to be_zero
end
it 'does not add an offense for unrelated classes' do
inspect_source(<<~CODE.strip_indent)
class SomeClass
params do
requires :values, type: Array[String]
end
end
CODE
expect(cop.offenses.size).to be_zero
end
end
......@@ -31,7 +31,7 @@ describe RuboCop::Cop::CodeReuse::Worker do
.and_return(true)
expect_offense(<<~SOURCE)
class Foo < Grape::API
class Foo < Grape::API::Instance
resource :projects do
get '/' do
FooWorker.perform_async
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment