Skip to content
Snippets Groups Projects
Commit 06bbebfd authored by Jacques Erasmus's avatar Jacques Erasmus Committed by Mayra Cabrera
Browse files

Display environment button

Display an environment button on the refactored blob viewer
parent abd5838c
No related branches found
No related tags found
No related merge requests found
Showing
with 168 additions and 21 deletions
Loading
Loading
@@ -92,6 +92,8 @@ export default {
:active-viewer="viewer"
:has-render-error="hasRenderError"
:is-binary="isBinary"
:environment-name="blob.environmentFormattedExternalUrl"
:environment-path="blob.environmentExternalUrlForRouteMap"
@copy="proxyCopyRequest"
/>
</div>
Loading
Loading
<script>
import { GlButton, GlButtonGroup, GlTooltipDirective } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale';
import {
BTN_COPY_CONTENTS_TITLE,
BTN_DOWNLOAD_TITLE,
Loading
Loading
@@ -37,6 +38,16 @@ export default {
required: false,
default: false,
},
environmentName: {
type: String,
required: false,
default: null,
},
environmentPath: {
type: String,
required: false,
default: null,
},
},
computed: {
downloadUrl() {
Loading
Loading
@@ -51,6 +62,11 @@ export default {
showCopyButton() {
return !this.hasRenderError && !this.isBinary;
},
environmentTitle() {
return sprintf(s__('BlobViewer|View on %{environmentName}'), {
environmentName: this.environmentName,
});
},
},
BTN_COPY_CONTENTS_TITLE,
BTN_DOWNLOAD_TITLE,
Loading
Loading
@@ -93,5 +109,17 @@ export default {
category="primary"
variant="default"
/>
<gl-button
v-if="environmentName && environmentPath"
v-gl-tooltip.hover
:aria-label="environmentTitle"
:title="environmentTitle"
:href="environmentPath"
data-testid="environment"
target="_blank"
icon="external-link"
category="primary"
variant="default"
/>
</gl-button-group>
</template>
Loading
Loading
@@ -106,6 +106,8 @@ export default {
ideForkAndEditPath: '',
storedExternally: false,
externalStorage: '',
environmentFormattedExternalUrl: '',
environmentExternalUrlForRouteMap: '',
canModifyBlob: false,
canCurrentUserPushToBranch: false,
archived: false,
Loading
Loading
Loading
Loading
@@ -25,6 +25,8 @@ query getBlobInfo($projectPath: ID!, $filePath: String!, $ref: String!) {
ideEditPath
forkAndEditPath
ideForkAndEditPath
environmentFormattedExternalUrl
environmentExternalUrlForRouteMap
canModifyBlob
canCurrentUserPushToBranch
archived
Loading
Loading
Loading
Loading
@@ -15,8 +15,8 @@ def execute
deployments =
if ref
Deployment.where(ref: ref.to_s)
elsif commit
Deployment.where(sha: commit.sha)
elsif sha
Deployment.where(sha: sha)
else
Deployment.none
end
Loading
Loading
@@ -47,7 +47,7 @@ def valid_environment?(environment)
return false unless Ability.allowed?(current_user, :read_environment, environment)
 
return false if ref && params[:recently_updated] && !environment.recently_updated_on_branch?(ref)
return false if ref && commit && !environment.includes_commit?(commit)
return false if ref && sha && !environment.includes_commit?(sha)
 
true
end
Loading
Loading
@@ -56,8 +56,8 @@ def ref
params[:ref].try(:to_s)
end
 
def commit
params[:commit]
def sha
params[:sha] || params[:commit]&.id
end
end
end
Loading
Loading
@@ -87,6 +87,14 @@ class BlobType < BaseObject
description: 'Web path to blob permalink.',
calls_gitaly: true
 
field :environment_formatted_external_url, GraphQL::Types::String, null: true,
description: 'Environment on which the blob is available.',
calls_gitaly: true
field :environment_external_url_for_route_map, GraphQL::Types::String, null: true,
description: 'Web path to blob on an environment.',
calls_gitaly: true
field :code_owners, [Types::UserType], null: true,
description: 'List of code owners for the blob.',
calls_gitaly: true
Loading
Loading
Loading
Loading
@@ -255,10 +255,10 @@ def playable_build
end
end
 
def includes_commit?(commit)
return false unless commit
def includes_commit?(ancestor_sha)
return false unless sha
 
project.repository.ancestor?(commit.id, sha)
project.repository.ancestor?(ancestor_sha, sha)
end
 
def update_merge_request_metrics!
Loading
Loading
Loading
Loading
@@ -235,10 +235,10 @@ def set_environment_type
self.environment_type = names.many? ? names.first : nil
end
 
def includes_commit?(commit)
def includes_commit?(sha)
return false unless last_deployment
 
last_deployment.includes_commit?(commit)
last_deployment.includes_commit?(sha)
end
 
def last_deployed_at
Loading
Loading
Loading
Loading
@@ -79,6 +79,18 @@ def permalink_path
url_helpers.project_blob_path(project, File.join(project.repository.commit.sha, blob.path))
end
 
def environment_formatted_external_url
return unless environment
environment.formatted_external_url
end
def environment_external_url_for_route_map
return unless environment
environment.external_url_for(blob.path, blob.commit_id)
end
# Will be overridden in EE
def code_owners
[]
Loading
Loading
@@ -122,6 +134,12 @@ def url_helpers
Gitlab::Routing.url_helpers
end
 
def environment
environment_params = project.repository.branch_exists?(blob.commit_id) ? { ref: blob.commit_id } : { sha: blob.commit_id }
environment_params[:find_latest] = true
::Environments::EnvironmentsByDeploymentsFinder.new(project, current_user, environment_params).execute.last
end
def project
blob.repository.project
end
Loading
Loading
Loading
Loading
@@ -14593,6 +14593,8 @@ Returns [`Tree`](#tree).
| <a id="repositoryblobcanmodifyblob"></a>`canModifyBlob` | [`Boolean`](#boolean) | Whether the current user can modify the blob. |
| <a id="repositoryblobcodeowners"></a>`codeOwners` | [`[UserCore!]`](#usercore) | List of code owners for the blob. |
| <a id="repositoryblobeditblobpath"></a>`editBlobPath` | [`String`](#string) | Web path to edit the blob in the old-style editor. |
| <a id="repositoryblobenvironmentexternalurlforroutemap"></a>`environmentExternalUrlForRouteMap` | [`String`](#string) | Web path to blob on an environment. |
| <a id="repositoryblobenvironmentformattedexternalurl"></a>`environmentFormattedExternalUrl` | [`String`](#string) | Environment on which the blob is available. |
| <a id="repositoryblobexternalstorage"></a>`externalStorage` | [`String`](#string) | External storage being used, if enabled (for instance, 'LFS'). |
| <a id="repositoryblobexternalstorageurl"></a>`externalStorageUrl` | [`String`](#string) | Web path to download the raw blob via external storage, if enabled. |
| <a id="repositoryblobfiletype"></a>`fileType` | [`String`](#string) | Expected format of the blob based on the extension. |
Loading
Loading
@@ -5631,6 +5631,9 @@ msgstr ""
msgid "Blame"
msgstr ""
 
msgid "BlobViewer|View on %{environmentName}"
msgstr ""
msgid "Block user"
msgstr ""
 
Loading
Loading
Loading
Loading
@@ -9,7 +9,6 @@
let(:user) { project.creator }
 
before do
stub_feature_flags(refactor_blob_viewer: false) # This stub will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/350457
project.add_maintainer(user)
end
 
Loading
Loading
Loading
Loading
@@ -64,6 +64,22 @@
end
end
 
context 'sha deployment' do
before do
create(:deployment, :success, environment: environment, sha: project.commit.id)
end
it 'returns environment' do
expect(described_class.new(project, user, sha: project.commit.id).execute)
.to contain_exactly(environment)
end
it 'does not return environment when sha is different' do
expect(described_class.new(project, user, sha: '1234').execute)
.to be_empty
end
end
context 'commit deployment' do
before do
create(:deployment, :success, environment: environment, ref: 'master', sha: project.commit.id)
Loading
Loading
import { GlButtonGroup, GlButton } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import BlobHeaderActions from '~/blob/components/blob_header_default_actions.vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import {
BTN_COPY_CONTENTS_TITLE,
BTN_DOWNLOAD_TITLE,
BTN_RAW_TITLE,
RICH_BLOB_VIEWER,
} from '~/blob/components/constants';
import { Blob } from './mock_data';
import { Blob, mockEnvironmentName, mockEnvironmentPath } from './mock_data';
 
describe('Blob Header Default Actions', () => {
let wrapper;
Loading
Loading
@@ -17,7 +17,7 @@ describe('Blob Header Default Actions', () => {
const blobHash = 'foo-bar';
 
function createComponent(propsData = {}) {
wrapper = mount(BlobHeaderActions, {
wrapper = shallowMountExtended(BlobHeaderActions, {
provide: {
blobHash,
},
Loading
Loading
@@ -39,8 +39,8 @@ describe('Blob Header Default Actions', () => {
});
 
describe('renders', () => {
const findCopyButton = () => wrapper.find('[data-testid="copyContentsButton"]');
const findViewRawButton = () => wrapper.find('[data-testid="viewRawButton"]');
const findCopyButton = () => wrapper.findByTestId('copyContentsButton');
const findViewRawButton = () => wrapper.findByTestId('viewRawButton');
 
it('gl-button-group component', () => {
expect(btnGroup.exists()).toBe(true);
Loading
Loading
@@ -89,4 +89,37 @@ describe('Blob Header Default Actions', () => {
expect(findViewRawButton().exists()).toBe(false);
});
});
describe('view on environment button', () => {
const findEnvironmentButton = () => wrapper.findByTestId('environment');
it.each`
environmentName | environmentPath | isVisible
${null} | ${null} | ${false}
${null} | ${mockEnvironmentPath} | ${false}
${mockEnvironmentName} | ${null} | ${false}
${mockEnvironmentName} | ${mockEnvironmentPath} | ${true}
`(
'when environmentName is $environmentName and environmentPath is $environmentPath',
({ environmentName, environmentPath, isVisible }) => {
createComponent({ environmentName, environmentPath });
expect(findEnvironmentButton().exists()).toBe(isVisible);
},
);
it('renders the correct attributes', () => {
createComponent({
environmentName: mockEnvironmentName,
environmentPath: mockEnvironmentPath,
});
expect(findEnvironmentButton().attributes()).toMatchObject({
title: `View on ${mockEnvironmentName}`,
href: mockEnvironmentPath,
});
expect(findEnvironmentButton().props('icon')).toBe('external-link');
});
});
});
Loading
Loading
@@ -55,3 +55,6 @@ export const SimpleBlobContentMock = {
path: 'foo.js',
plainData: 'Plain',
};
export const mockEnvironmentName = 'my.testing.environment';
export const mockEnvironmentPath = 'https://my.testing.environment';
Loading
Loading
@@ -11,6 +11,8 @@ export const simpleViewerMock = {
ideEditPath: 'some_file.js/ide/edit',
forkAndEditPath: 'some_file.js/fork/edit',
ideForkAndEditPath: 'some_file.js/fork/ide',
environmentFormattedExternalUrl: '',
environmentExternalUrlForRouteMap: '',
canModifyBlob: true,
canCurrentUserPushToBranch: true,
archived: false,
Loading
Loading
Loading
Loading
@@ -29,6 +29,8 @@
:blame_path,
:history_path,
:permalink_path,
:environment_formatted_external_url,
:environment_external_url_for_route_map,
:code_owners,
:simple_viewer,
:rich_viewer,
Loading
Loading
Loading
Loading
@@ -615,7 +615,7 @@
it 'returns false' do
commit = project.commit('feature')
 
expect(deployment.includes_commit?(commit)).to be false
expect(deployment.includes_commit?(commit.id)).to be false
end
end
 
Loading
Loading
@@ -623,7 +623,7 @@
it 'returns true' do
commit = project.commit
 
expect(deployment.includes_commit?(commit)).to be true
expect(deployment.includes_commit?(commit.id)).to be true
end
end
 
Loading
Loading
@@ -632,7 +632,7 @@
deployment.update!(sha: Gitlab::Git::BLANK_SHA)
commit = project.commit
 
expect(deployment.includes_commit?(commit)).to be false
expect(deployment.includes_commit?(commit.id)).to be false
end
end
end
Loading
Loading
Loading
Loading
@@ -412,7 +412,7 @@
 
context 'in the same branch' do
it 'returns true' do
expect(environment.includes_commit?(RepoHelpers.sample_commit)).to be true
expect(environment.includes_commit?(RepoHelpers.sample_commit.id)).to be true
end
end
 
Loading
Loading
@@ -422,7 +422,7 @@
end
 
it 'returns false' do
expect(environment.includes_commit?(RepoHelpers.sample_commit)).to be false
expect(environment.includes_commit?(RepoHelpers.sample_commit.id)).to be false
end
end
end
Loading
Loading
Loading
Loading
@@ -87,6 +87,33 @@
it { expect(presenter.permalink_path).to eq("/#{project.full_path}/-/blob/#{project.repository.commit.sha}/files/ruby/regex.rb") }
end
 
context 'environment has been deployed' do
let(:external_url) { "https://some.environment" }
let(:environment) { create(:environment, project: project, external_url: external_url) }
let!(:deployment) { create(:deployment, :success, environment: environment, project: project, sha: blob.commit_id) }
before do
allow(project).to receive(:public_path_for_source_path).with(blob.path, blob.commit_id).and_return(blob.path)
end
describe '#environment_formatted_external_url' do
it { expect(presenter.environment_formatted_external_url).to eq("some.environment") }
end
describe '#environment_external_url_for_route_map' do
it { expect(presenter.environment_external_url_for_route_map).to eq("#{external_url}/#{blob.path}") }
end
describe 'chooses the latest deployed environment for #environment_formatted_external_url and #environment_external_url_for_route_map' do
let(:another_external_url) { "https://another.environment" }
let(:another_environment) { create(:environment, project: project, external_url: another_external_url) }
let!(:another_deployment) { create(:deployment, :success, environment: another_environment, project: project, sha: blob.commit_id) }
it { expect(presenter.environment_formatted_external_url).to eq("another.environment") }
it { expect(presenter.environment_external_url_for_route_map).to eq("#{another_external_url}/#{blob.path}") }
end
end
describe '#code_owners' do
it { expect(presenter.code_owners).to match_array([]) }
end
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment