From 3a77664d1a550cfbedc92068926e4adae9c82b87 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 10 Jul 2018 10:35:16 +0100 Subject: Allow merge requests from forks to be opened in Web IDE Closes #47460 --- app/assets/javascripts/ide/ide_router.js | 3 +++ .../javascripts/ide/stores/actions/merge_request.js | 14 ++++++++------ .../components/mr_widget_header.vue | 6 +++++- .../vue_merge_request_widget/stores/mr_widget_store.js | 10 +++++++--- app/serializers/merge_request_widget_entity.rb | 6 ++++++ .../api/schemas/entities/merge_request_widget.json | 2 ++ spec/javascripts/vue_mr_widget/mock_data.js | 2 ++ spec/serializers/merge_request_widget_entity_spec.rb | 15 +++++++++++++++ 8 files changed, 48 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js index cc8dbb942d8..44c35e9a5a5 100644 --- a/app/assets/javascripts/ide/ide_router.js +++ b/app/assets/javascripts/ide/ide_router.js @@ -101,6 +101,7 @@ router.beforeEach((to, from, next) => { store .dispatch('getMergeRequestData', { projectId: fullProjectId, + targetProjectId: to.query.target_project, mergeRequestId: to.params.mrid, }) .then(mr => { @@ -119,12 +120,14 @@ router.beforeEach((to, from, next) => { .then(() => store.dispatch('getMergeRequestVersions', { projectId: fullProjectId, + targetProjectId: to.query.target_project, mergeRequestId: to.params.mrid, }), ) .then(() => store.dispatch('getMergeRequestChanges', { projectId: fullProjectId, + targetProjectId: to.query.target_project, mergeRequestId: to.params.mrid, }), ) diff --git a/app/assets/javascripts/ide/stores/actions/merge_request.js b/app/assets/javascripts/ide/stores/actions/merge_request.js index 6bdf9dc3028..1887b77b00b 100644 --- a/app/assets/javascripts/ide/stores/actions/merge_request.js +++ b/app/assets/javascripts/ide/stores/actions/merge_request.js @@ -4,12 +4,14 @@ import * as types from '../mutation_types'; export const getMergeRequestData = ( { commit, dispatch, state }, - { projectId, mergeRequestId, force = false } = {}, + { projectId, mergeRequestId, targetProjectId = null, force = false } = {}, ) => new Promise((resolve, reject) => { if (!state.projects[projectId].mergeRequests[mergeRequestId] || force) { service - .getProjectMergeRequestData(projectId, mergeRequestId, { render_html: true }) + .getProjectMergeRequestData(targetProjectId || projectId, mergeRequestId, { + render_html: true, + }) .then(({ data }) => { commit(types.SET_MERGE_REQUEST, { projectPath: projectId, @@ -38,12 +40,12 @@ export const getMergeRequestData = ( export const getMergeRequestChanges = ( { commit, dispatch, state }, - { projectId, mergeRequestId, force = false } = {}, + { projectId, mergeRequestId, targetProjectId = null, force = false } = {}, ) => new Promise((resolve, reject) => { if (!state.projects[projectId].mergeRequests[mergeRequestId].changes.length || force) { service - .getProjectMergeRequestChanges(projectId, mergeRequestId) + .getProjectMergeRequestChanges(targetProjectId || projectId, mergeRequestId) .then(({ data }) => { commit(types.SET_MERGE_REQUEST_CHANGES, { projectPath: projectId, @@ -71,12 +73,12 @@ export const getMergeRequestChanges = ( export const getMergeRequestVersions = ( { commit, dispatch, state }, - { projectId, mergeRequestId, force = false } = {}, + { projectId, mergeRequestId, targetProjectId = null, force = false } = {}, ) => new Promise((resolve, reject) => { if (!state.projects[projectId].mergeRequests[mergeRequestId].versions.length || force) { service - .getProjectMergeRequestVersions(projectId, mergeRequestId) + .getProjectMergeRequestVersions(targetProjectId || projectId, mergeRequestId) .then(res => res.data) .then(data => { commit(types.SET_MERGE_REQUEST_VERSIONS, { diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue index c18b74743e4..a3b1e7d1340 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue @@ -43,7 +43,11 @@ export default { return this.isBranchTitleLong(this.mr.targetBranch); }, webIdePath() { - return webIDEUrl(this.mr.statusPath.replace('.json', '')); + return webIDEUrl( + `/${this.mr.sourceProjectFullPath}/merge_requests/${this.mr.iid}?target_project=${ + this.mr.targetProjectFullPath + }`, + ); }, }, methods: { diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js index c881cd496d1..e84c436905d 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js @@ -16,10 +16,11 @@ export default class MergeRequestStore { const pipelineStatus = data.pipeline ? data.pipeline.details.status : null; this.squash = data.squash; - this.squashBeforeMergeHelpPath = this.squashBeforeMergeHelpPath || - data.squash_before_merge_help_path; + this.squashBeforeMergeHelpPath = + this.squashBeforeMergeHelpPath || data.squash_before_merge_help_path; this.enableSquashBeforeMerge = this.enableSquashBeforeMerge || true; + this.iid = data.iid; this.title = data.title; this.targetBranch = data.target_branch; this.sourceBranch = data.source_branch; @@ -85,6 +86,8 @@ export default class MergeRequestStore { this.isMergeAllowed = data.mergeable || false; this.mergeOngoing = data.merge_ongoing; this.allowCollaboration = data.allow_collaboration; + this.targetProjectFullPath = data.target_project_full_path; + this.sourceProjectFullPath = data.source_project_full_path; // Cherry-pick and Revert actions related this.canCherryPickInCurrentMR = currentUser.can_cherry_pick_on_current_merge_request || false; @@ -97,7 +100,8 @@ export default class MergeRequestStore { this.hasCI = data.has_ci; this.ciStatus = data.ci_status; this.isPipelineFailed = this.ciStatus === 'failed' || this.ciStatus === 'canceled'; - this.isPipelinePassing = this.ciStatus === 'success' || this.ciStatus === 'success_with_warnings'; + this.isPipelinePassing = + this.ciStatus === 'success' || this.ciStatus === 'success_with_warnings'; this.isPipelineSkipped = this.ciStatus === 'skipped'; this.pipelineDetailedStatus = pipelineStatus; this.isPipelineActive = data.pipeline ? data.pipeline.active : false; diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb index 5d72ebdd7fd..a78bd77cf7c 100644 --- a/app/serializers/merge_request_widget_entity.rb +++ b/app/serializers/merge_request_widget_entity.rb @@ -10,9 +10,15 @@ class MergeRequestWidgetEntity < IssuableEntity expose :merge_when_pipeline_succeeds expose :source_branch expose :source_project_id + expose :source_project_full_path do |merge_request| + merge_request.source_project&.full_path + end expose :squash expose :target_branch expose :target_project_id + expose :target_project_full_path do |merge_request| + merge_request.project&.full_path + end expose :allow_collaboration expose :should_be_rebased?, as: :should_be_rebased diff --git a/spec/fixtures/api/schemas/entities/merge_request_widget.json b/spec/fixtures/api/schemas/entities/merge_request_widget.json index 38ce92a5dc7..3b741d51598 100644 --- a/spec/fixtures/api/schemas/entities/merge_request_widget.json +++ b/spec/fixtures/api/schemas/entities/merge_request_widget.json @@ -29,8 +29,10 @@ "merge_when_pipeline_succeeds": { "type": "boolean" }, "source_branch": { "type": "string" }, "source_project_id": { "type": "integer" }, + "source_project_full_path": { "type": ["string", "null"]}, "target_branch": { "type": "string" }, "target_project_id": { "type": "integer" }, + "target_project_full_path": { "type": ["string", "null"]}, "allow_collaboration": { "type": "boolean"}, "metrics": { "oneOf": [ diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js index 9d2a15ff009..c0b5a7d4455 100644 --- a/spec/javascripts/vue_mr_widget/mock_data.js +++ b/spec/javascripts/vue_mr_widget/mock_data.js @@ -29,8 +29,10 @@ export default { source_branch: 'daaaa', source_branch_link: 'daaaa', source_project_id: 19, + source_project_full_path: '/group1/project1', target_branch: 'master', target_project_id: 19, + target_project_full_path: '/group2/project2', metrics: { merged_by: { name: 'Administrator', diff --git a/spec/serializers/merge_request_widget_entity_spec.rb b/spec/serializers/merge_request_widget_entity_spec.rb index d2072198d83..0ba2539a717 100644 --- a/spec/serializers/merge_request_widget_entity_spec.rb +++ b/spec/serializers/merge_request_widget_entity_spec.rb @@ -11,6 +11,21 @@ describe MergeRequestWidgetEntity do described_class.new(resource, request: request).as_json end + describe 'source_project_full_path' do + it 'includes the full path of the source project' do + expect(subject[:source_project_full_path]).to be_present + end + + context 'when the source project is missing' do + it 'returns `nil` for the source project' do + resource.allow_broken = true + resource.update!(source_project: nil) + + expect(subject[:source_project_full_path]).to be_nil + end + end + end + describe 'pipeline' do let(:pipeline) { create(:ci_empty_pipeline, project: project, ref: resource.source_branch, sha: resource.source_branch_sha, head_pipeline_of: resource) } -- cgit v1.2.1