From c38e4ccde4272ef572b531d478f9803d1e7e4234 Mon Sep 17 00:00:00 2001 From: Jacques <jerasmus@gitlab.com> Date: Mon, 22 Nov 2021 15:35:42 +0100 Subject: [PATCH] Add canCurrentUserPushToBranch permission Added canCurrentUserPushToBranch to graphQL Changelog: added --- .../components/blob_button_group.vue | 5 ++++ .../components/blob_content_viewer.vue | 2 ++ .../components/delete_blob_modal.vue | 13 +++++++--- .../queries/blob_info.query.graphql | 1 + app/graphql/types/repository/blob_type.rb | 3 +++ app/presenters/blob_presenter.rb | 6 +++++ doc/api/graphql/reference/index.md | 1 + .../components/blob_button_group_spec.js | 1 + .../components/blob_button_group_spec.js | 1 + .../components/delete_blob_modal_spec.js | 26 +++++++++++-------- spec/frontend/repository/mock_data.js | 1 + .../types/repository/blob_type_spec.rb | 1 + spec/presenters/blob_presenter_spec.rb | 22 ++++++++++++++++ 13 files changed, 69 insertions(+), 14 deletions(-) diff --git a/app/assets/javascripts/repository/components/blob_button_group.vue b/app/assets/javascripts/repository/components/blob_button_group.vue index e2ba5cea6ccdc..de6156d48dc64 100644 --- a/app/assets/javascripts/repository/components/blob_button_group.vue +++ b/app/assets/javascripts/repository/components/blob_button_group.vue @@ -53,6 +53,10 @@ export default { type: Boolean, required: true, }, + canPushToBranch: { + type: Boolean, + required: true, + }, emptyRepo: { type: Boolean, required: true, @@ -126,6 +130,7 @@ export default { :target-branch="targetBranch || ref" :original-branch="originalBranch || ref" :can-push-code="canPushCode" + :can-push-to-branch="canPushToBranch" :empty-repo="emptyRepo" /> </div> diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue index 2cc5a8a79d23b..cea95645fa4bf 100644 --- a/app/assets/javascripts/repository/components/blob_content_viewer.vue +++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue @@ -106,6 +106,7 @@ export default { ideForkAndEditPath: '', storedExternally: false, canModifyBlob: false, + canCurrentUserPushToBranch: false, rawPath: '', externalStorageUrl: '', replacePath: '', @@ -266,6 +267,7 @@ export default { :replace-path="blobInfo.replacePath" :delete-path="blobInfo.webPath" :can-push-code="project.userPermissions.pushCode" + :can-push-to-branch="blobInfo.canCurrentUserPushToBranch" :empty-repo="project.repository.empty" :project-path="projectPath" :is-locked="isLocked" diff --git a/app/assets/javascripts/repository/components/delete_blob_modal.vue b/app/assets/javascripts/repository/components/delete_blob_modal.vue index e15f9b01c6278..0d3dc06c2c8ec 100644 --- a/app/assets/javascripts/repository/components/delete_blob_modal.vue +++ b/app/assets/javascripts/repository/components/delete_blob_modal.vue @@ -71,6 +71,10 @@ export default { type: Boolean, required: true, }, + canPushToBranch: { + type: Boolean, + required: true, + }, emptyRepo: { type: Boolean, required: true, @@ -176,9 +180,12 @@ export default { </template> <template v-else> <input type="hidden" name="original_branch" :value="originalBranch" /> - <!-- Once "push to branch" permission is made available, will need to add to conditional - Follow-up issue: https://gitlab.com/gitlab-org/gitlab/-/issues/335462 --> - <input v-if="createNewMr" type="hidden" name="create_merge_request" value="1" /> + <input + v-if="createNewMr || !canPushToBranch" + type="hidden" + name="create_merge_request" + value="1" + /> <gl-form-group :label="$options.i18n.COMMIT_LABEL" label-for="commit_message" diff --git a/app/assets/javascripts/repository/queries/blob_info.query.graphql b/app/assets/javascripts/repository/queries/blob_info.query.graphql index cf3892802fdec..539719175e35f 100644 --- a/app/assets/javascripts/repository/queries/blob_info.query.graphql +++ b/app/assets/javascripts/repository/queries/blob_info.query.graphql @@ -28,6 +28,7 @@ query getBlobInfo($projectPath: ID!, $filePath: String!, $ref: String!) { forkAndEditPath ideForkAndEditPath canModifyBlob + canCurrentUserPushToBranch storedExternally rawPath replacePath diff --git a/app/graphql/types/repository/blob_type.rb b/app/graphql/types/repository/blob_type.rb index 104171e6772d3..cd4993ea86ddd 100644 --- a/app/graphql/types/repository/blob_type.rb +++ b/app/graphql/types/repository/blob_type.rb @@ -91,6 +91,9 @@ class BlobType < BaseObject calls_gitaly: true, description: 'Whether the current user can modify the blob.' + field :can_current_user_push_to_branch, GraphQL::Types::Boolean, null: true, method: :can_current_user_push_to_branch?, + description: 'Whether the current user can push to the branch.' + def raw_text_blob object.data unless object.binary? end diff --git a/app/presenters/blob_presenter.rb b/app/presenters/blob_presenter.rb index 5835a77d0b90d..3555c6c3d0cbf 100644 --- a/app/presenters/blob_presenter.rb +++ b/app/presenters/blob_presenter.rb @@ -78,6 +78,12 @@ def can_modify_blob? super(blob, project, blob.commit_id) end + def can_current_user_push_to_branch? + return false unless current_user && project.repository.branch_exists?(blob.commit_id) + + user_access(project).can_push_to_branch?(blob.commit_id) + end + def ide_edit_path super(project, blob.commit_id, blob.path) end diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index d49eee5d15c34..c4167e3ecd215 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -14072,6 +14072,7 @@ Returns [`Tree`](#tree). | Name | Type | Description | | ---- | ---- | ----------- | +| <a id="repositoryblobcancurrentuserpushtobranch"></a>`canCurrentUserPushToBranch` | [`Boolean`](#boolean) | Whether the current user can push to the branch. | | <a id="repositoryblobcanmodifyblob"></a>`canModifyBlob` | [`Boolean`](#boolean) | Whether the current user can modify the blob. | | <a id="repositoryblobeditblobpath"></a>`editBlobPath` | [`String`](#string) | Web path to edit the blob in the old-style editor. | | <a id="repositoryblobexternalstorageurl"></a>`externalStorageUrl` | [`String`](#string) | Web path to download the raw blob via external storage, if enabled. | diff --git a/ee/spec/frontend/repository/components/blob_button_group_spec.js b/ee/spec/frontend/repository/components/blob_button_group_spec.js index 3db9e53c65949..8719c5316db56 100644 --- a/ee/spec/frontend/repository/components/blob_button_group_spec.js +++ b/ee/spec/frontend/repository/components/blob_button_group_spec.js @@ -6,6 +6,7 @@ const DEFAULT_PROPS = { name: 'some name', path: 'some/path', canPushCode: true, + canPushToBranch: true, replacePath: 'some/replace/path', deletePath: 'some/delete/path', emptyRepo: false, diff --git a/spec/frontend/repository/components/blob_button_group_spec.js b/spec/frontend/repository/components/blob_button_group_spec.js index f2a3354f204c6..9f9d574a8ed37 100644 --- a/spec/frontend/repository/components/blob_button_group_spec.js +++ b/spec/frontend/repository/components/blob_button_group_spec.js @@ -9,6 +9,7 @@ const DEFAULT_PROPS = { name: 'some name', path: 'some/path', canPushCode: true, + canPushToBranch: true, replacePath: 'some/replace/path', deletePath: 'some/delete/path', emptyRepo: false, diff --git a/spec/frontend/repository/components/delete_blob_modal_spec.js b/spec/frontend/repository/components/delete_blob_modal_spec.js index 2c62868f3916f..785783b2e7588 100644 --- a/spec/frontend/repository/components/delete_blob_modal_spec.js +++ b/spec/frontend/repository/components/delete_blob_modal_spec.js @@ -13,6 +13,7 @@ const initialProps = { targetBranch: 'some-target-branch', originalBranch: 'main', canPushCode: true, + canPushToBranch: true, emptyRepo: false, }; @@ -103,22 +104,25 @@ describe('DeleteBlobModal', () => { ); it.each` - input | value | emptyRepo | canPushCode | exist - ${'authenticity_token'} | ${'mock-csrf-token'} | ${false} | ${true} | ${true} - ${'authenticity_token'} | ${'mock-csrf-token'} | ${true} | ${false} | ${true} - ${'_method'} | ${'delete'} | ${false} | ${true} | ${true} - ${'_method'} | ${'delete'} | ${true} | ${false} | ${true} - ${'original_branch'} | ${initialProps.originalBranch} | ${false} | ${true} | ${true} - ${'original_branch'} | ${undefined} | ${true} | ${true} | ${false} - ${'create_merge_request'} | ${'1'} | ${false} | ${false} | ${true} - ${'create_merge_request'} | ${'1'} | ${false} | ${true} | ${true} - ${'create_merge_request'} | ${undefined} | ${true} | ${false} | ${false} + input | value | emptyRepo | canPushCode | canPushToBranch | exist + ${'authenticity_token'} | ${'mock-csrf-token'} | ${false} | ${true} | ${true} | ${true} + ${'authenticity_token'} | ${'mock-csrf-token'} | ${true} | ${false} | ${true} | ${true} + ${'_method'} | ${'delete'} | ${false} | ${true} | ${true} | ${true} + ${'_method'} | ${'delete'} | ${true} | ${false} | ${true} | ${true} + ${'original_branch'} | ${initialProps.originalBranch} | ${false} | ${true} | ${true} | ${true} + ${'original_branch'} | ${undefined} | ${true} | ${true} | ${true} | ${false} + ${'create_merge_request'} | ${'1'} | ${false} | ${false} | ${true} | ${true} + ${'create_merge_request'} | ${'1'} | ${false} | ${true} | ${true} | ${true} + ${'create_merge_request'} | ${'1'} | ${false} | ${false} | ${false} | ${true} + ${'create_merge_request'} | ${'1'} | ${false} | ${false} | ${true} | ${true} + ${'create_merge_request'} | ${undefined} | ${true} | ${false} | ${true} | ${false} `( 'passes $input as a hidden input with the correct value', - ({ input, value, emptyRepo, canPushCode, exist }) => { + ({ input, value, emptyRepo, canPushCode, canPushToBranch, exist }) => { createComponent({ emptyRepo, canPushCode, + canPushToBranch, }); const inputMethod = findForm().find(`input[name="${input}"]`); diff --git a/spec/frontend/repository/mock_data.js b/spec/frontend/repository/mock_data.js index adf5991ac3c8e..99018de6efd85 100644 --- a/spec/frontend/repository/mock_data.js +++ b/spec/frontend/repository/mock_data.js @@ -11,6 +11,7 @@ export const simpleViewerMock = { forkAndEditPath: 'some_file.js/fork/edit', ideForkAndEditPath: 'some_file.js/fork/ide', canModifyBlob: true, + canCurrentUserPushToBranch: true, storedExternally: false, rawPath: 'some_file.js', replacePath: 'some_file.js/replace', diff --git a/spec/graphql/types/repository/blob_type_spec.rb b/spec/graphql/types/repository/blob_type_spec.rb index 7f37237f355b2..968cb026401a1 100644 --- a/spec/graphql/types/repository/blob_type_spec.rb +++ b/spec/graphql/types/repository/blob_type_spec.rb @@ -28,6 +28,7 @@ :rich_viewer, :plain_data, :can_modify_blob, + :can_current_user_push_to_branch, :ide_edit_path, :external_storage_url, :fork_and_edit_path, diff --git a/spec/presenters/blob_presenter_spec.rb b/spec/presenters/blob_presenter_spec.rb index 28e18708eab1d..cd38e74e0eaf3 100644 --- a/spec/presenters/blob_presenter_spec.rb +++ b/spec/presenters/blob_presenter_spec.rb @@ -31,6 +31,28 @@ it { expect(presenter.replace_path).to eq("/#{project.full_path}/-/create/#{blob.commit_id}/#{blob.path}") } end + describe '#can_current_user_push_to_branch' do + let(:branch_exists) { true } + + before do + allow(project.repository).to receive(:branch_exists?).with(blob.commit_id).and_return(branch_exists) + end + + it { expect(presenter.can_current_user_push_to_branch?).to eq(true) } + + context 'current_user is nil' do + let(:user) { nil } + + it { expect(presenter.can_current_user_push_to_branch?).to eq(false) } + end + + context 'branch does not exist' do + let(:branch_exists) { false } + + it { expect(presenter.can_current_user_push_to_branch?).to eq(false) } + end + end + describe '#pipeline_editor_path' do context 'when blob is .gitlab-ci.yml' do before do -- GitLab