Render repository blob when it exists

If the feature flag is enabled, the snippet has
a repository attached and it's not empty, then we'll use
the blob stored in the repository. If not, we show the snippet
stored in the database.

This behavior will change once we perform the migration.
parent 8de9414e
...@@ -6,20 +6,18 @@ module SnippetsActions ...@@ -6,20 +6,18 @@ module SnippetsActions
def edit def edit
end end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def raw def raw
disposition = params[:inline] == 'false' ? 'attachment' : 'inline' disposition = params[:inline] == 'false' ? 'attachment' : 'inline'
workhorse_set_content_type! workhorse_set_content_type!
send_data( send_data(
convert_line_endings(@snippet.content), convert_line_endings(blob.data),
type: 'text/plain; charset=utf-8', type: 'text/plain; charset=utf-8',
disposition: disposition, disposition: disposition,
filename: @snippet.sanitized_file_name filename: Snippet.sanitized_file_name(blob.name)
) )
end end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def js_request? def js_request?
request.format.js? request.format.js?
......
...@@ -66,7 +66,6 @@ class Projects::SnippetsController < Projects::ApplicationController ...@@ -66,7 +66,6 @@ class Projects::SnippetsController < Projects::ApplicationController
end end
def show def show
blob = @snippet.blob
conditionally_expand_blob(blob) conditionally_expand_blob(blob)
respond_to do |format| respond_to do |format|
...@@ -115,6 +114,16 @@ class Projects::SnippetsController < Projects::ApplicationController ...@@ -115,6 +114,16 @@ class Projects::SnippetsController < Projects::ApplicationController
alias_method :awardable, :snippet alias_method :awardable, :snippet
alias_method :spammable, :snippet alias_method :spammable, :snippet
def blob
return unless snippet
@blob ||= if Feature.enabled?(:version_snippets, current_user) && !snippet.repository.empty?
snippet.blobs.first
else
snippet.blob
end
end
def spammable_path def spammable_path
project_snippet_path(@project, @snippet) project_snippet_path(@project, @snippet)
end end
......
...@@ -68,17 +68,15 @@ class SnippetsController < ApplicationController ...@@ -68,17 +68,15 @@ class SnippetsController < ApplicationController
end end
def show def show
blob = @snippet.blob
conditionally_expand_blob(blob) conditionally_expand_blob(blob)
@note = Note.new(noteable: @snippet)
@noteable = @snippet
@discussions = @snippet.discussions
@notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes), @noteable)
respond_to do |format| respond_to do |format|
format.html do format.html do
@note = Note.new(noteable: @snippet)
@noteable = @snippet
@discussions = @snippet.discussions
@notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes), @noteable)
render 'show' render 'show'
end end
...@@ -121,6 +119,16 @@ class SnippetsController < ApplicationController ...@@ -121,6 +119,16 @@ class SnippetsController < ApplicationController
alias_method :awardable, :snippet alias_method :awardable, :snippet
alias_method :spammable, :snippet alias_method :spammable, :snippet
def blob
return unless snippet
@blob ||= if Feature.enabled?(:version_snippets, current_user) && !snippet.repository.empty?
snippet.blobs.first
else
snippet.blob
end
end
def spammable_path def spammable_path
snippet_path(@snippet) snippet_path(@snippet)
end end
......
...@@ -62,6 +62,7 @@ module Types ...@@ -62,6 +62,7 @@ module Types
field :blob, type: Types::Snippets::BlobType, field :blob, type: Types::Snippets::BlobType,
description: 'Snippet blob', description: 'Snippet blob',
calls_gitaly: true,
null: false null: false
markdown_field :description_html, null: true, method: :description markdown_field :description_html, null: true, method: :description
......
# frozen_string_literal: true # frozen_string_literal: true
# Applicable for blob classes with project attribute
module BlobLanguageFromGitAttributes module BlobLanguageFromGitAttributes
extend ActiveSupport::Concern extend ActiveSupport::Concern
def language_from_gitattributes def language_from_gitattributes
return unless project return unless repository&.exists?
repository = project.repository
repository.gitattribute(path, 'gitlab-language') repository.gitattribute(path, 'gitlab-language')
end end
end end
...@@ -197,7 +197,11 @@ class Snippet < ApplicationRecord ...@@ -197,7 +197,11 @@ class Snippet < ApplicationRecord
end end
def blob def blob
@blob ||= Blob.decorate(SnippetBlob.new(self), nil) @blob ||= Blob.decorate(SnippetBlob.new(self), self)
end
def blobs
repository.ls_files(repository.root_ref).map { |file| Blob.lazy(self, repository.root_ref, file) }
end end
def hook_attrs def hook_attrs
...@@ -208,7 +212,7 @@ class Snippet < ApplicationRecord ...@@ -208,7 +212,7 @@ class Snippet < ApplicationRecord
super.to_s super.to_s
end end
def sanitized_file_name def self.sanitized_file_name(file_name)
file_name.gsub(/[^a-zA-Z0-9_\-\.]+/, '') file_name.gsub(/[^a-zA-Z0-9_\-\.]+/, '')
end end
......
...@@ -32,7 +32,7 @@ class SnippetBlobPresenter < BlobPresenter ...@@ -32,7 +32,7 @@ class SnippetBlobPresenter < BlobPresenter
end end
def snippet def snippet
blob.snippet blob.container
end end
def language def language
......
...@@ -27,6 +27,14 @@ class SnippetPresenter < Gitlab::View::Presenter::Delegated ...@@ -27,6 +27,14 @@ class SnippetPresenter < Gitlab::View::Presenter::Delegated
snippet.submittable_as_spam_by?(current_user) snippet.submittable_as_spam_by?(current_user)
end end
def blob
if Feature.enabled?(:version_snippets, current_user) && !snippet.repository.empty?
snippet.blobs.first
else
snippet.blob
end
end
private private
def can_access_resource?(ability_prefix) def can_access_resource?(ability_prefix)
......
...@@ -9,8 +9,7 @@ ...@@ -9,8 +9,7 @@
= render 'shared/snippets/header' = render 'shared/snippets/header'
.project-snippets .project-snippets
%article.file-holder.snippet-file-content = render 'shared/snippets/blob', blob: @blob
= render 'shared/snippets/blob'
.row-content-block.top-block.content-component-block .row-content-block.top-block.content-component-block
= render 'award_emoji/awards_block', awardable: @snippet, inline: true = render 'award_emoji/awards_block', awardable: @snippet, inline: true
......
- blob = @snippet.blob %article.file-holder.snippet-file-content
.js-file-title.file-title-flex-parent .js-file-title.file-title-flex-parent
= render 'projects/blob/header_content', blob: blob = render 'projects/blob/header_content', blob: blob
.file-actions.d-none.d-sm-block .file-actions.d-none.d-sm-block
= render 'projects/blob/viewer_switcher', blob: blob = render 'projects/blob/viewer_switcher', blob: blob
.btn-group{ role: "group" }< .btn-group{ role: "group" }<
= copy_blob_source_button(blob) = copy_blob_source_button(blob)
= open_raw_blob_button(blob) = open_raw_blob_button(blob)
= download_raw_snippet_button(@snippet) = download_raw_snippet_button(@snippet)
= render 'projects/blob/content', blob: blob = render 'projects/blob/content', blob: blob
- blob = @snippet.blob
.gitlab-embed-snippets .gitlab-embed-snippets
.js-file-title.file-title-flex-parent .js-file-title.file-title-flex-parent
.file-header-content .file-header-content
...@@ -6,10 +5,10 @@ ...@@ -6,10 +5,10 @@
%strong.file-title-name %strong.file-title-name
%a.gitlab-embedded-snippets-title{ href: url_for(only_path: false, overwrite_params: nil) } %a.gitlab-embedded-snippets-title{ href: url_for(only_path: false, overwrite_params: nil) }
= blob.name = @blob.name
%small %small
= number_to_human_size(blob.raw_size) = number_to_human_size(@blob.raw_size)
%a.gitlab-logo-wrapper{ href: url_for(only_path: false, overwrite_params: nil), title: 'view on gitlab' } %a.gitlab-logo-wrapper{ href: url_for(only_path: false, overwrite_params: nil), title: 'view on gitlab' }
%img.gitlab-logo{ src: image_url('ext_snippet_icons/logo.svg'), alt: "GitLab logo" } %img.gitlab-logo{ src: image_url('ext_snippet_icons/logo.svg'), alt: "GitLab logo" }
...@@ -19,4 +18,4 @@ ...@@ -19,4 +18,4 @@
= embedded_snippet_download_button = embedded_snippet_download_button
%article.file-holder.snippet-file-content %article.file-holder.snippet-file-content
= render 'projects/blob/viewer', viewer: @snippet.blob.simple_viewer, load_async: false, external_embed: true = render 'projects/blob/viewer', viewer: @blob.simple_viewer, load_async: false, external_embed: true
...@@ -10,8 +10,7 @@ ...@@ -10,8 +10,7 @@
= render 'shared/snippets/header' = render 'shared/snippets/header'
.personal-snippets .personal-snippets
%article.file-holder.snippet-file-content = render 'shared/snippets/blob', blob: @blob
= render 'shared/snippets/blob'
.row-content-block.top-block.content-component-block .row-content-block.top-block.content-component-block
= render 'award_emoji/awards_block', awardable: @snippet, inline: true = render 'award_emoji/awards_block', awardable: @snippet, inline: true
......
---
title: Render single snippet blob in repository
merge_request: 23848
author:
type: added
...@@ -155,7 +155,7 @@ module Gitlab ...@@ -155,7 +155,7 @@ module Gitlab
end end
def repository def repository
@repository ||= project.repository @repository ||= project&.repository
end end
end end
end end
......
...@@ -3,9 +3,11 @@ ...@@ -3,9 +3,11 @@
require 'spec_helper' require 'spec_helper'
describe Projects::SnippetsController do describe Projects::SnippetsController do
include Gitlab::Routing
let_it_be(:user) { create(:user) }
let_it_be(:user2) { create(:user) }
let(:project) { create(:project_empty_repo, :public) } let(:project) { create(:project_empty_repo, :public) }
let(:user) { create(:user) }
let(:user2) { create(:user) }
before do before do
project.add_maintainer(user) project.add_maintainer(user)
...@@ -318,14 +320,45 @@ describe Projects::SnippetsController do ...@@ -318,14 +320,45 @@ describe Projects::SnippetsController do
end end
end end
shared_examples 'successful response' do
it 'renders the snippet' do
subject
expect(assigns(:snippet)).to eq(project_snippet)
expect(response).to have_gitlab_http_status(:ok)
end
it 'renders the blob from the repository' do
subject
expect(assigns(:blob)).to eq(project_snippet.blobs.first)
end
context 'when feature flag version_snippets is disabled' do
before do
stub_feature_flags(version_snippets: false)
end
it 'returns the snippet database content' do
subject
blob = assigns(:blob)
expect(blob.data).to eq(project_snippet.content)
end
end
end
%w[show raw].each do |action| %w[show raw].each do |action|
describe "GET ##{action}" do describe "GET ##{action}" do
context 'when the project snippet is private' do context 'when the project snippet is private' do
let(:project_snippet) { create(:project_snippet, :private, project: project, author: user) } let(:project_snippet) { create(:project_snippet, :private, :repository, project: project, author: user) }
subject { get action, params: { namespace_id: project.namespace, project_id: project, id: project_snippet.to_param } }
context 'when anonymous' do context 'when anonymous' do
it 'responds with status 404' do it 'responds with status 404' do
get action, params: { namespace_id: project.namespace, project_id: project, id: project_snippet.to_param } subject
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
...@@ -336,12 +369,7 @@ describe Projects::SnippetsController do ...@@ -336,12 +369,7 @@ describe Projects::SnippetsController do
sign_in(user) sign_in(user)
end end
it 'renders the snippet' do it_behaves_like 'successful response'
get action, params: { namespace_id: project.namespace, project_id: project, id: project_snippet.to_param }
expect(assigns(:snippet)).to eq(project_snippet)
expect(response).to have_gitlab_http_status(:ok)
end
end end
context 'when signed in as a project member' do context 'when signed in as a project member' do
...@@ -349,19 +377,16 @@ describe Projects::SnippetsController do ...@@ -349,19 +377,16 @@ describe Projects::SnippetsController do
sign_in(user2) sign_in(user2)
end end
it 'renders the snippet' do it_behaves_like 'successful response'
get action, params: { namespace_id: project.namespace, project_id: project, id: project_snippet.to_param }
expect(assigns(:snippet)).to eq(project_snippet)
expect(response).to have_gitlab_http_status(:ok)
end
end end
end end
context 'when the project snippet does not exist' do context 'when the project snippet does not exist' do
subject { get action, params: { namespace_id: project.namespace, project_id: project, id: 42 } }
context 'when anonymous' do context 'when anonymous' do
it 'responds with status 404' do it 'responds with status 404' do
get action, params: { namespace_id: project.namespace, project_id: project, id: 42 } subject
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
...@@ -373,7 +398,7 @@ describe Projects::SnippetsController do ...@@ -373,7 +398,7 @@ describe Projects::SnippetsController do
end end
it 'responds with status 404' do it 'responds with status 404' do
get action, params: { namespace_id: project.namespace, project_id: project, id: 42 } subject
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
...@@ -383,18 +408,20 @@ describe Projects::SnippetsController do ...@@ -383,18 +408,20 @@ describe Projects::SnippetsController do
end end
describe "GET #show for embeddable content" do describe "GET #show for embeddable content" do
let(:project_snippet) { create(:project_snippet, snippet_permission, project: project, author: user) } let(:project_snippet) { create(:project_snippet, :repository, snippet_permission, project: project, author: user) }
before do before do
sign_in(user) sign_in(user)
get :show, params: { namespace_id: project.namespace, project_id: project, id: project_snippet.to_param }, format: :js
end end
subject { get :show, params: { namespace_id: project.namespace, project_id: project, id: project_snippet.to_param }, format: :js }
context 'when snippet is private' do context 'when snippet is private' do
let(:snippet_permission) { :private } let(:snippet_permission) { :private }
it 'responds with status 404' do it 'responds with status 404' do
subject
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
end end
...@@ -402,10 +429,7 @@ describe Projects::SnippetsController do ...@@ -402,10 +429,7 @@ describe Projects::SnippetsController do
context 'when snippet is public' do context 'when snippet is public' do
let(:snippet_permission) { :public } let(:snippet_permission) { :public }
it 'responds with status 200' do it_behaves_like 'successful response'
expect(assigns(:snippet)).to eq(project_snippet)
expect(response).to have_gitlab_http_status(:ok)
end
end end
context 'when the project is private' do context 'when the project is private' do
...@@ -415,6 +439,8 @@ describe Projects::SnippetsController do ...@@ -415,6 +439,8 @@ describe Projects::SnippetsController do
let(:project_snippet) { create(:project_snippet, :public, project: project, author: user) } let(:project_snippet) { create(:project_snippet, :public, project: project, author: user) }
it 'responds with status 404' do it 'responds with status 404' do
subject
expect(assigns(:snippet)).to eq(project_snippet) expect(assigns(:snippet)).to eq(project_snippet)
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
...@@ -423,14 +449,17 @@ describe Projects::SnippetsController do ...@@ -423,14 +449,17 @@ describe Projects::SnippetsController do
end end
describe 'GET #raw' do describe 'GET #raw' do
let(:content) { "first line\r\nsecond line\r\nthird line" }
let(:formatted_content) { content.gsub(/\r\n/, "\n") }
let(:project_snippet) do let(:project_snippet) do
create( create(
:project_snippet, :public, :project_snippet, :public, :repository,
project: project, project: project,
author: user, author: user,
content: "first line\r\nsecond line\r\nthird line" content: content
) )
end end
let(:blob) { project_snippet.blobs.first }
context 'CRLF line ending' do context 'CRLF line ending' do
let(:params) do let(:params) do
...@@ -441,16 +470,22 @@ describe Projects::SnippetsController do ...@@ -441,16 +470,22 @@ describe Projects::SnippetsController do
} }
end end
before do
allow_next_instance_of(Blob) do |instance|
allow(instance).to receive(:data).and_return(content)
end
end
it 'returns LF line endings by default' do it 'returns LF line endings by default' do
get :raw, params: params get :raw, params: params
expect(response.body).to eq("first line\nsecond line\nthird line") expect(response.body).to eq(formatted_content)
end end
it 'does not convert line endings when parameter present' do it 'does not convert line endings when parameter present' do
get :raw, params: params.merge(line_ending: :raw) get :raw, params: params.merge(line_ending: :raw)
expect(response.body).to eq("first line\r\nsecond line\r\nthird line") expect(response.body).to eq(content)
end end
end end
end end
......
...@@ -3,11 +3,9 @@ ...@@ -3,11 +3,9 @@
require 'spec_helper' require 'spec_helper'
describe SnippetsController do describe SnippetsController do
let(:user) { create(:user) } let_it_be(:user) { create(:user) }
describe 'GET #index' do describe 'GET #index' do
let(:user) { create(:user) }
context 'when username parameter is present' do context 'when username parameter is present' do
it_behaves_like 'paginated collection' do it_behaves_like 'paginated collection' do
let(:collection) { Snippet.all } let(:collection) { Snippet.all }
...@@ -75,8 +73,37 @@ describe SnippetsController do ...@@ -75,8 +73,37 @@ describe SnippetsController do
end end
describe 'GET #show' do describe 'GET #show' do
shared_examples 'successful response' do
it 'renders the snippet' do
subject
expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_gitlab_http_status(:ok)
end
it 'renders the blob from the repository' do
subject
expect(assigns(:blob)).to eq(personal_snippet.blobs.first)
end
context 'when feature flag version_snippets is disabled' do
before do
stub_feature_flags(version_snippets: false)
end
it 'returns the snippet database content' do
subject
blob = assigns(:blob)
expect(blob.data).to eq(personal_snippet.content)
end
end
end
context 'when the personal snippet is private' do context 'when the personal snippet is private' do
let(:personal_snippet) { create(:personal_snippet, :private, author: user) } let_it_be(:personal_snippet) { create(:personal_snippet, :private, :repository, author: user) }
context 'when signed in' do context 'when signed in' do
before do before do
...@@ -95,11 +122,8 @@ describe SnippetsController do ...@@ -95,11 +122,8 @@ describe SnippetsController do
end end
context 'when signed in user is the author' do context 'when signed in user is the author' do
it 'renders the snippet' do it_behaves_like 'successful response' do
get :show, params: { id: personal_snippet.to_param } subject { get :show, params: { id: personal_snippet.to_param } }
expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_gitlab_http_status(:ok)
end end
it 'responds with status 404 when embeddable content is requested' do it 'responds with status 404 when embeddable content is requested' do
...@@ -120,18 +144,15 @@ describe SnippetsController do ...@@ -120,18 +144,15 @@ describe SnippetsController do
end end
context 'when the personal snippet is internal' do context 'when the personal snippet is internal' do
let(:personal_snippet) { create(:personal_snippet, :internal, author: user) } let_it_be(:personal_snippet) { create(:personal_snippet, :internal, :repository, author: user) }
context 'when signed in' do context 'when signed in' do
before do before do
sign_in(user) sign_in(user)
end end
it 'renders the snippet' do it_behaves_like 'successful response' do
get :show, params: { id: personal_snippet.to_param } subject { get :show, params: { id: personal_snippet.to_param } }
expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_gitlab_http_status(:ok)
end end
it 'responds with status 404 when embeddable content is requested' do it 'responds with status 404 when embeddable content is requested' do
...@@ -151,18 +172,15 @@ describe SnippetsController do ...@@ -151,18 +172,15 @@ describe SnippetsController do
end end
context 'when the personal snippet is public' do context 'when the personal snippet is public' do
let(:personal_snippet) { create(:personal_snippet, :public, author: user) } let_it_be(:personal_snippet) { create(:personal_snippet, :public, :repository, author: user) }
context 'when signed in' do context 'when signed in' do
before do before do
sign_in(user) sign_in(user)
end end
it 'renders the snippet' do it_behaves_like 'successful response' do
get :show, params: { id: personal_snippet.to_param } subject { get :show, params: { id: personal_snippet.to_param } }
expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_gitlab_http_status(:ok)
end end
it 'responds with status 200 when embeddable content is requested' do it 'responds with status 200 when embeddable content is requested' do
...@@ -481,8 +499,82 @@ describe SnippetsController do ...@@ -481,8 +499,82 @@ describe SnippetsController do
end end
describe "GET #raw" do describe "GET #raw" do
shared_examples '200 status' do
before do
subject
end
it 'responds with status 200' do
expect(assigns(:snippet)).to eq(snippet)
expect(response).to have_gitlab_http_status(:ok)
end
it 'has expected headers' do
expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
expect(response.header['Content-Disposition']).to match(/inline/)
end
it "sets #{Gitlab::Workhorse::DETECT_HEADER} header" do
expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq 'true'
end
end
shared_examples 'CRLF line ending' do
let(:content) { "first line\r\nsecond line\r\nthird line" }
let(:formatted_content) { content.gsub(/\r\n/, "\n") }
let(:snippet) do
create(:personal_snippet, :public, :repository, author: user, content: content)
end
before do
allow_next_instance_of(Blob) do |instance|
allow(instance).to receive(:data).and_return(content)
end
subject
end
it 'returns LF line endings by default' do
expect(response.body).to eq(formatted_content)
end
context 'when parameter present' do
let(:params) { { id: snippet.to_param, line_ending: :raw } }
it 'does not convert line endings when parameter present' do
expect(response.body).to eq(content)
end
end
end
shared_examples 'successful response' do
it_behaves_like '200 status'
it_behaves_like 'CRLF line ending'
it 'returns snippet first blob data' do
subject
expect(response.body).to eq snippet.blobs.first.data
end
context 'when feature flag version_snippets is disabled' do
before do
stub_feature_flags(version_snippets: false)
end
it_behaves_like '200 status'
it_behaves_like 'CRLF line ending'
it 'returns snippet database content' do
subject
expect(response.body).to eq snippet.content
end
end
end
context 'when the personal snippet is private' do context 'when the personal snippet is private' do
let(:personal_snippet) { create(:personal_snippet, :private, author: user) } let_it_be(:personal_snippet) { create(:personal_snippet, :private, :repository, author: user) }
context 'when signed in' do context 'when signed in' do
before do before do
...@@ -501,24 +593,11 @@ describe SnippetsController do ...@@ -501,24 +593,11 @@ describe SnippetsController do
end end
context 'when signed in user is the author' do context 'when signed in user is the author' do
before do it_behaves_like 'successful response' do
get :raw, params: { id: personal_snippet.to_param } let(:snippet) { personal_snippet }
end let(:params) { { id: snippet.to_param } }
it 'responds with status 200' do subject { get :raw, params: params }
expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_gitlab_http_status(:ok)
end
it 'has expected headers' do
expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
expect(response.header['Content-Disposition']).to match(/inline/)
end
it "sets #{Gitlab::Workhorse::DETECT_HEADER} header" do
expect(response).to have_gitlab_http_status(:ok)
expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end end
end end
end end
...@@ -533,18 +612,18 @@ describe SnippetsController do ...@@ -533,18 +612,18 @@ describe SnippetsController do
end end
context 'when the personal snippet is internal' do context 'when the personal snippet is internal' do
let(:personal_snippet) { create(:personal_snippet, :internal, author: user) } let_it_be(:personal_snippet) { create(:personal_snippet, :internal, :repository, author: user) }
context 'when signed in' do context 'when signed in' do
before do before do
sign_in(user) sign_in(user)
end end
it 'responds with status 200' do it_behaves_like 'successful response' do
get :raw, params: { id: personal_snippet.to_param } let(:snippet) { personal_snippet }
let(:params) { { id: snippet.to_param } }
expect(assigns(:snippet)).to eq(personal_snippet) subject { get :raw, params: params }
expect(response).to have_gitlab_http_status(:ok)
end end
end end
...@@ -558,36 +637,18 @@ describe SnippetsController do ...@@ -558,36 +637,18 @@ describe SnippetsController do
end end
context 'when the personal snippet is public' do context 'when the personal snippet is public' do
let(:personal_snippet) { create(:personal_snippet, :public, author: user) } let_it_be(:personal_snippet) { create(:personal_snippet, :public, :repository, author: user) }
context 'when signed in' do context 'when signed in' do
before do before do
sign_in(user) sign_in(user)
end end
it 'responds with status 200' do it_behaves_like 'successful response' do
get :raw, params: { id: personal_snippet.to_param } let(:snippet) { personal_snippet }
let(:params) { { id: snippet.to_param } }
expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_gitlab_http_status(:ok)
end
context 'CRLF line ending' do
let(:personal_snippet) do
create(:personal_snippet, :public, author: user, content: "first line\r\nsecond line\r\nthird line")
end
it 'returns LF line endings by default' do subject { get :raw, params: params }
get :raw, params: { id: personal_snippet.to_param }
expect(response.body).to eq("first line\nsecond line\nthird line")
end
it 'does not convert line endings when parameter present' do
get :raw, params: { id: personal_snippet.to_param, line_ending: :raw }
expect(response.body).to eq("first line\r\nsecond line\r\nthird line")
end
end end
end end
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
require 'spec_helper' require 'spec_helper'
describe 'Thread Comments Snippet', :js do describe 'Thread Comments Snippet', :js do
let(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:project) { create(:project) } let_it_be(:project) { create(:project) }
let(:snippet) { create(:project_snippet, :private, project: project, author: user) } let_it_be(:snippet) { create(:project_snippet, :private, :repository, project: project, author: user) }
before do before do
stub_feature_flags(snippets_vue: false) stub_feature_flags(snippets_vue: false)
......
...@@ -5,8 +5,8 @@ require 'spec_helper' ...@@ -5,8 +5,8 @@ require 'spec_helper'
describe 'Projects > Snippets > Create Snippet', :js do describe 'Projects > Snippets > Create Snippet', :js do
include DropzoneHelper include DropzoneHelper
let(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:project) { create(:project, :public) } let_it_be(:project) { create(:project, :public) }
def description_field def description_field
find('.js-description-input input,textarea') find('.js-description-input input,textarea')
...@@ -102,7 +102,7 @@ describe 'Projects > Snippets > Create Snippet', :js do ...@@ -102,7 +102,7 @@ describe 'Projects > Snippets > Create Snippet', :js do
end end
it 'shows a public snippet on the index page but not the New snippet button' do it 'shows a public snippet on the index page but not the New snippet button' do
snippet = create(:project_snippet, :public, project: project) snippet = create(:project_snippet, :public, :repository, project: project)
visit project_snippets_path(project) visit project_snippets_path(project)
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
require 'spec_helper' require 'spec_helper'
describe 'Projects > Snippets > User comments on a snippet', :js do describe 'Projects > Snippets > User comments on a snippet', :js do
let(:project) { create(:project) } let_it_be(:project) { create(:project) }
let!(:snippet) { create(:project_snippet, project: project, author: user) } let_it_be(:user) { create(:user) }
let(:user) { create(:user) } let_it_be(:snippet) { create(:project_snippet, :repository, project: project, author: user) }
before do before do
stub_feature_flags(snippets_vue: false) stub_feature_flags(snippets_vue: false)
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
require 'spec_helper' require 'spec_helper'
describe 'Reportable note on snippets', :js do describe 'Reportable note on snippets', :js do
let(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:project) { create(:project) } let_it_be(:project) { create(:project) }
before do before do
stub_feature_flags(snippets_vue: false) stub_feature_flags(snippets_vue: false)
...@@ -13,8 +13,8 @@ describe 'Reportable note on snippets', :js do ...@@ -13,8 +13,8 @@ describe 'Reportable note on snippets', :js do
end end
describe 'on project snippet' do describe 'on project snippet' do
let(:snippet) { create(:project_snippet, :public, project: project, author: user) } let_it_be(:snippet) { create(:project_snippet, :public, :repository, project: project, author: user) }
let!(:note) { create(:note_on_project_snippet, noteable: snippet, project: project) } let_it_be(:note) { create(:note_on_project_snippet, noteable: snippet, project: project) }
before do before do
visit project_snippet_path(project, snippet) visit project_snippet_path(project, snippet)
......
...@@ -16,4 +16,47 @@ describe GitlabSchema.types['Snippet'] do ...@@ -16,4 +16,47 @@ describe GitlabSchema.types['Snippet'] do
describe 'authorizations' do describe 'authorizations' do
it { expect(described_class).to require_graphql_authorizations(:read_snippet) } it { expect(described_class).to require_graphql_authorizations(:read_snippet) }
end end
describe '#blob' do
let_it_be(:user) { create(:user) }
let(:query_blob) { subject.dig('data', 'snippets', 'edges')[0]['node']['blob'] }
let(:query) do
%(
{
snippets {
edges {
node {
blob {
name
path
}
}
}
}
}
)
end
subject { GitlabSchema.execute(query, context: { current_user: user }).as_json }
context 'when snippet has repository' do
let!(:snippet) { create(:personal_snippet, :repository, :public, author: user) }
let(:blob) { snippet.blobs.first }
it 'returns blob from the repository' do
expect(query_blob['name']).to eq blob.name
expect(query_blob['path']).to eq blob.path
end
end
context 'when snippet does not have a repository' do
let!(:snippet) { create(:personal_snippet, :public, author: user) }
let(:blob) { snippet.blob }
it 'returns SnippetBlob type' do
expect(query_blob['name']).to eq blob.name
expect(query_blob['path']).to eq blob.path
end
end
end
end end
...@@ -11,13 +11,20 @@ describe BlobLanguageFromGitAttributes do ...@@ -11,13 +11,20 @@ describe BlobLanguageFromGitAttributes do
subject(:blob) { fake_blob(path: 'file.md') } subject(:blob) { fake_blob(path: 'file.md') }
it 'returns return value from gitattribute' do it 'returns return value from gitattribute' do
expect(blob.project.repository).to receive(:gitattribute).with(blob.path, 'gitlab-language').and_return('erb?parent=json') allow(blob.repository).to receive(:exists?).and_return(true)
expect(blob.repository).to receive(:gitattribute).with(blob.path, 'gitlab-language').and_return('erb?parent=json')
expect(blob.language_from_gitattributes).to eq('erb?parent=json') expect(blob.language_from_gitattributes).to eq('erb?parent=json')
end end
it 'returns nil if project is absent' do it 'returns nil if repository is absent' do
allow(blob).to receive(:project).and_return(nil) allow(blob).to receive(:repository).and_return(nil)
expect(blob.language_from_gitattributes).to eq(nil)
end
it 'returns nil if repository does not exist' do
allow(blob.repository).to receive(:exists?).and_return(false)
expect(blob.language_from_gitattributes).to eq(nil) expect(blob.language_from_gitattributes).to eq(nil)
end end
......
...@@ -76,18 +76,18 @@ describe SnippetBlobPresenter do ...@@ -76,18 +76,18 @@ describe SnippetBlobPresenter do
context 'with ProjectSnippet' do context 'with ProjectSnippet' do
let!(:project) { create(:project) } let!(:project) { create(:project) }
let(:snippet) { build(:project_snippet, project: project, id: 1) } let(:snippet) { create(:project_snippet, project: project) }
it 'returns the raw path' do it 'returns the raw path' do
expect(subject).to eq "/#{snippet.project.full_path}/snippets/1/raw" expect(subject).to eq "/#{snippet.project.full_path}/snippets/#{snippet.id}/raw"
end end
end end
context 'with PersonalSnippet' do context 'with PersonalSnippet' do
let(:snippet) { build(:personal_snippet, id: 1) } let(:snippet) { create(:personal_snippet) }
it 'returns the raw path' do it 'returns the raw path' do
expect(subject).to eq "/snippets/1/raw" expect(subject).to eq "/snippets/#{snippet.id}/raw"
end end
end end
end end
......
...@@ -143,4 +143,24 @@ describe SnippetPresenter do ...@@ -143,4 +143,24 @@ describe SnippetPresenter do
expect(subject).to be_truthy expect(subject).to be_truthy
end end
end end
describe '#blob' do
let(:snippet) { personal_snippet }
subject { presenter.blob }
context 'when snippet does not have a repository' do
it 'returns SnippetBlob' do
expect(subject).to eq snippet.blob
end
end
context 'when snippet has a repository' do
let(:snippet) { create(:snippet, :repository, author: user) }
it 'returns repository first blob' do
expect(subject).to eq snippet.blobs.first
end
end
end
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