Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-03-03 06:11:13 +00:00
parent 8d9c82762d
commit ccbe90951f
19 changed files with 744 additions and 31 deletions

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
module Admin
class PlansFinder
attr_reader :params
def initialize(params = {})
@params = params
end
def execute
plans = Plan.all
by_name(plans)
end
private
def by_name(plans)
return plans unless params[:name]
Plan.find_by(name: params[:name]) # rubocop: disable CodeReuse/ActiveRecord
end
end
end

View File

@ -22,11 +22,14 @@ module AvatarsHelper
end
def avatar_icon_for_email(email = nil, size = nil, scale = 2, only_path: true)
user = User.find_by_any_email(email)
if user
avatar_icon_for_user(user, size, scale, only_path: only_path)
return gravatar_icon(email, size, scale) if email.nil?
if Feature.enabled?(:avatar_cache_for_email, @current_user, type: :development)
Gitlab::AvatarCache.by_email(email, size, scale, only_path) do
avatar_icon_by_user_email_or_gravatar(email, size, scale, only_path: only_path)
end
else
gravatar_icon(email, size, scale)
avatar_icon_by_user_email_or_gravatar(email, size, scale, only_path: only_path)
end
end
@ -101,19 +104,23 @@ module AvatarsHelper
private
def user_avatar_url_for(only_path: true, **options)
return options[:url] if options[:url]
email = options[:user_email]
user = options.key?(:user) ? options[:user] : User.find_by_any_email(email)
def avatar_icon_by_user_email_or_gravatar(email, size, scale, only_path:)
user = User.find_by_any_email(email)
if user
avatar_icon_for_user(user, options[:size], only_path: only_path)
avatar_icon_for_user(user, size, scale, only_path: only_path)
else
gravatar_icon(email, options[:size])
gravatar_icon(email, size, scale)
end
end
def user_avatar_url_for(only_path: true, **options)
return options[:url] if options[:url]
return avatar_icon_for_user(options[:user], options[:size], only_path: only_path) if options[:user]
avatar_icon_for_email(options[:user_email], options[:size], only_path: only_path)
end
def source_icon(source, options = {})
avatar_url = source.try(:avatar_url)

View File

@ -20,6 +20,7 @@ module Avatarable
mount_uploader :avatar, AvatarUploader
after_initialize :add_avatar_to_batch
after_commit :clear_avatar_caches
end
module ShadowMethods
@ -127,4 +128,11 @@ module Avatarable
def avatar_mounter
strong_memoize(:avatar_mounter) { _mounter(:avatar) }
end
def clear_avatar_caches
return unless respond_to?(:verified_emails) && verified_emails.any? && avatar_changed?
return unless Feature.enabled?(:avatar_cache_for_email, self, type: :development)
Gitlab::AvatarCache.delete_by_email(*verified_emails)
end
end

View File

@ -0,0 +1,5 @@
---
title: Add API endpoint /application/plan_limits for package file size limits
merge_request: 54232
author: Jonas Wälter @wwwjon
type: added

View File

@ -0,0 +1,8 @@
---
name: avatar_cache_for_email
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55184
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/323185
milestone: '13.10'
type: development
group: group::source code
default_enabled: false

View File

@ -155,6 +155,7 @@ The following API resources are available outside of project and group contexts
| [Namespaces](namespaces.md) | `/namespaces` |
| [Notification settings](notification_settings.md) | `/notification_settings` (also available for groups and projects) |
| [Pages domains](pages_domains.md) | `/pages/domains` (also available for projects) |
| [Plan limits](plan_limits.md) | `/application/plan_limits` |
| [Personal access tokens](personal_access_tokens.md) | `/personal_access_tokens` |
| [Projects](projects.md) | `/users/:id/projects` (also available for projects) |
| [Project repository storage moves](project_repository_storage_moves.md) **(FREE SELF)** | `/project_repository_storage_moves` |

81
doc/api/plan_limits.md Normal file
View File

@ -0,0 +1,81 @@
---
stage: Manage
group: Access
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Plan limits API **(FREE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54232) in GitLab 13.10.
The plan limits API allows you to maintain the application limits for the existing subscription plans.
The existing plans depend on the GitLab edition. In the Community Edition, only the plan `default`
is available. In the Enterprise Edition, additional plans are available as well.
NOTE:
Administrator access is required to use this API.
## Get current plan limits
List the current limits of a plan on the GitLab instance.
```plaintext
GET /application/plan_limits
```
| Attribute | Type | Required | Description |
| --------------------------------- | ------- | -------- | ----------- |
| `plan_name` | string | no | Name of the plan to get the limits from. Default: `default`. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/application/plan_limits"
```
Example response:
```json
{
"conan_max_file_size": 3221225472,
"generic_packages_max_file_size": 5368709120,
"maven_max_file_size": 3221225472,
"npm_max_file_size": 524288000,
"nuget_max_file_size": 524288000,
"pypi_max_file_size": 3221225472
}
```
## Change plan limits
Modify the limits of a plan on the GitLab instance.
```plaintext
PUT /application/plan_limits
```
| Attribute | Type | Required | Description |
| --------------------------------- | ------- | -------- | ----------- |
| `plan_name` | string | yes | Name of the plan to update. |
| `conan_max_file_size` | integer | no | Maximum Conan package file size in bytes. |
| `generic_packages_max_file_size` | integer | no | Maximum generic package file size in bytes. |
| `maven_max_file_size` | integer | no | Maximum Maven package file size in bytes. |
| `npm_max_file_size` | integer | no | Maximum NPM package file size in bytes. |
| `nuget_max_file_size` | integer | no | Maximum NuGet package file size in bytes. |
| `pypi_max_file_size` | integer | no | Maximum PyPI package file size in bytes. |
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/application/plan_limits?plan_name=default&conan_max_file_size=3221225472"
```
Example response:
```json
{
"conan_max_file_size": 3221225472,
"generic_packages_max_file_size": 5368709120,
"maven_max_file_size": 3221225472,
"npm_max_file_size": 524288000,
"nuget_max_file_size": 524288000,
"pypi_max_file_size": 3221225472
}
```

View File

@ -400,3 +400,8 @@ listed in the descriptions of the relevant settings.
| `version_check_enabled` | boolean | no | Let GitLab inform you when an update is available. |
| `web_ide_clientside_preview_enabled` | boolean | no | Live Preview (allow live previews of JavaScript projects in the Web IDE using CodeSandbox Live Preview). |
| `wiki_page_max_content_bytes` | integer | no | Maximum wiki page content size in **bytes**. Default: 52428800 Bytes (50 MB). The minimum value is 1024 bytes. |
### Package Registry: Package file size limits
The package file size limits are not part of the Application settings API.
Instead, these settings can be accessed using the [Plan limits API](plan_limits.md).

View File

@ -0,0 +1,57 @@
# frozen_string_literal: true
module API
module Admin
class PlanLimits < ::API::Base
before { authenticated_as_admin! }
feature_category :not_owned
helpers do
def current_plan(name)
plan = ::Admin::PlansFinder.new({ name: name }).execute
not_found!('Plan') unless plan
plan
end
end
desc 'Get current plan limits' do
success Entities::PlanLimit
end
params do
optional :plan_name, type: String, values: Plan.all_plans, default: Plan::DEFAULT, desc: 'Name of the plan'
end
get "application/plan_limits" do
params = declared_params(include_missing: false)
plan = current_plan(params.delete(:plan_name))
present plan.actual_limits, with: Entities::PlanLimit
end
desc 'Modify plan limits' do
success Entities::PlanLimit
end
params do
requires :plan_name, type: String, values: Plan.all_plans, desc: 'Name of the plan'
optional :conan_max_file_size, type: Integer, desc: 'Maximum Conan package file size in bytes'
optional :generic_packages_max_file_size, type: Integer, desc: 'Maximum generic package file size in bytes'
optional :maven_max_file_size, type: Integer, desc: 'Maximum Maven package file size in bytes'
optional :npm_max_file_size, type: Integer, desc: 'Maximum NPM package file size in bytes'
optional :nuget_max_file_size, type: Integer, desc: 'Maximum NuGet package file size in bytes'
optional :pypi_max_file_size, type: Integer, desc: 'Maximum PyPI package file size in bytes'
end
put "application/plan_limits" do
params = declared_params(include_missing: false)
plan = current_plan(params.delete(:plan_name))
if plan.actual_limits.update(params)
present plan.actual_limits, with: Entities::PlanLimit
else
render_validation_error!(plan.actual_limits)
end
end
end
end
end

View File

@ -169,6 +169,7 @@ module API
mount ::API::AccessRequests
mount ::API::Admin::Ci::Variables
mount ::API::Admin::InstanceClusters
mount ::API::Admin::PlanLimits
mount ::API::Admin::Sidekiq
mount ::API::Appearance
mount ::API::Applications

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
module API
module Entities
class PlanLimit < Grape::Entity
expose :conan_max_file_size
expose :generic_packages_max_file_size
expose :maven_max_file_size
expose :npm_max_file_size
expose :nuget_max_file_size
expose :pypi_max_file_size
end
end
end

View File

@ -0,0 +1,86 @@
# frozen_string_literal: true
module Gitlab
class AvatarCache
class << self
# Increment this if a breaking change requires
# immediate cache expiry of all avatar caches.
#
# @return [Integer]
VERSION = 1
# @return [Symbol]
BASE_KEY = :avatar_cache
# @return [ActiveSupport::Duration]
DEFAULT_EXPIRY = 7.days
# Look up cached avatar data by email address.
# This accepts a block to provide the value to be
# cached in the event nothing is found.
#
# Multiple calls in the same request will be served from the
# request store.
#
# @param email [String]
# @param additional_keys [*Object] all must respond to `#to_s`
# @param expires_in [ActiveSupport::Duration, Integer]
# @yield [email, *additional_keys] yields the supplied params back to the block
# @return [String]
def by_email(email, *additional_keys, expires_in: DEFAULT_EXPIRY)
key = email_key(email)
subkey = additional_keys.join(":")
Gitlab::SafeRequestStore.fetch([key, subkey]) do
with do |redis|
# Look for existing cache value
cached = redis.hget(key, subkey)
# Return the cached entry if set
break cached unless cached.nil?
# Otherwise, call the block to get the value
to_cache = yield(email, *additional_keys).to_s
# Set it in the cache
redis.hset(key, subkey, to_cache)
# Update the expiry time
redis.expire(key, expires_in)
# Return this new value
break to_cache
end
end
end
# Remove one or more emails from the cache
#
# @param emails [String] one or more emails to delete
# @return [Integer] the number of keys deleted
def delete_by_email(*emails)
return 0 if emails.empty?
with do |redis|
keys = emails.map { |email| email_key(email) }
Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
redis.unlink(*keys)
end
end
end
private
# @param email [String]
# @return [String]
def email_key(email)
"#{BASE_KEY}:v#{VERSION}:#{email}"
end
def with(&blk)
Gitlab::Redis::Cache.with(&blk) # rubocop:disable CodeReuse/ActiveRecord
end
end
end
end

View File

@ -85,6 +85,7 @@ RSpec.describe 'Member autocomplete', :js do
let(:note) { create(:note_on_commit, project: project, commit_id: project.commit.id) }
before do
allow(User).to receive(:find_by_any_email).and_call_original
allow(User).to receive(:find_by_any_email)
.with(noteable.author_email.downcase, confirmed: true).and_return(author)

View File

@ -0,0 +1,54 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Admin::PlansFinder do
let_it_be(:plan1) { create(:plan, name: 'plan1') }
let_it_be(:plan2) { create(:plan, name: 'plan2') }
describe '#execute' do
context 'with no params' do
it 'returns all plans' do
found = described_class.new.execute
expect(found).to match_array([plan1, plan2])
end
end
context 'with missing name in params' do
before do
@params = { title: 'plan2' }
end
it 'returns all plans' do
found = described_class.new(@params).execute
expect(found).to match_array([plan1, plan2])
end
end
context 'with existing name in params' do
before do
@params = { name: 'plan2' }
end
it 'returns the plan' do
found = described_class.new(@params).execute
expect(found).to match(plan2)
end
end
context 'with non-existing name in params' do
before do
@params = { name: 'non-existing-plan' }
end
it 'returns nil' do
found = described_class.new(@params).execute
expect(found).to be_nil
end
end
end
end

View File

@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe AvatarsHelper do
include UploadHelpers
let(:user) { create(:user) }
let_it_be(:user) { create(:user) }
describe '#project_icon & #group_icon' do
shared_examples 'resource with a default avatar' do |source_type|
@ -89,33 +89,60 @@ RSpec.describe AvatarsHelper do
end
end
describe '#avatar_icon_for_email' do
describe '#avatar_icon_for_email', :clean_gitlab_redis_cache do
let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) }
context 'using an email' do
context 'when there is a matching user' do
it 'returns a relative URL for the avatar' do
expect(helper.avatar_icon_for_email(user.email).to_s)
.to eq(user.avatar.url)
subject { helper.avatar_icon_for_email(user.email).to_s }
shared_examples "returns avatar for email" do
context 'using an email' do
context 'when there is a matching user' do
it 'returns a relative URL for the avatar' do
expect(subject).to eq(user.avatar.url)
end
end
context 'when no user exists for the email' do
it 'calls gravatar_icon' do
expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2)
helper.avatar_icon_for_email('foo@example.com', 20, 2)
end
end
context 'without an email passed' do
it 'calls gravatar_icon' do
expect(helper).to receive(:gravatar_icon).with(nil, 20, 2)
expect(User).not_to receive(:find_by_any_email)
helper.avatar_icon_for_email(nil, 20, 2)
end
end
end
end
context 'when no user exists for the email' do
it 'calls gravatar_icon' do
expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2)
helper.avatar_icon_for_email('foo@example.com', 20, 2)
end
context "when :avatar_cache_for_email flag is enabled" do
before do
stub_feature_flags(avatar_cache_for_email: true)
end
context 'without an email passed' do
it 'calls gravatar_icon' do
expect(helper).to receive(:gravatar_icon).with(nil, 20, 2)
it_behaves_like "returns avatar for email"
helper.avatar_icon_for_email(nil, 20, 2)
end
it "caches the request" do
expect(User).to receive(:find_by_any_email).once.and_call_original
expect(helper.avatar_icon_for_email(user.email).to_s).to eq(user.avatar.url)
expect(helper.avatar_icon_for_email(user.email).to_s).to eq(user.avatar.url)
end
end
context "when :avatar_cache_for_email flag is disabled" do
before do
stub_feature_flags(avatar_cache_for_email: false)
end
it_behaves_like "returns avatar for email"
end
end
describe '#avatar_icon_for_user' do
@ -346,7 +373,7 @@ RSpec.describe AvatarsHelper do
is_expected.to eq tag(
:img,
alt: "#{options[:user_name]}'s avatar",
src: avatar_icon_for_email(options[:user_email], 16),
src: helper.avatar_icon_for_email(options[:user_email], 16),
data: { container: 'body' },
class: "avatar s16 has-tooltip",
title: options[:user_name]
@ -379,7 +406,7 @@ RSpec.describe AvatarsHelper do
is_expected.to eq tag(
:img,
alt: "#{user_with_avatar.username}'s avatar",
src: avatar_icon_for_email(user_with_avatar.email, 16, only_path: false),
src: helper.avatar_icon_for_email(user_with_avatar.email, 16, only_path: false),
data: { container: 'body' },
class: "avatar s16 has-tooltip",
title: user_with_avatar.username

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::Entities::PlanLimit do
let(:plan_limits) { create(:plan_limits) }
subject { described_class.new(plan_limits).as_json }
it 'exposes correct attributes' do
expect(subject).to include(
:conan_max_file_size,
:generic_packages_max_file_size,
:maven_max_file_size,
:npm_max_file_size,
:nuget_max_file_size,
:pypi_max_file_size
)
end
it 'does not expose id and plan_id' do
expect(subject).not_to include(:id, :plan_id)
end
end

View File

@ -0,0 +1,101 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe Gitlab::AvatarCache, :clean_gitlab_redis_cache do
def with(&blk)
Gitlab::Redis::Cache.with(&blk) # rubocop:disable CodeReuse/ActiveRecord
end
def read(key, subkey)
with do |redis|
redis.hget(key, subkey)
end
end
let(:thing) { double("thing", avatar_path: avatar_path) }
let(:avatar_path) { "/avatars/my_fancy_avatar.png" }
let(:key) { described_class.send(:email_key, "foo@bar.com") }
let(:perform_fetch) do
described_class.by_email("foo@bar.com", 20, 2, true) do
thing.avatar_path
end
end
describe "#by_email" do
it "writes a new value into the cache" do
expect(read(key, "20:2:true")).to eq(nil)
perform_fetch
expect(read(key, "20:2:true")).to eq(avatar_path)
end
it "finds the cached value and doesn't execute the block" do
expect(thing).to receive(:avatar_path).once
described_class.by_email("foo@bar.com", 20, 2, true) do
thing.avatar_path
end
described_class.by_email("foo@bar.com", 20, 2, true) do
thing.avatar_path
end
end
it "finds the cached value in the request store and doesn't execute the block" do
expect(thing).to receive(:avatar_path).once
Gitlab::WithRequestStore.with_request_store do
described_class.by_email("foo@bar.com", 20, 2, true) do
thing.avatar_path
end
described_class.by_email("foo@bar.com", 20, 2, true) do
thing.avatar_path
end
expect(Gitlab::SafeRequestStore.read([key, "20:2:true"])).to eq(avatar_path)
end
end
end
describe "#delete_by_email" do
subject { described_class.delete_by_email(*emails) }
before do
perform_fetch
end
context "no emails, somehow" do
let(:emails) { [] }
it { is_expected.to eq(0) }
end
context "single email" do
let(:emails) { "foo@bar.com" }
it "removes the email" do
expect(read(key, "20:2:true")).to eq(avatar_path)
expect(subject).to eq(1)
expect(read(key, "20:2:true")).to eq(nil)
end
end
context "multiple emails" do
let(:emails) { ["foo@bar.com", "missing@baz.com"] }
it "removes the emails it finds" do
expect(read(key, "20:2:true")).to eq(avatar_path)
expect(subject).to eq(1)
expect(read(key, "20:2:true")).to eq(nil)
end
end
end
end

View File

@ -2499,6 +2499,38 @@ RSpec.describe User do
end
end
describe "#clear_avatar_caches" do
let(:user) { create(:user) }
context "when :avatar_cache_for_email flag is enabled" do
before do
stub_feature_flags(avatar_cache_for_email: true)
end
it "clears the avatar cache when saving" do
allow(user).to receive(:avatar_changed?).and_return(true)
expect(Gitlab::AvatarCache).to receive(:delete_by_email).with(*user.verified_emails)
user.update(avatar: fixture_file_upload('spec/fixtures/dk.png'))
end
end
context "when :avatar_cache_for_email flag is disabled" do
before do
stub_feature_flags(avatar_cache_for_email: false)
end
it "doesn't attempt to clear the avatar cache" do
allow(user).to receive(:avatar_changed?).and_return(true)
expect(Gitlab::AvatarCache).not_to receive(:delete_by_email)
user.update(avatar: fixture_file_upload('spec/fixtures/dk.png'))
end
end
end
describe '#accept_pending_invitations!' do
let(:user) { create(:user, email: 'user@email.com') }
let!(:project_member_invite) { create(:project_member, :invited, invite_email: user.email) }

View File

@ -0,0 +1,177 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::Admin::PlanLimits, 'PlanLimits' do
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
let_it_be(:plan) { create(:plan, name: 'default') }
describe 'GET /application/plan_limits' do
context 'as a non-admin user' do
it 'returns 403' do
get api('/application/plan_limits', user)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'as an admin user' do
context 'no params' do
it 'returns plan limits' do
get api('/application/plan_limits', admin)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Hash
expect(json_response['conan_max_file_size']).to eq(Plan.default.actual_limits.conan_max_file_size)
expect(json_response['generic_packages_max_file_size']).to eq(Plan.default.actual_limits.generic_packages_max_file_size)
expect(json_response['maven_max_file_size']).to eq(Plan.default.actual_limits.maven_max_file_size)
expect(json_response['npm_max_file_size']).to eq(Plan.default.actual_limits.npm_max_file_size)
expect(json_response['nuget_max_file_size']).to eq(Plan.default.actual_limits.nuget_max_file_size)
expect(json_response['pypi_max_file_size']).to eq(Plan.default.actual_limits.pypi_max_file_size)
end
end
context 'correct plan name in params' do
before do
@params = { plan_name: 'default' }
end
it 'returns plan limits' do
get api('/application/plan_limits', admin), params: @params
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Hash
expect(json_response['conan_max_file_size']).to eq(Plan.default.actual_limits.conan_max_file_size)
expect(json_response['generic_packages_max_file_size']).to eq(Plan.default.actual_limits.generic_packages_max_file_size)
expect(json_response['maven_max_file_size']).to eq(Plan.default.actual_limits.maven_max_file_size)
expect(json_response['npm_max_file_size']).to eq(Plan.default.actual_limits.npm_max_file_size)
expect(json_response['nuget_max_file_size']).to eq(Plan.default.actual_limits.nuget_max_file_size)
expect(json_response['pypi_max_file_size']).to eq(Plan.default.actual_limits.pypi_max_file_size)
end
end
context 'invalid plan name in params' do
before do
@params = { plan_name: 'my-plan' }
end
it 'returns validation error' do
get api('/application/plan_limits', admin), params: @params
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq('plan_name does not have a valid value')
end
end
end
end
describe 'PUT /application/plan_limits' do
context 'as a non-admin user' do
it 'returns 403' do
put api('/application/plan_limits', user), params: { plan_name: 'default' }
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'as an admin user' do
context 'correct params' do
it 'updates multiple plan limits' do
put api('/application/plan_limits', admin), params: {
'plan_name': 'default',
'conan_max_file_size': 10,
'generic_packages_max_file_size': 20,
'maven_max_file_size': 30,
'npm_max_file_size': 40,
'nuget_max_file_size': 50,
'pypi_max_file_size': 60
}
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Hash
expect(json_response['conan_max_file_size']).to eq(10)
expect(json_response['generic_packages_max_file_size']).to eq(20)
expect(json_response['maven_max_file_size']).to eq(30)
expect(json_response['npm_max_file_size']).to eq(40)
expect(json_response['nuget_max_file_size']).to eq(50)
expect(json_response['pypi_max_file_size']).to eq(60)
end
it 'updates single plan limits' do
put api('/application/plan_limits', admin), params: {
'plan_name': 'default',
'maven_max_file_size': 100
}
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Hash
expect(json_response['maven_max_file_size']).to eq(100)
end
end
context 'empty params' do
it 'fails to update plan limits' do
put api('/application/plan_limits', admin), params: {}
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to match('plan_name is missing')
end
end
context 'params with wrong type' do
it 'fails to update plan limits' do
put api('/application/plan_limits', admin), params: {
'plan_name': 'default',
'conan_max_file_size': 'a',
'generic_packages_max_file_size': 'b',
'maven_max_file_size': 'c',
'npm_max_file_size': 'd',
'nuget_max_file_size': 'e',
'pypi_max_file_size': 'f'
}
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to include(
'conan_max_file_size is invalid',
'generic_packages_max_file_size is invalid',
'maven_max_file_size is invalid',
'generic_packages_max_file_size is invalid',
'npm_max_file_size is invalid',
'nuget_max_file_size is invalid',
'pypi_max_file_size is invalid'
)
end
end
context 'missing plan_name in params' do
it 'fails to update plan limits' do
put api('/application/plan_limits', admin), params: { 'conan_max_file_size': 0 }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to match('plan_name is missing')
end
end
context 'additional undeclared params' do
before do
Plan.default.actual_limits.update!({ 'golang_max_file_size': 1000 })
end
it 'updates only declared plan limits' do
put api('/application/plan_limits', admin), params: {
'plan_name': 'default',
'pypi_max_file_size': 200,
'golang_max_file_size': 999
}
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Hash
expect(json_response['pypi_max_file_size']).to eq(200)
expect(json_response['golang_max_file_size']).to be_nil
expect(Plan.default.actual_limits.golang_max_file_size).to eq(1000)
end
end
end
end
end