Commit d79ec3f6 authored by Himanshu Kapoor's avatar Himanshu Kapoor Committed by Peter Leitzen

When opening Web IDE, follow path to fork if it exists

Currently when a user doesn't have permissions to commit or create a
merge  request in a project we offer the user the option to fork the
project when opening the Web IDE. However, if a user already has a fork
then we open the upstream project and not the forked project in the
Web IDE. This means users are "doomed to fail" as they won't be able
to make changes, or save their changes and make them in the
appropriate context.
parent 6fa8b695
<script>
import TreeActionLink from './tree_action_link.vue';
import { __ } from '~/locale';
import { webIDEUrl } from '~/lib/utils/url_utility';
export default {
components: {
TreeActionLink,
},
props: {
projectPath: {
type: String,
required: true,
},
refSha: {
type: String,
required: true,
},
canPushCode: {
type: Boolean,
required: false,
default: true,
},
forkPath: {
type: String,
required: false,
default: '',
},
},
computed: {
showLinkToFork() {
return !this.canPushCode && this.forkPath;
},
text() {
return this.showLinkToFork ? __('Edit fork in Web IDE') : __('Web IDE');
},
path() {
const path = this.showLinkToFork ? this.forkPath : this.projectPath;
return webIDEUrl(`/${path}/edit/${this.refSha}/-/${this.$route.params.path || ''}`);
},
},
};
</script>
<template>
<tree-action-link :path="path" :text="text" data-qa-selector="web_ide_button" />
</template>
......@@ -4,18 +4,26 @@ import App from './components/app.vue';
import Breadcrumbs from './components/breadcrumbs.vue';
import LastCommit from './components/last_commit.vue';
import TreeActionLink from './components/tree_action_link.vue';
import WebIdeLink from './components/web_ide_link.vue';
import DirectoryDownloadLinks from './components/directory_download_links.vue';
import apolloProvider from './graphql';
import { setTitle } from './utils/title';
import { updateFormAction } from './utils/dom';
import { parseBoolean } from '../lib/utils/common_utils';
import { webIDEUrl } from '../lib/utils/url_utility';
import { __ } from '../locale';
export default function setupVueRepositoryList() {
const el = document.getElementById('js-tree-list');
const { dataset } = el;
const { projectPath, projectShortPath, ref, escapedRef, fullName } = dataset;
const {
canPushCode,
projectPath,
projectShortPath,
forkPath,
ref,
escapedRef,
fullName,
} = dataset;
const router = createRouter(projectPath, escapedRef);
apolloProvider.clients.defaultClient.cache.writeData({
......@@ -117,11 +125,12 @@ export default function setupVueRepositoryList() {
el: webIdeLinkEl,
router,
render(h) {
return h(TreeActionLink, {
return h(WebIdeLink, {
props: {
path: webIDEUrl(`/${projectPath}/edit/${ref}/-/${this.$route.params.path || ''}`),
text: __('Web IDE'),
cssClass: 'qa-web-ide-button',
projectPath,
refSha: ref,
forkPath,
canPushCode: parseBoolean(canPushCode),
},
});
},
......
......@@ -191,8 +191,10 @@ module TreeHelper
def vue_file_list_data(project, ref)
{
can_push_code: current_user&.can?(:push_code, project) && "true",
project_path: project.full_path,
project_short_path: project.path,
fork_path: current_user&.fork_of(project)&.full_path,
ref: ref,
escaped_ref: ActionDispatch::Journey::Router::Utils.escape_path(ref),
full_name: project.name_with_namespace
......
---
title: If a user does not have write access to repo, but a fork exists, the Web IDE
button should take them to the fork
merge_request: 36548
author:
type: added
......@@ -8505,6 +8505,9 @@ msgstr ""
msgid "Edit files in the editor and commit changes here"
msgstr ""
msgid "Edit fork in Web IDE"
msgstr ""
msgid "Edit group: %{group_name}"
msgstr ""
......
......@@ -56,7 +56,7 @@ module QA
element :new_file_option
end
view 'app/assets/javascripts/repository/index.js' do
view 'app/assets/javascripts/repository/components/web_ide_link.vue' do
element :web_ide_button
end
......
import WebIdeLink from '~/repository/components/web_ide_link.vue';
import { mount } from '@vue/test-utils';
describe('Web IDE link component', () => {
let wrapper;
function createComponent(props) {
wrapper = mount(WebIdeLink, {
propsData: { ...props },
mocks: {
$route: {
params: {},
},
},
});
}
afterEach(() => {
wrapper.destroy();
});
it('renders link to the Web IDE for a project if only projectPath is given', () => {
createComponent({ projectPath: 'gitlab-org/gitlab', refSha: 'master' });
expect(wrapper.attributes('href')).toBe('/-/ide/project/gitlab-org/gitlab/edit/master/-/');
expect(wrapper.text()).toBe('Web IDE');
});
it('renders link to the Web IDE for a project even if both projectPath and forkPath are given', () => {
createComponent({
projectPath: 'gitlab-org/gitlab',
refSha: 'master',
forkPath: 'my-namespace/gitlab',
});
expect(wrapper.attributes('href')).toBe('/-/ide/project/gitlab-org/gitlab/edit/master/-/');
expect(wrapper.text()).toBe('Web IDE');
});
it('renders link to the forked project if it exists and cannot write to the repo', () => {
createComponent({
projectPath: 'gitlab-org/gitlab',
refSha: 'master',
forkPath: 'my-namespace/gitlab',
canPushCode: false,
});
expect(wrapper.attributes('href')).toBe('/-/ide/project/my-namespace/gitlab/edit/master/-/');
expect(wrapper.text()).toBe('Edit fork in Web IDE');
});
});
......@@ -154,4 +154,58 @@ RSpec.describe TreeHelper do
expect(helper.commit_in_single_accessible_branch).to include(escaped_branch_name)
end
end
describe '#vue_file_list_data' do
before do
allow(helper).to receive(:current_user).and_return(nil)
end
it 'returns a list of attributes related to the project' do
expect(helper.vue_file_list_data(project, sha)).to include(
can_push_code: nil,
fork_path: nil,
escaped_ref: sha,
ref: sha,
project_path: project.full_path,
project_short_path: project.path,
full_name: project.name_with_namespace
)
end
context 'user does not have write access but a personal fork exists' do
include ProjectForksHelper
let_it_be(:user) { create(:user) }
let!(:forked_project) { create(:project, :repository, namespace: user.namespace) }
before do
project.add_guest(user)
fork_project(project, nil, target_project: forked_project)
allow(helper).to receive(:current_user).and_return(user)
end
it 'includes fork_path too' do
expect(helper.vue_file_list_data(project, sha)).to include(
fork_path: forked_project.full_path
)
end
end
context 'user has write access' do
let_it_be(:user) { create(:user) }
before do
project.add_developer(user)
allow(helper).to receive(:current_user).and_return(user)
end
it 'includes can_push_code: true' do
expect(helper.vue_file_list_data(project, sha)).to include(
can_push_code: "true"
)
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