Merge branch '49663-branch-to-mr-connection' into 'master'
Link to the MR from current branch in Web IDE Closes #49663 See merge request gitlab-org/gitlab-ce!25689
This commit is contained in:
commit
1caa760f5d
8 changed files with 203 additions and 11 deletions
|
@ -12,6 +12,7 @@ const Api = {
|
|||
projectsPath: '/api/:version/projects.json',
|
||||
projectPath: '/api/:version/projects/:id',
|
||||
projectLabelsPath: '/:namespace_path/:project_path/labels',
|
||||
projectMergeRequestsPath: '/api/:version/projects/:id/merge_requests',
|
||||
projectMergeRequestPath: '/api/:version/projects/:id/merge_requests/:mrid',
|
||||
projectMergeRequestChangesPath: '/api/:version/projects/:id/merge_requests/:mrid/changes',
|
||||
projectMergeRequestVersionsPath: '/api/:version/projects/:id/merge_requests/:mrid/versions',
|
||||
|
@ -111,6 +112,22 @@ const Api = {
|
|||
return axios.get(url);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get all Merge Requests for a project, eventually filtering based on
|
||||
* supplied parameters
|
||||
* @param projectPath
|
||||
* @param params
|
||||
* @returns {Promise}
|
||||
*/
|
||||
projectMergeRequests(projectPath, params = {}) {
|
||||
const url = Api.buildUrl(Api.projectMergeRequestsPath).replace(
|
||||
':id',
|
||||
encodeURIComponent(projectPath),
|
||||
);
|
||||
|
||||
return axios.get(url, { params });
|
||||
},
|
||||
|
||||
// Return Merge Request for project
|
||||
projectMergeRequest(projectPath, mergeRequestId, params = {}) {
|
||||
const url = Api.buildUrl(Api.projectMergeRequestPath)
|
||||
|
|
|
@ -40,6 +40,9 @@ export default {
|
|||
getProjectData(namespace, project) {
|
||||
return Api.project(`${namespace}/${project}`);
|
||||
},
|
||||
getProjectMergeRequests(projectId, params = {}) {
|
||||
return Api.projectMergeRequests(projectId, params);
|
||||
},
|
||||
getProjectMergeRequestData(projectId, mergeRequestId, params = {}) {
|
||||
return Api.projectMergeRequest(projectId, mergeRequestId, params);
|
||||
},
|
||||
|
|
|
@ -4,6 +4,38 @@ import service from '../../services';
|
|||
import * as types from '../mutation_types';
|
||||
import { activityBarViews } from '../../constants';
|
||||
|
||||
export const getMergeRequestsForBranch = ({ commit }, { projectId, branchId } = {}) =>
|
||||
service
|
||||
.getProjectMergeRequests(`${projectId}`, {
|
||||
source_branch: branchId,
|
||||
order_by: 'created_at',
|
||||
per_page: 1,
|
||||
})
|
||||
.then(({ data }) => {
|
||||
if (data.length > 0) {
|
||||
const currentMR = data[0];
|
||||
|
||||
commit(types.SET_MERGE_REQUEST, {
|
||||
projectPath: projectId,
|
||||
mergeRequestId: currentMR.iid,
|
||||
mergeRequest: currentMR,
|
||||
});
|
||||
|
||||
commit(types.SET_CURRENT_MERGE_REQUEST, `${currentMR.iid}`);
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
flash(
|
||||
__(`Error fetching merge requests for ${branchId}`),
|
||||
'alert',
|
||||
document,
|
||||
null,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
throw e;
|
||||
});
|
||||
|
||||
export const getMergeRequestData = (
|
||||
{ commit, dispatch, state },
|
||||
{ projectId, mergeRequestId, targetProjectId = null, force = false } = {},
|
||||
|
|
|
@ -136,17 +136,24 @@ export const openBranch = ({ dispatch, state }, { projectId, branchId, basePath
|
|||
return dispatch('getFiles', {
|
||||
projectId,
|
||||
branchId,
|
||||
}).then(() => {
|
||||
if (basePath) {
|
||||
const path = basePath.slice(-1) === '/' ? basePath.slice(0, -1) : basePath;
|
||||
const treeEntryKey = Object.keys(state.entries).find(
|
||||
key => key === path && !state.entries[key].pending,
|
||||
);
|
||||
const treeEntry = state.entries[treeEntryKey];
|
||||
})
|
||||
.then(() => {
|
||||
if (basePath) {
|
||||
const path = basePath.slice(-1) === '/' ? basePath.slice(0, -1) : basePath;
|
||||
const treeEntryKey = Object.keys(state.entries).find(
|
||||
key => key === path && !state.entries[key].pending,
|
||||
);
|
||||
const treeEntry = state.entries[treeEntryKey];
|
||||
|
||||
if (treeEntry) {
|
||||
dispatch('handleTreeEntryAction', treeEntry);
|
||||
if (treeEntry) {
|
||||
dispatch('handleTreeEntryAction', treeEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
dispatch('getMergeRequestsForBranch', {
|
||||
projectId,
|
||||
branchId,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
5
changelogs/unreleased/49663-branch-to-mr-connection.yml
Normal file
5
changelogs/unreleased/49663-branch-to-mr-connection.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Link to most recent MR from a branch
|
||||
merge_request: 25689
|
||||
author:
|
||||
type: added
|
|
@ -139,6 +139,40 @@ describe('Api', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('projectMergeRequests', () => {
|
||||
const projectPath = 'abc';
|
||||
const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/projects/${projectPath}/merge_requests`;
|
||||
|
||||
it('fetches all merge requests for a project', done => {
|
||||
const mockData = [{ source_branch: 'foo' }, { source_branch: 'bar' }];
|
||||
mock.onGet(expectedUrl).reply(200, mockData);
|
||||
Api.projectMergeRequests(projectPath)
|
||||
.then(({ data }) => {
|
||||
expect(data.length).toEqual(2);
|
||||
expect(data[0].source_branch).toBe('foo');
|
||||
expect(data[1].source_branch).toBe('bar');
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('fetches merge requests filtered with passed params', done => {
|
||||
const params = {
|
||||
source_branch: 'bar',
|
||||
};
|
||||
const mockData = [{ source_branch: 'bar' }];
|
||||
mock.onGet(expectedUrl, { params }).reply(200, mockData);
|
||||
|
||||
Api.projectMergeRequests(projectPath, params)
|
||||
.then(({ data }) => {
|
||||
expect(data.length).toEqual(1);
|
||||
expect(data[0].source_branch).toBe('bar');
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
});
|
||||
|
||||
describe('projectMergeRequest', () => {
|
||||
it('fetches a merge request', done => {
|
||||
const projectPath = 'abc';
|
||||
|
|
|
@ -2,6 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
|
|||
import axios from '~/lib/utils/axios_utils';
|
||||
import store from '~/ide/stores';
|
||||
import actions, {
|
||||
getMergeRequestsForBranch,
|
||||
getMergeRequestData,
|
||||
getMergeRequestChanges,
|
||||
getMergeRequestVersions,
|
||||
|
@ -27,6 +28,98 @@ describe('IDE store merge request actions', () => {
|
|||
resetStore(store);
|
||||
});
|
||||
|
||||
describe('getMergeRequestsForBranch', () => {
|
||||
describe('success', () => {
|
||||
const mrData = { iid: 2, source_branch: 'bar' };
|
||||
const mockData = [mrData];
|
||||
|
||||
describe('base case', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(service, 'getProjectMergeRequests').and.callThrough();
|
||||
mock.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests/).reply(200, mockData);
|
||||
});
|
||||
|
||||
it('calls getProjectMergeRequests service method', done => {
|
||||
store
|
||||
.dispatch('getMergeRequestsForBranch', { projectId: 'abcproject', branchId: 'bar' })
|
||||
.then(() => {
|
||||
expect(service.getProjectMergeRequests).toHaveBeenCalledWith('abcproject', {
|
||||
source_branch: 'bar',
|
||||
order_by: 'created_at',
|
||||
per_page: 1,
|
||||
});
|
||||
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('sets the "Merge Request" Object', done => {
|
||||
store
|
||||
.dispatch('getMergeRequestsForBranch', { projectId: 'abcproject', branchId: 'bar' })
|
||||
.then(() => {
|
||||
expect(Object.keys(store.state.projects.abcproject.mergeRequests).length).toEqual(1);
|
||||
expect(Object.keys(store.state.projects.abcproject.mergeRequests)[0]).toEqual('2');
|
||||
expect(store.state.projects.abcproject.mergeRequests[2]).toEqual(
|
||||
jasmine.objectContaining(mrData),
|
||||
);
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('sets "Current Merge Request" object to the most recent MR', done => {
|
||||
store
|
||||
.dispatch('getMergeRequestsForBranch', { projectId: 'abcproject', branchId: 'bar' })
|
||||
.then(() => {
|
||||
expect(store.state.currentMergeRequestId).toEqual('2');
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
});
|
||||
});
|
||||
|
||||
describe('no merge requests for branch available case', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(service, 'getProjectMergeRequests').and.callThrough();
|
||||
mock.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests/).reply(200, []);
|
||||
});
|
||||
|
||||
it('does not fail if there are no merge requests for current branch', done => {
|
||||
store
|
||||
.dispatch('getMergeRequestsForBranch', { projectId: 'abcproject', branchId: 'foo' })
|
||||
.then(() => {
|
||||
expect(Object.keys(store.state.projects.abcproject.mergeRequests).length).toEqual(0);
|
||||
expect(store.state.currentMergeRequestId).toEqual('');
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('error', () => {
|
||||
beforeEach(() => {
|
||||
mock.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests/).networkError();
|
||||
});
|
||||
|
||||
it('flashes message, if error', done => {
|
||||
const flashSpy = spyOnDependency(actions, 'flash');
|
||||
|
||||
getMergeRequestsForBranch({ commit() {} }, { projectId: 'abcproject', branchId: 'bar' })
|
||||
.then(() => {
|
||||
fail('Expected getMergeRequestsForBranch to throw an error');
|
||||
})
|
||||
.catch(() => {
|
||||
expect(flashSpy).toHaveBeenCalled();
|
||||
expect(flashSpy.calls.argsFor(0)[0]).toEqual('Error fetching merge requests for bar');
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMergeRequestData', () => {
|
||||
describe('success', () => {
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -249,6 +249,7 @@ describe('IDE store project actions', () => {
|
|||
['setCurrentBranchId', branch.branchId],
|
||||
['getBranchData', branch],
|
||||
['getFiles', branch],
|
||||
['getMergeRequestsForBranch', branch],
|
||||
]);
|
||||
})
|
||||
.then(done)
|
||||
|
|
Loading…
Reference in a new issue