From 0ed510419aad30b531303b88f627cbea907d0efe Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Thu, 6 May 2021 16:26:58 +0100 Subject: [PATCH] Add more attributes to the blob GraphQL API `ide_edit_path`, `can_modify_blob`, `fork_and_edit_path` and `ide_fork_and_edit_path` all have implementations in `BlobHelper`, which are difficult to extract out. For now, just include the required helpers to get access to the code. In the future, we should be able to delete the existing FE for viewing the repository, and at that point we should also be able to move this code from BlobHelper and into BlobPresenter more easily. `external_storage_url` has its implementation in `ApplicationHelper`. Long term, moving this seems less likely. It probably fits better in a URL generation location like `GitlabRoutingHelpers`, but let's not try to tackle that as part of this MR. Changelog: added --- app/graphql/types/repository/blob_type.rb | 16 ++++++++ app/presenters/blob_presenter.rb | 28 +++++++++++++ .../323195-add-more-blob-attributes.yml | 5 +++ doc/api/graphql/reference/index.md | 5 +++ .../types/repository/blob_type_spec.rb | 7 +++- spec/presenters/blob_presenter_spec.rb | 39 ++++++++++++++++++- 6 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/323195-add-more-blob-attributes.yml diff --git a/app/graphql/types/repository/blob_type.rb b/app/graphql/types/repository/blob_type.rb index 004bceea154025..8ed97d7e6634da 100644 --- a/app/graphql/types/repository/blob_type.rb +++ b/app/graphql/types/repository/blob_type.rb @@ -32,6 +32,15 @@ class BlobType < BaseObject field :web_path, GraphQL::STRING_TYPE, null: true, description: 'Web path of the blob.' + field :ide_edit_path, GraphQL::STRING_TYPE, null: true, + description: 'Web path to edit this blob in the Web IDE.' + + field :fork_and_edit_path, GraphQL::STRING_TYPE, null: true, + description: 'Web path to edit this blob using a forked project.' + + field :ide_fork_and_edit_path, GraphQL::STRING_TYPE, null: true, + description: 'Web path to edit this blob in the Web IDE using a forked project.' + field :size, GraphQL::INT_TYPE, null: true, description: 'Size (in bytes) of the blob.' @@ -53,6 +62,9 @@ class BlobType < BaseObject field :raw_path, GraphQL::STRING_TYPE, null: true, description: 'Web path to download the raw blob.' + field :external_storage_url, GraphQL::STRING_TYPE, null: true, + description: 'Web path to download the raw blob via external storage, if enabled.' + field :replace_path, GraphQL::STRING_TYPE, null: true, description: 'Web path to replace the blob content.' @@ -72,6 +84,10 @@ class BlobType < BaseObject null: true, calls_gitaly: true + field :can_modify_blob, GraphQL::BOOLEAN_TYPE, null: true, method: :can_modify_blob?, + calls_gitaly: true, + description: 'Whether the current user can modify the blob.' + 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 cfe2bba8aa5b76..56dd056b9bc875 100644 --- a/app/presenters/blob_presenter.rb +++ b/app/presenters/blob_presenter.rb @@ -1,6 +1,12 @@ # frozen_string_literal: true class BlobPresenter < Gitlab::View::Presenter::Delegated + include ApplicationHelper + include BlobHelper + include DiffHelper + include TreeHelper + include ChecksCollaboration + presents :blob def highlight(to: nil, plain: nil) @@ -40,6 +46,28 @@ def replace_path url_helpers.project_create_blob_path(project, ref_qualified_path) end + def fork_and_edit_path + fork_path_for_current_user(project, edit_blob_path) + end + + def ide_fork_and_edit_path + fork_path_for_current_user(project, ide_edit_path) + end + + def can_modify_blob? + super(blob, project, blob.commit_id) + end + + def ide_edit_path + super(project, blob.commit_id, blob.path) + end + + def external_storage_url + return unless static_objects_external_storage_enabled? + + external_storage_url_or_path(url_helpers.project_raw_url(project, ref_qualified_path)) + end + private def url_helpers diff --git a/changelogs/unreleased/323195-add-more-blob-attributes.yml b/changelogs/unreleased/323195-add-more-blob-attributes.yml new file mode 100644 index 00000000000000..83ddedd47f9de1 --- /dev/null +++ b/changelogs/unreleased/323195-add-more-blob-attributes.yml @@ -0,0 +1,5 @@ +--- +title: Add more attributes to the blob GraphQL API +merge_request: 61155 +author: +type: added diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index a1c397890ef7ea..df17e0f58fd600 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -11784,9 +11784,14 @@ Returns [`Tree`](#tree). | Name | Type | Description | | ---- | ---- | ----------- | +| `canModifyBlob` | [`Boolean`](#boolean) | Whether the current user can modify the blob. | | `editBlobPath` | [`String`](#string) | Web path to edit the blob in the old-style editor. | +| `externalStorageUrl` | [`String`](#string) | Web path to download the raw blob via external storage, if enabled. | | `fileType` | [`String`](#string) | The expected format of the blob based on the extension. | +| `forkAndEditPath` | [`String`](#string) | Web path to edit this blob using a forked project. | | `id` | [`ID!`](#id) | ID of the blob. | +| `ideEditPath` | [`String`](#string) | Web path to edit this blob in the Web IDE. | +| `ideForkAndEditPath` | [`String`](#string) | Web path to edit this blob in the Web IDE using a forked project. | | `lfsOid` | [`String`](#string) | LFS OID of the blob. | | `mode` | [`String`](#string) | Blob mode. | | `name` | [`String`](#string) | Blob name. | diff --git a/spec/graphql/types/repository/blob_type_spec.rb b/spec/graphql/types/repository/blob_type_spec.rb index c588f8230de00b..beab4dcebc2f90 100644 --- a/spec/graphql/types/repository/blob_type_spec.rb +++ b/spec/graphql/types/repository/blob_type_spec.rb @@ -25,7 +25,12 @@ :replace_path, :simple_viewer, :rich_viewer, - :plain_data + :plain_data, + :can_modify_blob, + :ide_edit_path, + :external_storage_url, + :fork_and_edit_path, + :ide_fork_and_edit_path ) end end diff --git a/spec/presenters/blob_presenter_spec.rb b/spec/presenters/blob_presenter_spec.rb index 1fdd31b1f925ed..38bdf3b936437f 100644 --- a/spec/presenters/blob_presenter_spec.rb +++ b/spec/presenters/blob_presenter_spec.rb @@ -4,11 +4,12 @@ RSpec.describe BlobPresenter do let_it_be(:project) { create(:project, :repository) } + let_it_be(:user) { project.owner } let(:repository) { project.repository } let(:blob) { repository.blob_at('HEAD', 'files/ruby/regex.rb') } - subject(:presenter) { described_class.new(blob) } + subject(:presenter) { described_class.new(blob, current_user: user) } describe '#web_url' do it { expect(presenter.web_url).to eq("http://localhost/#{project.full_path}/-/blob/#{blob.commit_id}/#{blob.path}") } @@ -30,6 +31,42 @@ it { expect(presenter.replace_path).to eq("/#{project.full_path}/-/create/#{blob.commit_id}/#{blob.path}") } end + describe '#ide_edit_path' do + it { expect(presenter.ide_edit_path).to eq("/-/ide/project/#{project.full_path}/edit/HEAD/-/files/ruby/regex.rb") } + end + + describe '#fork_and_edit_path' do + it 'generates expected URI + query' do + uri = URI.parse(presenter.fork_and_edit_path) + query = Rack::Utils.parse_query(uri.query) + + expect(uri.path).to eq("/#{project.full_path}/-/forks") + expect(query).to include('continue[to]' => presenter.edit_blob_path, 'namespace_key' => user.namespace_id.to_s) + end + + context 'current_user is nil' do + let(:user) { nil } + + it { expect(presenter.fork_and_edit_path).to be_nil } + end + end + + describe '#ide_fork_and_edit_path' do + it 'generates expected URI + query' do + uri = URI.parse(presenter.ide_fork_and_edit_path) + query = Rack::Utils.parse_query(uri.query) + + expect(uri.path).to eq("/#{project.full_path}/-/forks") + expect(query).to include('continue[to]' => presenter.ide_edit_path, 'namespace_key' => user.namespace_id.to_s) + end + + context 'current_user is nil' do + let(:user) { nil } + + it { expect(presenter.ide_fork_and_edit_path).to be_nil } + end + end + context 'given a Gitlab::Graphql::Representation::TreeEntry' do let(:blob) { Gitlab::Graphql::Representation::TreeEntry.new(super(), repository) } -- GitLab