diff --git a/app/graphql/types/repository/blob_type.rb b/app/graphql/types/repository/blob_type.rb index 004bceea154025f0b2a9f8e3f815abeb1cce9082..8ed97d7e6634da57310a128e8fb5c828b14ec4ab 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 cfe2bba8aa5b76cf06fb6f3942df05a6d5316a72..56dd056b9bc875bb523bca75a73d59edc5514dde 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 0000000000000000000000000000000000000000..83ddedd47f9de12e185ef1fab9a9250a16617662 --- /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 a1c397890ef7eab581a4d3c6ffffa1c7ceaca0eb..df17e0f58fd6005a8e81362c19b5f18104b7213b 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 c588f8230de00b8f6c346dd5448f133b5e330c7c..beab4dcebc2f904da37de1ada4899826dd09784e 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 1fdd31b1f925ed09eeb501fb2c602a5591398c3d..38bdf3b936437f4aaba7f04b49f61445dd4c195c 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) }