Commit 954aaecf authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch 'al-39083-move-snippets-services-to-own-namespace' into 'master'

Move snippets services to their own namespace

See merge request gitlab-org/gitlab!22287
parents 6b2078dc 19f470ed
......@@ -46,8 +46,8 @@ class Projects::SnippetsController < Projects::ApplicationController
def create
create_params = snippet_params.merge(spammable_params)
@snippet = CreateSnippetService.new(@project, current_user, create_params).execute
service_response = Snippets::CreateService.new(project, current_user, create_params).execute
@snippet = service_response.payload[:snippet]
recaptcha_check_with_fallback { render :new }
end
......@@ -55,7 +55,8 @@ class Projects::SnippetsController < Projects::ApplicationController
def update
update_params = snippet_params.merge(spammable_params)
UpdateSnippetService.new(project, current_user, @snippet, update_params).execute
service_response = Snippets::UpdateService.new(project, current_user, update_params).execute(@snippet)
@snippet = service_response.payload[:snippet]
recaptcha_check_with_fallback { render :edit }
end
......@@ -89,11 +90,17 @@ class Projects::SnippetsController < Projects::ApplicationController
end
def destroy
return access_denied! unless can?(current_user, :admin_project_snippet, @snippet)
@snippet.destroy
service_response = Snippets::DestroyService.new(current_user, @snippet).execute
redirect_to project_snippets_path(@project), status: :found
if service_response.success?
redirect_to project_snippets_path(project), status: :found
elsif service_response.http_status == 403
access_denied!
else
redirect_to project_snippet_path(project, @snippet),
status: :found,
alert: service_response.message
end
end
protected
......
......@@ -50,8 +50,8 @@ class SnippetsController < ApplicationController
def create
create_params = snippet_params.merge(spammable_params)
@snippet = CreateSnippetService.new(nil, current_user, create_params).execute
service_response = Snippets::CreateService.new(nil, current_user, create_params).execute
@snippet = service_response.payload[:snippet]
move_temporary_files if @snippet.valid? && params[:files]
......@@ -61,7 +61,8 @@ class SnippetsController < ApplicationController
def update
update_params = snippet_params.merge(spammable_params)
UpdateSnippetService.new(nil, current_user, @snippet, update_params).execute
service_response = Snippets::UpdateService.new(nil, current_user, update_params).execute(@snippet)
@snippet = service_response.payload[:snippet]
recaptcha_check_with_fallback { render :edit }
end
......@@ -96,11 +97,17 @@ class SnippetsController < ApplicationController
end
def destroy
return access_denied! unless can?(current_user, :admin_personal_snippet, @snippet)
@snippet.destroy
service_response = Snippets::DestroyService.new(current_user, @snippet).execute
redirect_to snippets_path, status: :found
if service_response.success?
redirect_to dashboard_snippets_path, status: :found
elsif service_response.http_status == 403
access_denied!
else
redirect_to snippet_path(@snippet),
status: :found,
alert: service_response.message
end
end
protected
......
......@@ -45,9 +45,10 @@ module Mutations
raise_resource_not_available_error!
end
snippet = CreateSnippetService.new(project,
service_response = ::Snippets::CreateService.new(project,
context[:current_user],
args).execute
snippet = service_response.payload[:snippet]
{
snippet: snippet.valid? ? snippet : nil,
......
......@@ -15,8 +15,8 @@ module Mutations
def resolve(id:)
snippet = authorized_find!(id: id)
result = snippet.destroy
errors = result ? [] : [ERROR_MSG]
response = ::Snippets::DestroyService.new(current_user, snippet).execute
errors = response.success? ? [] : [ERROR_MSG]
{
errors: errors
......
......@@ -33,13 +33,13 @@ module Mutations
def resolve(args)
snippet = authorized_find!(id: args.delete(:id))
result = UpdateSnippetService.new(snippet.project,
result = ::Snippets::UpdateService.new(snippet.project,
context[:current_user],
snippet,
args).execute
args).execute(snippet)
snippet = result.payload[:snippet]
{
snippet: result ? snippet : snippet.reset,
snippet: result.success? ? snippet : snippet.reset,
errors: errors_on_object(snippet)
}
end
......
# frozen_string_literal: true
class CreateSnippetService < BaseService
include SpamCheckMethods
def execute
filter_spam_check_params
snippet = if project
project.snippets.build(params)
else
PersonalSnippet.new(params)
end
unless Gitlab::VisibilityLevel.allowed_for?(current_user, snippet.visibility_level)
deny_visibility_level(snippet)
return snippet
end
snippet.author = current_user
spam_check(snippet, current_user)
snippet_saved = snippet.with_transaction_returning_status do
snippet.save && snippet.store_mentions!
end
if snippet_saved
UserAgentDetailService.new(snippet, @request).create
Gitlab::UsageDataCounters::SnippetCounter.count(:create)
end
snippet
end
end
# frozen_string_literal: true
module Snippets
class BaseService < ::BaseService
private
def snippet_error_response(snippet, http_status)
ServiceResponse.error(
message: snippet.errors.full_messages.to_sentence,
http_status: http_status,
payload: { snippet: snippet }
)
end
end
end
# frozen_string_literal: true
module Snippets
class CreateService < Snippets::BaseService
include SpamCheckMethods
def execute
filter_spam_check_params
snippet = if project
project.snippets.build(params)
else
PersonalSnippet.new(params)
end
unless Gitlab::VisibilityLevel.allowed_for?(current_user, snippet.visibility_level)
deny_visibility_level(snippet)
return snippet_error_response(snippet, 403)
end
snippet.author = current_user
spam_check(snippet, current_user)
snippet_saved = snippet.with_transaction_returning_status do
snippet.save && snippet.store_mentions!
end
if snippet_saved
UserAgentDetailService.new(snippet, @request).create
Gitlab::UsageDataCounters::SnippetCounter.count(:create)
ServiceResponse.success(payload: { snippet: snippet } )
else
snippet_error_response(snippet, 400)
end
end
end
end
# frozen_string_literal: true
module Snippets
class DestroyService
include Gitlab::Allowable
attr_reader :current_user, :project
def initialize(user, snippet)
@current_user = user
@snippet = snippet
@project = snippet&.project
end
def execute
if snippet.nil?
return service_response_error('No snippet found.', 404)
end
unless user_can_delete_snippet?
return service_response_error(
"You don't have access to delete this snippet.",
403
)
end
if snippet.destroy
ServiceResponse.success(message: 'Snippet was deleted.')
else
service_response_error('Failed to remove snippet.', 400)
end
end
private
attr_reader :snippet
def user_can_delete_snippet?
return can?(current_user, :admin_project_snippet, snippet) if project
can?(current_user, :admin_personal_snippet, snippet)
end
def service_response_error(message, http_status)
ServiceResponse.error(message: message, http_status: http_status)
end
end
end
# frozen_string_literal: true
module Snippets
class UpdateService < Snippets::BaseService
include SpamCheckMethods
def execute(snippet)
# check that user is allowed to set specified visibility_level
new_visibility = visibility_level
if new_visibility && new_visibility.to_i != snippet.visibility_level
unless Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
deny_visibility_level(snippet, new_visibility)
return snippet_error_response(snippet, 403)
end
end
filter_spam_check_params
snippet.assign_attributes(params)
spam_check(snippet, current_user)
snippet_saved = snippet.with_transaction_returning_status do
snippet.save && snippet.store_mentions!
end
if snippet_saved
Gitlab::UsageDataCounters::SnippetCounter.count(:update)
ServiceResponse.success(payload: { snippet: snippet } )
else
snippet_error_response(snippet, 400)
end
end
end
end
# frozen_string_literal: true
class UpdateSnippetService < BaseService
include SpamCheckMethods
attr_accessor :snippet
def initialize(project, user, snippet, params)
super(project, user, params)
@snippet = snippet
end
def execute
# check that user is allowed to set specified visibility_level
new_visibility = visibility_level
if new_visibility && new_visibility.to_i != snippet.visibility_level
unless Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
deny_visibility_level(snippet, new_visibility)
return snippet
end
end
filter_spam_check_params
snippet.assign_attributes(params)
spam_check(snippet, current_user)
snippet_saved = snippet.with_transaction_returning_status do
snippet.save && snippet.store_mentions!
end
if snippet_saved
Gitlab::UsageDataCounters::SnippetCounter.count(:update)
end
end
end
......@@ -64,7 +64,8 @@ module API
snippet_params = declared_params(include_missing: false).merge(request: request, api: true)
snippet_params[:content] = snippet_params.delete(:code) if snippet_params[:code].present?
snippet = CreateSnippetService.new(user_project, current_user, snippet_params).execute
service_response = ::Snippets::CreateService.new(user_project, current_user, snippet_params).execute
snippet = service_response.payload[:snippet]
render_spam_error! if snippet.spam?
......@@ -103,8 +104,8 @@ module API
snippet_params[:content] = snippet_params.delete(:code) if snippet_params[:code].present?
UpdateSnippetService.new(user_project, current_user, snippet,
snippet_params).execute
service_response = ::Snippets::UpdateService.new(user_project, current_user, snippet_params).execute(snippet)
snippet = service_response.payload[:snippet]
render_spam_error! if snippet.spam?
......@@ -127,7 +128,14 @@ module API
authorize! :admin_project_snippet, snippet
destroy_conditionally!(snippet)
destroy_conditionally!(snippet) do |snippet|
service = ::Snippets::DestroyService.new(current_user, snippet)
response = service.execute
if response.error?
render_api_error!({ error: response.message }, response.http_status)
end
end
end
# rubocop: enable CodeReuse/ActiveRecord
......
......@@ -75,7 +75,8 @@ module API
end
post do
attrs = declared_params(include_missing: false).merge(request: request, api: true)
snippet = CreateSnippetService.new(nil, current_user, attrs).execute
service_response = ::Snippets::CreateService.new(nil, current_user, attrs).execute
snippet = service_response.payload[:snippet]
render_spam_error! if snippet.spam?
......@@ -108,8 +109,8 @@ module API
authorize! :update_personal_snippet, snippet
attrs = declared_params(include_missing: false).merge(request: request, api: true)
UpdateSnippetService.new(nil, current_user, snippet, attrs).execute
service_response = ::Snippets::UpdateService.new(nil, current_user, attrs).execute(snippet)
snippet = service_response.payload[:snippet]
render_spam_error! if snippet.spam?
......@@ -133,7 +134,14 @@ module API
authorize! :admin_personal_snippet, snippet
destroy_conditionally!(snippet)
destroy_conditionally!(snippet) do |snippet|
service = ::Snippets::DestroyService.new(current_user, snippet)
response = service.execute
if response.error?
render_api_error!({ error: response.message }, response.http_status)
end
end
end
desc 'Get a raw snippet' do
......
......@@ -445,4 +445,64 @@ describe Projects::SnippetsController do
end
end
end
describe 'DELETE #destroy' do
let!(:snippet) { create(:project_snippet, :private, project: project, author: user) }
let(:params) do
{
namespace_id: project.namespace.to_param,
project_id: project,
id: snippet.to_param
}
end
context 'when current user has ability to destroy the snippet' do
before do
sign_in(user)
end
it 'removes the snippet' do
delete :destroy, params: params
expect { snippet.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
context 'when snippet is succesfuly destroyed' do
it 'redirects to the project snippets page' do
delete :destroy, params: params
expect(response).to redirect_to(project_snippets_path(project))
end
end
context 'when snippet is not destroyed' do
before do
allow(snippet).to receive(:destroy).and_return(false)
controller.instance_variable_set(:@snippet, snippet)
end
it 'renders the snippet page with errors' do
delete :destroy, params: params
expect(flash[:alert]).to eq('Failed to remove snippet.')
expect(response).to redirect_to(project_snippet_path(project, snippet))
end
end
end
context 'when current_user does not have ability to destroy the snippet' do
let(:another_user) { create(:user) }
before do
sign_in(another_user)
end
it 'responds with status 404' do
delete :destroy, params: params
expect(response).to have_gitlab_http_status(404)
end
end
end
end
......@@ -664,4 +664,56 @@ describe SnippetsController do
expect(json_response.keys).to match_array(%w(body references))
end
end
describe 'DELETE #destroy' do
let!(:snippet) { create :personal_snippet, author: user }
context 'when current user has ability to destroy the snippet' do
before do
sign_in(user)
end
it 'removes the snippet' do
delete :destroy, params: { id: snippet.to_param }
expect { snippet.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
context 'when snippet is succesfuly destroyed' do
it 'redirects to the project snippets page' do
delete :destroy, params: { id: snippet.to_param }
expect(response).to redirect_to(dashboard_snippets_path)
end
end
context 'when snippet is not destroyed' do
before do
allow(snippet).to receive(:destroy).and_return(false)
controller.instance_variable_set(:@snippet, snippet)
end
it 'renders the snippet page with errors' do
delete :destroy, params: { id: snippet.to_param }
expect(flash[:alert]).to eq('Failed to remove snippet.')
expect(response).to redirect_to(snippet_path(snippet))
end
end
end
context 'when current_user does not have ability to destroy the snippet' do
let(:another_user) { create(:user) }
before do
sign_in(another_user)
end
it 'responds with status 404' do
delete :destroy, params: { id: snippet.to_param }
expect(response).to have_gitlab_http_status(404)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe CreateSnippetService do
let(:user) { create(:user) }
let(:admin) { create(:user, :admin) }
let(:opts) { base_opts.merge(extra_opts) }
let(:base_opts) do
{
title: 'Test snippet',
file_name: 'snippet.rb',
content: 'puts "hello world"',
visibility_level: Gitlab::VisibilityLevel::PRIVATE
}
end
let(:extra_opts) { {} }
context 'When public visibility is restricted' do
let(:extra_opts) { { visibility_level: Gitlab::VisibilityLevel::PUBLIC } }
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
end
it 'non-admins are not able to create a public snippet' do
snippet = create_snippet(nil, user, opts)
expect(snippet.errors.messages).to have_key(:visibility_level)
expect(snippet.errors.messages[:visibility_level].first).to(
match('has been restricted')
)
end
it 'admins are able to create a public snippet' do
snippet = create_snippet(nil, admin, opts)
expect(snippet.errors.any?).to be_falsey
expect(snippet.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
end
describe "when visibility level is passed as a string" do
let(:extra_opts) { { visibility: 'internal' } }
before do
base_opts.delete(:visibility_level)
end
it "assigns the correct visibility level" do
snippet = create_snippet(nil, user, opts)
expect(snippet.errors.any?).to be_falsey
expect(snippet.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
end
end
end
context 'checking spam' do
shared_examples 'marked as spam' do
let(:snippet) { create_snippet(nil, admin, opts) }
it 'marks a snippet as a spam ' do
expect(snippet).to be_spam
end
it 'invalidates the snippet' do
expect(snippet).to be_invalid
end
it 'creates a new spam_log' do
expect { snippet }
.to log_spam(title: snippet.title, noteable_type: 'PersonalSnippet')
end
it 'assigns a spam_log to an issue' do
expect(snippet.spam_log).to eq(SpamLog.last)
end
end
let(:extra_opts) do
{ visibility_level: Gitlab::VisibilityLevel::PUBLIC, request: double(:request, env: {}) }
end
before do
expect_next_instance_of(AkismetService) do |akismet_service|
expect(akismet_service).to receive_messages(spam?: true)
end
end
[true, false, nil].each do |allow_possible_spam|
context "when recaptcha_disabled flag is #{allow_possible_spam.inspect}" do
before do
stub_feature_flags(allow_possible_spam: allow_possible_spam) unless allow_possible_spam.nil?
end
it_behaves_like 'marked as spam'
end
end
end
describe 'usage counter' do
let(:counter) { Gitlab::UsageDataCounters::SnippetCounter }
it 'increments count' do
expect do
create_snippet(nil, admin, opts)
end.to change { counter.read(:create) }.by 1
end
it 'does not increment count if create fails' do
expect do
create_snippet(nil, admin, {})
end.not_to change { counter.read(:create) }
end
end
def create_snippet(project, user, opts)
CreateSnippetService.new(project, user, opts).execute
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Snippets::CreateService do
describe '#execute' do
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:user, :admin) }
let(:opts) { base_opts.merge(extra_opts) }
let(:base_opts) do
{
title: 'Test snippet',
file_name: 'snippet.rb',
content: 'puts "hello world"',
visibility_level: Gitlab::VisibilityLevel::PRIVATE
}
end
let(:extra_opts) { {} }
let(:creator) { admin }
subject { Snippets::CreateService.new(project, creator, opts).execute }
let(:snippet) { subject.payload[:snippet] }
shared_examples 'a service that creates a snippet' do
it 'creates a snippet with the provided attributes' do
expect(snippet.title).to eq(opts[:title])
expect(snippet.file_name).to eq(opts[:file_name])
expect(snippet.content).to eq(opts[:content])
expect(snippet.visibility_level).to eq(opts[:visibility_level])
end
end
shared_examples 'public visibility level restrictions apply' do
let(:extra_opts) { { visibility_level: Gitlab::VisibilityLevel::PUBLIC } }
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
end
context 'when user is not an admin' do
let(:creator) { user }
it 'responds with an error' do
expect(subject).to be_error
end
it 'does not create a public snippet' do
expect(subject.message).to match('has been restricted')
end
end
context 'when user is an admin' do
it 'responds with success' do
expect(subject).to be_success
end
it 'creates a public snippet' do
expect(snippet.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
end
end
describe 'when visibility level is passed as a string' do
let(:extra_opts) { { visibility: 'internal' } }
before do
base_opts.delete(:visibility_level)
end
it 'assigns the correct visibility level' do
expect(subject).to be_success
expect(snippet.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
end
end
end
shared_examples 'spam check is performed' do
shared_examples 'marked as spam' do
it 'marks a snippet as spam ' do
expect(snippet).to be_spam
end
it 'invalidates the snippet' do
expect(snippet).to be_invalid
end
it 'creates a new spam_log' do
expect { snippet }
.to log_spam(title: snippet.title, noteable_type: snippet.class.name)
end
it 'assigns a spam_log to an issue' do
expect(snippet.spam_log).to eq(SpamLog.last)
end
end
let(:extra_opts) do
{ visibility_level: Gitlab::VisibilityLevel::PUBLIC, request: double(:request, env: {}) }
end
before do
expect_next_instance_of(AkismetService) do |akismet_service|
expect(akismet_service).to receive_messages(spam?: true)
end
end
[true, false, nil].each do |allow_possible_spam|
context "when recaptcha_disabled flag is #{allow_possible_spam.inspect}" do
before do
stub_feature_flags(allow_possible_spam: allow_possible_spam) unless allow_possible_spam.nil?
end
it_behaves_like 'marked as spam'
end
end
end
shared_examples 'snippet create data is tracked' do
let(:counter) { Gitlab::UsageDataCounters::SnippetCounter }
it 'increments count when create succeeds' do
expect { subject }.to change { counter.read(:create) }.by 1
end
context 'when create fails' do
let(:opts) { {} }
it 'does not increment count' do
expect { subject }.not_to change { counter.read(:create) }
end
end
end
shared_examples 'an error service response when save fails' do
let(:extra_opts) { { content: nil } }
it 'responds with an error' do
expect(subject).to be_error
end
it 'does not create the snippet' do
expect { subject }.not_to change { Snippet.count }
end
end
context 'when Project Snippet' do
let_it_be(:project) { create(:project) }
before do
project.add_developer(user)
end
it_behaves_like 'a service that creates a snippet'
it_behaves_like 'public visibility level restrictions apply'
it_behaves_like 'spam check is performed'
it_behaves_like 'snippet create data is tracked'
it_behaves_like 'an error service response when save fails'
end
context 'when PersonalSnippet' do
let(:project) { nil }
it_behaves_like 'a service that creates a snippet'
it_behaves_like 'public visibility level restrictions apply'
it_behaves_like 'spam check is performed'
it_behaves_like 'snippet create data is tracked'
it_behaves_like 'an error service response when save fails'
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Snippets::DestroyService do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:other_user) { create(:user) }
describe '#execute' do
subject { Snippets::DestroyService.new(user, snippet).execute }
context 'when snippet is nil' do
let(:snippet) { nil }
it 'returns a ServiceResponse error' do
expect(subject).to be_error
end
end
shared_examples 'a successful destroy' do
it 'deletes the snippet' do
expect { subject }.to change { Snippet.count }.by(-1)
end
it 'returns ServiceResponse success' do
expect(subject).to be_success
end
end
shared_examples 'an unsuccessful destroy' do
it 'does not delete the snippet' do
expect { subject }.to change { Snippet.count }.by(0)
end
it 'returns ServiceResponse error' do
expect(subject).to be_error
end
end
context 'when ProjectSnippet' do
let!(:snippet) { create(:project_snippet, project: project, author: author) }
context 'when user is able to admin_project_snippet' do
let(:author) { user }
before do
project.add_developer(user)
end
it_behaves_like 'a successful destroy'
end
context 'when user is not able to admin_project_snippet' do
let(:author) { other_user }
it_behaves_like 'an unsuccessful destroy'
end
end
context 'when PersonalSnippet' do
let!(:snippet) { create(:personal_snippet, author: author) }
context 'when user is able to admin_personal_snippet' do
let(:author) { user }
it_behaves_like 'a successful destroy'
end
context 'when user is not able to admin_personal_snippet' do
let(:author) { other_user }
it_behaves_like 'an unsuccessful destroy'
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Snippets::UpdateService do
describe '#execute' do
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create :user, admin: true }
let(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }
let(:options) do
{
title: 'Test snippet',
file_name: 'snippet.rb',
content: 'puts "hello world"',
visibility_level: visibility_level
}
end
let(:updater) { user }
subject do
Snippets::UpdateService.new(
project,
updater,
options
).execute(snippet)
end
shared_examples 'a service that updates a snippet' do
it 'updates a snippet with the provided attributes' do
expect { subject }.to change { snippet.title }.from(snippet.title).to(options[:title])
.and change { snippet.file_name }.from(snippet.file_name).to(options[:file_name])
.and change { snippet.content }.from(snippet.content).to(options[:content])
end
end
shared_examples 'public visibility level restrictions apply' do
let(:visibility_level) { Gitlab::VisibilityLevel::PUBLIC }
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
end
context 'when user is not an admin' do
it 'responds with an error' do
expect(subject).to be_error
end
it 'does not update snippet to public visibility' do
original_visibility = snippet.visibility_level
expect(subject.message).to match('has been restricted')
expect(snippet.visibility_level).to eq(original_visibility)
end
end
context 'when user is an admin' do
let(:updater) { admin }
it 'responds with success' do
expect(subject).to be_success
end
it 'updates the snippet to public visibility' do
old_visibility = snippet.visibility_level
expect(subject.payload[:snippet]).not_to be_nil
expect(snippet.visibility_level).not_to eq(old_visibility)
expect(snippet.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
end
end
context 'when visibility level is passed as a string' do
before do
options[:visibility] = 'internal'
options.delete(:visibility_level)
end
it 'assigns the correct visibility level' do
expect(subject).to be_success
expect(snippet.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
end
end
end
shared_examples 'snippet update data is tracked' do
let(:counter) { Gitlab::UsageDataCounters::SnippetCounter }
it 'increments count when create succeeds' do
expect { subject }.to change { counter.read(:update) }.by 1
end
context 'when update fails' do
let(:options) { { title: '' } }
it 'does not increment count' do
expect { subject }.not_to change { counter.read(:update) }
end
end
end
context 'when Project Snippet' do
let_it_be(:project) { create(:project) }
let!(:snippet) { create(:project_snippet, author: user, project: project) }
before do
project.add_developer(user)
end
it_behaves_like 'a service that updates a snippet'
it_behaves_like 'public visibility level restrictions apply'
it_behaves_like 'snippet update data is tracked'
end
context 'when PersonalSnippet' do
let(:project) { nil }
let!(:snippet) { create(:personal_snippet, author: user) }
it_behaves_like 'a service that updates a snippet'
it_behaves_like 'public visibility level restrictions apply'
it_behaves_like 'snippet update data is tracked'
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe UpdateSnippetService do
before do
@user = create :user
@admin = create :user, admin: true
@opts = {
title: 'Test snippet',
file_name: 'snippet.rb',
content: 'puts "hello world"',
visibility_level: Gitlab::VisibilityLevel::PRIVATE
}
end
context 'When public visibility is restricted' do
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
@snippet = create_snippet(@project, @user, @opts)
@opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
end
it 'non-admins should not be able to update to public visibility' do
old_visibility = @snippet.visibility_level
update_snippet(@project, @user, @snippet, @opts)
expect(@snippet.errors.messages).to have_key(:visibility_level)
expect(@snippet.errors.messages[:visibility_level].first).to(
match('has been restricted')
)
expect(@snippet.visibility_level).to eq(old_visibility)
end
it 'admins should be able to update to public visibility' do
old_visibility = @snippet.visibility_level
update_snippet(@project, @admin, @snippet, @opts)
expect(@snippet.visibility_level).not_to eq(old_visibility)
expect(@snippet.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
end
describe "when visibility level is passed as a string" do
before do
@opts[:visibility] = 'internal'
@opts.delete(:visibility_level)
end
it "assigns the correct visibility level" do
update_snippet(@project, @user, @snippet, @opts)
expect(@snippet.errors.any?).to be_falsey
expect(@snippet.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
end
end
end
describe 'usage counter' do
let(:counter) { Gitlab::UsageDataCounters::SnippetCounter }
let(:snippet) { create_snippet(nil, @user, @opts) }
it 'increments count' do
expect do
update_snippet(nil, @admin, snippet, @opts)
end.to change { counter.read(:update) }.by 1
end
it 'does not increment count if create fails' do
expect do
update_snippet(nil, @admin, snippet, { title: '' })
end.not_to change { counter.read(:update) }
end
end
def create_snippet(project, user, opts)
CreateSnippetService.new(project, user, opts).execute
end
def update_snippet(project, user, snippet, opts)
UpdateSnippetService.new(project, user, snippet, opts).execute
end
end
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