Merge branch '57668-create-file-from-url' into 'master'
Resolve "Support creating new file from URL in the Web IDE" Closes #57668 See merge request gitlab-org/gitlab-ce!26622
This commit is contained in:
commit
9fb1dfa870
8 changed files with 271 additions and 17 deletions
|
@ -147,6 +147,11 @@ export const openBranch = ({ dispatch, state }, { projectId, branchId, basePath
|
||||||
|
|
||||||
if (treeEntry) {
|
if (treeEntry) {
|
||||||
dispatch('handleTreeEntryAction', treeEntry);
|
dispatch('handleTreeEntryAction', treeEntry);
|
||||||
|
} else {
|
||||||
|
dispatch('createTempEntry', {
|
||||||
|
name: path,
|
||||||
|
type: 'blob',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as types from '../mutation_types';
|
import * as types from '../mutation_types';
|
||||||
import { sortTree } from '../utils';
|
import { sortTree, mergeTrees } from '../utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
[types.TOGGLE_TREE_OPEN](state, path) {
|
[types.TOGGLE_TREE_OPEN](state, path) {
|
||||||
|
@ -23,9 +23,15 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[types.SET_DIRECTORY_DATA](state, { data, treePath }) {
|
[types.SET_DIRECTORY_DATA](state, { data, treePath }) {
|
||||||
Object.assign(state.trees[treePath], {
|
const selectedTree = state.trees[treePath];
|
||||||
tree: data,
|
|
||||||
});
|
// If we opened files while loading the tree, we need to merge them
|
||||||
|
// Otherwise, simply overwrite the tree
|
||||||
|
const tree = !selectedTree.tree.length
|
||||||
|
? data
|
||||||
|
: selectedTree.loading && mergeTrees(selectedTree.tree, data);
|
||||||
|
|
||||||
|
Object.assign(selectedTree, { tree });
|
||||||
},
|
},
|
||||||
[types.SET_LAST_COMMIT_URL](state, { tree = state, url }) {
|
[types.SET_LAST_COMMIT_URL](state, { tree = state, url }) {
|
||||||
Object.assign(tree, {
|
Object.assign(tree, {
|
||||||
|
|
|
@ -170,3 +170,31 @@ export const filePathMatches = (filePath, path) => filePath.indexOf(`${path}/`)
|
||||||
|
|
||||||
export const getChangesCountForFiles = (files, path) =>
|
export const getChangesCountForFiles = (files, path) =>
|
||||||
files.filter(f => filePathMatches(f.path, path)).length;
|
files.filter(f => filePathMatches(f.path, path)).length;
|
||||||
|
|
||||||
|
export const mergeTrees = (fromTree, toTree) => {
|
||||||
|
if (!fromTree || !fromTree.length) {
|
||||||
|
return toTree;
|
||||||
|
}
|
||||||
|
|
||||||
|
const recurseTree = (n, t) => {
|
||||||
|
if (!n) {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
const existingTreeNode = t.find(el => el.path === n.path);
|
||||||
|
|
||||||
|
if (existingTreeNode && n.tree.length > 0) {
|
||||||
|
existingTreeNode.opened = true;
|
||||||
|
recurseTree(n.tree[0], existingTreeNode.tree);
|
||||||
|
} else if (!existingTreeNode) {
|
||||||
|
const sorted = sortTree(t.concat(n));
|
||||||
|
t.splice(0, t.length + 1, ...sorted);
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = 0, l = fromTree.length; i < l; i += 1) {
|
||||||
|
recurseTree(fromTree[i], toTree);
|
||||||
|
}
|
||||||
|
|
||||||
|
return toTree;
|
||||||
|
};
|
||||||
|
|
5
changelogs/unreleased/57668-create-file-from-url.yml
Normal file
5
changelogs/unreleased/57668-create-file-from-url.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Implemented support for creation of new files from URL in Web IDE
|
||||||
|
merge_request: 26622
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -279,5 +279,22 @@ describe('IDE store project actions', () => {
|
||||||
.then(done)
|
.then(done)
|
||||||
.catch(done.fail);
|
.catch(done.fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('creates a new file supplied via URL if the file does not exist yet', done => {
|
||||||
|
openBranch(store, { ...branch, basePath: 'not-existent.md' })
|
||||||
|
.then(() => {
|
||||||
|
expect(store.dispatch).not.toHaveBeenCalledWith(
|
||||||
|
'handleTreeEntryAction',
|
||||||
|
jasmine.anything(),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(store.dispatch).toHaveBeenCalledWith('createTempEntry', {
|
||||||
|
name: 'not-existent.md',
|
||||||
|
type: 'blob',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(done)
|
||||||
|
.catch(done.fail);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import MockAdapter from 'axios-mock-adapter';
|
import MockAdapter from 'axios-mock-adapter';
|
||||||
import testAction from 'spec/helpers/vuex_action_helper';
|
import testAction from 'spec/helpers/vuex_action_helper';
|
||||||
import { showTreeEntry, getFiles } from '~/ide/stores/actions/tree';
|
import { showTreeEntry, getFiles, setDirectoryData } from '~/ide/stores/actions/tree';
|
||||||
import * as types from '~/ide/stores/mutation_types';
|
import * as types from '~/ide/stores/mutation_types';
|
||||||
import axios from '~/lib/utils/axios_utils';
|
import axios from '~/lib/utils/axios_utils';
|
||||||
import store from '~/ide/stores';
|
import store from '~/ide/stores';
|
||||||
|
@ -206,4 +206,35 @@ describe('Multi-file store tree actions', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('setDirectoryData', () => {
|
||||||
|
it('sets tree correctly if there are no opened files yet', done => {
|
||||||
|
const treeFile = file({ name: 'README.md' });
|
||||||
|
store.state.trees['abcproject/master'] = {};
|
||||||
|
|
||||||
|
testAction(
|
||||||
|
setDirectoryData,
|
||||||
|
{ projectId: 'abcproject', branchId: 'master', treeList: [treeFile] },
|
||||||
|
store.state,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: types.SET_DIRECTORY_DATA,
|
||||||
|
payload: {
|
||||||
|
treePath: 'abcproject/master',
|
||||||
|
data: [treeFile],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: types.TOGGLE_LOADING,
|
||||||
|
payload: {
|
||||||
|
entry: {},
|
||||||
|
forceValue: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
done,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -26,17 +26,11 @@ describe('Multi-file store tree mutations', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('SET_DIRECTORY_DATA', () => {
|
describe('SET_DIRECTORY_DATA', () => {
|
||||||
const data = [
|
let data;
|
||||||
{
|
|
||||||
name: 'tree',
|
beforeEach(() => {
|
||||||
},
|
data = [file('tree'), file('foo'), file('blob')];
|
||||||
{
|
});
|
||||||
name: 'submodule',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'blob',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
it('adds directory data', () => {
|
it('adds directory data', () => {
|
||||||
localState.trees['project/master'] = {
|
localState.trees['project/master'] = {
|
||||||
|
@ -52,7 +46,7 @@ describe('Multi-file store tree mutations', () => {
|
||||||
|
|
||||||
expect(tree.tree.length).toBe(3);
|
expect(tree.tree.length).toBe(3);
|
||||||
expect(tree.tree[0].name).toBe('tree');
|
expect(tree.tree[0].name).toBe('tree');
|
||||||
expect(tree.tree[1].name).toBe('submodule');
|
expect(tree.tree[1].name).toBe('foo');
|
||||||
expect(tree.tree[2].name).toBe('blob');
|
expect(tree.tree[2].name).toBe('blob');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -65,6 +59,49 @@ describe('Multi-file store tree mutations', () => {
|
||||||
|
|
||||||
expect(localState.trees['project/master'].loading).toBe(true);
|
expect(localState.trees['project/master'].loading).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not override tree already in state, but merges the two with correct order', () => {
|
||||||
|
const openedFile = file('new');
|
||||||
|
|
||||||
|
localState.trees['project/master'] = {
|
||||||
|
loading: true,
|
||||||
|
tree: [openedFile],
|
||||||
|
};
|
||||||
|
|
||||||
|
mutations.SET_DIRECTORY_DATA(localState, {
|
||||||
|
data,
|
||||||
|
treePath: 'project/master',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { tree } = localState.trees['project/master'];
|
||||||
|
|
||||||
|
expect(tree.length).toBe(4);
|
||||||
|
expect(tree[0].name).toBe('blob');
|
||||||
|
expect(tree[1].name).toBe('foo');
|
||||||
|
expect(tree[2].name).toBe('new');
|
||||||
|
expect(tree[3].name).toBe('tree');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns tree unchanged if the opened file is already in the tree', () => {
|
||||||
|
const openedFile = file('foo');
|
||||||
|
localState.trees['project/master'] = {
|
||||||
|
loading: true,
|
||||||
|
tree: [openedFile],
|
||||||
|
};
|
||||||
|
|
||||||
|
mutations.SET_DIRECTORY_DATA(localState, {
|
||||||
|
data,
|
||||||
|
treePath: 'project/master',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { tree } = localState.trees['project/master'];
|
||||||
|
|
||||||
|
expect(tree.length).toBe(3);
|
||||||
|
|
||||||
|
expect(tree[0].name).toBe('tree');
|
||||||
|
expect(tree[1].name).toBe('foo');
|
||||||
|
expect(tree[2].name).toBe('blob');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('REMOVE_ALL_CHANGES_FILES', () => {
|
describe('REMOVE_ALL_CHANGES_FILES', () => {
|
||||||
|
|
|
@ -235,4 +235,129 @@ describe('Multi-file store utils', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('mergeTrees', () => {
|
||||||
|
let fromTree;
|
||||||
|
let toTree;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fromTree = [file('foo')];
|
||||||
|
toTree = [file('bar')];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('merges simple trees with sorting the result', () => {
|
||||||
|
toTree = [file('beta'), file('alpha'), file('gamma')];
|
||||||
|
const res = utils.mergeTrees(fromTree, toTree);
|
||||||
|
|
||||||
|
expect(res.length).toEqual(4);
|
||||||
|
expect(res[0].name).toEqual('alpha');
|
||||||
|
expect(res[1].name).toEqual('beta');
|
||||||
|
expect(res[2].name).toEqual('foo');
|
||||||
|
expect(res[3].name).toEqual('gamma');
|
||||||
|
expect(res[2]).toBe(fromTree[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles edge cases', () => {
|
||||||
|
expect(utils.mergeTrees({}, []).length).toEqual(0);
|
||||||
|
|
||||||
|
let res = utils.mergeTrees({}, toTree);
|
||||||
|
|
||||||
|
expect(res.length).toEqual(1);
|
||||||
|
expect(res[0].name).toEqual('bar');
|
||||||
|
|
||||||
|
res = utils.mergeTrees(fromTree, []);
|
||||||
|
|
||||||
|
expect(res.length).toEqual(1);
|
||||||
|
expect(res[0].name).toEqual('foo');
|
||||||
|
expect(res[0]).toBe(fromTree[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('merges simple trees without producing duplicates', () => {
|
||||||
|
toTree.push(file('foo'));
|
||||||
|
|
||||||
|
const res = utils.mergeTrees(fromTree, toTree);
|
||||||
|
|
||||||
|
expect(res.length).toEqual(2);
|
||||||
|
expect(res[0].name).toEqual('bar');
|
||||||
|
expect(res[1].name).toEqual('foo');
|
||||||
|
expect(res[1]).not.toBe(fromTree[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('merges nested tree into the main one without duplicates', () => {
|
||||||
|
fromTree[0].tree.push({
|
||||||
|
...file('alpha'),
|
||||||
|
path: 'foo/alpha',
|
||||||
|
tree: [
|
||||||
|
{
|
||||||
|
...file('beta.md'),
|
||||||
|
path: 'foo/alpha/beta.md',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
toTree.push({
|
||||||
|
...file('foo'),
|
||||||
|
tree: [
|
||||||
|
{
|
||||||
|
...file('alpha'),
|
||||||
|
path: 'foo/alpha',
|
||||||
|
tree: [
|
||||||
|
{
|
||||||
|
...file('gamma.md'),
|
||||||
|
path: 'foo/alpha/gamma.md',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = utils.mergeTrees(fromTree, toTree);
|
||||||
|
|
||||||
|
expect(res.length).toEqual(2);
|
||||||
|
expect(res[1].name).toEqual('foo');
|
||||||
|
|
||||||
|
const finalBranch = res[1].tree[0].tree;
|
||||||
|
|
||||||
|
expect(finalBranch.length).toEqual(2);
|
||||||
|
expect(finalBranch[0].name).toEqual('beta.md');
|
||||||
|
expect(finalBranch[1].name).toEqual('gamma.md');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('marks correct folders as opened as the parsing goes on', () => {
|
||||||
|
fromTree[0].tree.push({
|
||||||
|
...file('alpha'),
|
||||||
|
path: 'foo/alpha',
|
||||||
|
tree: [
|
||||||
|
{
|
||||||
|
...file('beta.md'),
|
||||||
|
path: 'foo/alpha/beta.md',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
toTree.push({
|
||||||
|
...file('foo'),
|
||||||
|
tree: [
|
||||||
|
{
|
||||||
|
...file('alpha'),
|
||||||
|
path: 'foo/alpha',
|
||||||
|
tree: [
|
||||||
|
{
|
||||||
|
...file('gamma.md'),
|
||||||
|
path: 'foo/alpha/gamma.md',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = utils.mergeTrees(fromTree, toTree);
|
||||||
|
|
||||||
|
expect(res[1].name).toEqual('foo');
|
||||||
|
expect(res[1].opened).toEqual(true);
|
||||||
|
|
||||||
|
expect(res[1].tree[0].name).toEqual('alpha');
|
||||||
|
expect(res[1].tree[0].opened).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue