Commit 3248f11f authored by Nick Thomas's avatar Nick Thomas

Add more fields to the GraphQL blob type

Currently, the blobs type is targeted towards viewing a whole directory
at a time, rather than zooming in to the detail of an individual blob.
Adding more fields allows it to tackle both at the same time.

Changelog: added
parent 5589ff35
......@@ -2,28 +2,25 @@ query getBlobInfo($projectPath: ID!, $filePath: String!) {
project(fullPath: $projectPath) {
id
repository {
blobs(path: $filePath) {
name
size
rawBlob
type
fileType
tooLarge
path
editBlobPath
ideEditPath
storedExternally
rawPath
externalStorageUrl
replacePath
deletePath
canLock
isLocked
lockLink
canModifyBlob
forkPath
simpleViewer
richViewer
blobs(paths: [$filePath]) {
nodes {
webPath
name
rawSize
rawTextBlob
fileType
path
editBlobPath
ideEditPath
storedExternally
rawPath
externalStorageUrl
replacePath
canModifyBlob
forkPath
simpleViewer
richViewer
}
}
}
}
......
# frozen_string_literal: true
module Types
class BlobViewerType < BaseObject # rubocop:disable Graphql/AuthorizeTypes
graphql_name 'BlobViewer'
description 'Represents how the blob content should be displayed'
field :type, Types::BlobViewers::TypeEnum,
description: 'Type of blob viewer.',
null: false
field :load_async, GraphQL::BOOLEAN_TYPE,
description: 'Shows whether the blob content is loaded asynchronously.',
null: false
field :collapsed, GraphQL::BOOLEAN_TYPE,
description: 'Shows whether the blob should be displayed collapsed.',
method: :collapsed?,
null: false
field :too_large, GraphQL::BOOLEAN_TYPE,
description: 'Shows whether the blob is too large to be displayed.',
method: :too_large?,
null: false
field :render_error, GraphQL::STRING_TYPE,
description: 'Error rendering the blob content.',
null: true
field :file_type, GraphQL::STRING_TYPE,
description: 'Content file type.',
method: :partial_name,
null: false
field :loading_partial_name, GraphQL::STRING_TYPE,
description: 'Loading partial name.',
null: false
def collapsed
!!object&.collapsed?
end
def too_large
!!object&.too_large?
end
end
end
......@@ -32,6 +32,45 @@ module Types
field :web_path, GraphQL::STRING_TYPE, null: true,
description: 'Web path of the blob.'
field :size, GraphQL::INT_TYPE, null: true,
description: 'Size (in bytes) of the blob.'
field :raw_size, GraphQL::INT_TYPE, null: true,
description: 'Size (in bytes) of the blob, or the blob target if stored externally.'
field :raw_blob, GraphQL::STRING_TYPE, null: true, method: :data,
description: 'The raw content of the blob.'
field :raw_text_blob, GraphQL::STRING_TYPE, null: true, method: :text_only_data,
description: 'The raw content of the blob, if the blob is text data.'
field :stored_externally, GraphQL::BOOLEAN_TYPE, null: true, method: :stored_externally?,
description: "Whether the blob's content is stored externally (for instance, in LFS)."
field :edit_blob_path, GraphQL::STRING_TYPE, null: true,
description: 'Web path to edit the blob in the old-style editor.'
field :raw_path, GraphQL::STRING_TYPE, null: true,
description: 'Web path to download the raw blob.'
field :replace_path, GraphQL::STRING_TYPE, null: true,
description: 'Web path to replace the blob content.'
field :file_type, GraphQL::STRING_TYPE, null: true,
description: 'The expected format of the blob based on the extension.'
field :simple_viewer, type: Types::BlobViewerType,
description: 'Blob content simple viewer.',
null: false
field :rich_viewer, type: Types::BlobViewerType,
description: 'Blob content rich viewer.',
null: true
def raw_text_blob
object.data unless object.binary?
end
def lfs_oid
Gitlab::Graphql::Loaders::BatchLfsOidLoader.new(object.repository, object.id).find
end
......
......@@ -2,48 +2,10 @@
module Types
module Snippets
class BlobViewerType < BaseObject # rubocop:disable Graphql/AuthorizeTypes
# Kept to avoid changing the type of existing fields. New fields should use
# ::Types::BlobViewerType directly
class BlobViewerType < ::Types::BlobViewerType # rubocop:disable Graphql/AuthorizeTypes
graphql_name 'SnippetBlobViewer'
description 'Represents how the blob content should be displayed'
field :type, Types::BlobViewers::TypeEnum,
description: 'Type of blob viewer.',
null: false
field :load_async, GraphQL::BOOLEAN_TYPE,
description: 'Shows whether the blob content is loaded asynchronously.',
null: false
field :collapsed, GraphQL::BOOLEAN_TYPE,
description: 'Shows whether the blob should be displayed collapsed.',
method: :collapsed?,
null: false
field :too_large, GraphQL::BOOLEAN_TYPE,
description: 'Shows whether the blob too large to be displayed.',
method: :too_large?,
null: false
field :render_error, GraphQL::STRING_TYPE,
description: 'Error rendering the blob content.',
null: true
field :file_type, GraphQL::STRING_TYPE,
description: 'Content file type.',
method: :partial_name,
null: false
field :loading_partial_name, GraphQL::STRING_TYPE,
description: 'Loading partial name.',
null: false
def collapsed
!!object&.collapsed?
end
def too_large
!!object&.too_large?
end
end
end
end
......@@ -15,15 +15,39 @@ class BlobPresenter < Gitlab::View::Presenter::Delegated
end
def web_url
Gitlab::Routing.url_helpers.project_blob_url(blob.repository.project, File.join(blob.commit_id, blob.path))
url_helpers.project_blob_url(project, ref_qualified_path)
end
def web_path
Gitlab::Routing.url_helpers.project_blob_path(blob.repository.project, File.join(blob.commit_id, blob.path))
url_helpers.project_blob_path(project, ref_qualified_path)
end
def edit_blob_path
url_helpers.project_edit_blob_path(project, ref_qualified_path)
end
def raw_path
url_helpers.project_raw_path(project, ref_qualified_path)
end
def replace_path
url_helpers.project_create_blob_path(project, ref_qualified_path)
end
private
def url_helpers
Gitlab::Routing.url_helpers
end
def project
blob.repository.project
end
def ref_qualified_path
File.join(blob.commit_id, blob.path)
end
def load_all_blob_data
blob.load_all_data! if blob.respond_to?(:load_all_data!)
end
......
---
title: Add more fields to the GraphQL blob type
merge_request: 58906
author:
type: added
......@@ -6742,6 +6742,22 @@ An emoji awarded by a user.
| <a id="blobwebpath"></a>`webPath` | [`String`](#string) | Web path of the blob. |
| <a id="blobweburl"></a>`webUrl` | [`String`](#string) | Web URL of the blob. |
### `BlobViewer`
Represents how the blob content should be displayed.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="blobviewercollapsed"></a>`collapsed` | [`Boolean!`](#boolean) | Shows whether the blob should be displayed collapsed. |
| <a id="blobviewerfiletype"></a>`fileType` | [`String!`](#string) | Content file type. |
| <a id="blobviewerloadasync"></a>`loadAsync` | [`Boolean!`](#boolean) | Shows whether the blob content is loaded asynchronously. |
| <a id="blobviewerloadingpartialname"></a>`loadingPartialName` | [`String!`](#string) | Loading partial name. |
| <a id="blobviewerrendererror"></a>`renderError` | [`String`](#string) | Error rendering the blob content. |
| <a id="blobviewertoolarge"></a>`tooLarge` | [`Boolean!`](#boolean) | Shows whether the blob is too large to be displayed. |
| <a id="blobviewertype"></a>`type` | [`BlobViewersType!`](#blobviewerstype) | Type of blob viewer. |
### `Board`
Represents a project or group issue board.
......@@ -11257,12 +11273,23 @@ Returns [`Tree`](#tree).
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="repositoryblobeditblobpath"></a>`editBlobPath` | [`String`](#string) | Web path to edit the blob in the old-style editor. |
| <a id="repositoryblobfiletype"></a>`fileType` | [`String`](#string) | The expected format of the blob based on the extension. |
| <a id="repositoryblobid"></a>`id` | [`ID!`](#id) | ID of the blob. |
| <a id="repositorybloblfsoid"></a>`lfsOid` | [`String`](#string) | LFS OID of the blob. |
| <a id="repositoryblobmode"></a>`mode` | [`String`](#string) | Blob mode. |
| <a id="repositoryblobname"></a>`name` | [`String`](#string) | Blob name. |
| <a id="repositorybloboid"></a>`oid` | [`String!`](#string) | OID of the blob. |
| <a id="repositoryblobpath"></a>`path` | [`String!`](#string) | Path of the blob. |
| <a id="repositoryblobrawblob"></a>`rawBlob` | [`String`](#string) | The raw content of the blob. |
| <a id="repositoryblobrawpath"></a>`rawPath` | [`String`](#string) | Web path to download the raw blob. |
| <a id="repositoryblobrawsize"></a>`rawSize` | [`Int`](#int) | Size (in bytes) of the blob, or the blob target if stored externally. |
| <a id="repositoryblobrawtextblob"></a>`rawTextBlob` | [`String`](#string) | The raw content of the blob, if the blob is text data. |
| <a id="repositoryblobreplacepath"></a>`replacePath` | [`String`](#string) | Web path to replace the blob content. |
| <a id="repositoryblobrichviewer"></a>`richViewer` | [`BlobViewer`](#blobviewer) | Blob content rich viewer. |
| <a id="repositoryblobsimpleviewer"></a>`simpleViewer` | [`BlobViewer!`](#blobviewer) | Blob content simple viewer. |
| <a id="repositoryblobsize"></a>`size` | [`Int`](#int) | Size (in bytes) of the blob. |
| <a id="repositoryblobstoredexternally"></a>`storedExternally` | [`Boolean`](#boolean) | Whether the blob's content is stored externally (for instance, in LFS). |
| <a id="repositoryblobwebpath"></a>`webPath` | [`String`](#string) | Web path of the blob. |
### `Requirement`
......@@ -11757,7 +11784,7 @@ Represents how the blob content should be displayed.
| <a id="snippetblobviewerloadasync"></a>`loadAsync` | [`Boolean!`](#boolean) | Shows whether the blob content is loaded asynchronously. |
| <a id="snippetblobviewerloadingpartialname"></a>`loadingPartialName` | [`String!`](#string) | Loading partial name. |
| <a id="snippetblobviewerrendererror"></a>`renderError` | [`String`](#string) | Error rendering the blob content. |
| <a id="snippetblobviewertoolarge"></a>`tooLarge` | [`Boolean!`](#boolean) | Shows whether the blob too large to be displayed. |
| <a id="snippetblobviewertoolarge"></a>`tooLarge` | [`Boolean!`](#boolean) | Shows whether the blob is too large to be displayed. |
| <a id="snippetblobviewertype"></a>`type` | [`BlobViewersType!`](#blobviewerstype) | Type of blob viewer. |
### `SnippetPermissions`
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['BlobViewer'] do
it 'has the correct fields' do
expected_fields = [:type, :load_async, :too_large, :collapsed,
:render_error, :file_type, :loading_partial_name]
expect(described_class).to have_graphql_fields(*expected_fields)
end
end
......@@ -5,5 +5,26 @@ require 'spec_helper'
RSpec.describe Types::Repository::BlobType do
specify { expect(described_class.graphql_name).to eq('RepositoryBlob') }
specify { expect(described_class).to have_graphql_fields(:id, :oid, :name, :path, :web_path, :lfs_oid, :mode) }
specify do
expect(described_class).to have_graphql_fields(
:id,
:oid,
:name,
:path,
:web_path,
:lfs_oid,
:mode,
:size,
:raw_size,
:raw_blob,
:raw_text_blob,
:file_type,
:edit_blob_path,
:stored_externally,
:raw_path,
:replace_path,
:simple_viewer,
:rich_viewer
)
end
end
......@@ -2,52 +2,59 @@
require 'spec_helper'
RSpec.describe BlobPresenter, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
let(:git_blob) do
Gitlab::Git::Blob.find(
repository,
'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
'files/ruby/regex.rb'
)
RSpec.describe BlobPresenter do
let_it_be(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:blob) { repository.blob_at('HEAD', 'files/ruby/regex.rb') }
subject(:presenter) { described_class.new(blob) }
describe '#web_url' do
it { expect(presenter.web_url).to eq("http://localhost/#{project.full_path}/-/blob/#{blob.commit_id}/#{blob.path}") }
end
let(:blob) { Blob.new(git_blob) }
describe '#web_path' do
it { expect(presenter.web_path).to eq("/#{project.full_path}/-/blob/#{blob.commit_id}/#{blob.path}") }
end
describe '.web_url' do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:blob) { Gitlab::Graphql::Representation::TreeEntry.new(repository.tree.blobs.first, repository) }
describe '#edit_blob_path' do
it { expect(presenter.edit_blob_path).to eq("/#{project.full_path}/-/edit/#{blob.commit_id}/#{blob.path}") }
end
subject { described_class.new(blob) }
describe '#raw_path' do
it { expect(presenter.raw_path).to eq("/#{project.full_path}/-/raw/#{blob.commit_id}/#{blob.path}") }
end
it { expect(subject.web_url).to eq("http://localhost/#{project.full_path}/-/blob/#{blob.commit_id}/#{blob.path}") }
describe '#replace_path' do
it { expect(presenter.replace_path).to eq("/#{project.full_path}/-/create/#{blob.commit_id}/#{blob.path}") }
end
describe '#web_path' do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:blob) { Gitlab::Graphql::Representation::TreeEntry.new(repository.tree.blobs.first, repository) }
context 'given a Gitlab::Graphql::Representation::TreeEntry' do
let(:blob) { Gitlab::Graphql::Representation::TreeEntry.new(super(), repository) }
subject { described_class.new(blob) }
describe '#web_url' do
it { expect(presenter.web_url).to eq("http://localhost/#{project.full_path}/-/blob/#{blob.commit_id}/#{blob.path}") }
end
it { expect(subject.web_path).to eq("/#{project.full_path}/-/blob/#{blob.commit_id}/#{blob.path}") }
describe '#web_path' do
it { expect(presenter.web_path).to eq("/#{project.full_path}/-/blob/#{blob.commit_id}/#{blob.path}") }
end
end
describe '#highlight' do
subject { described_class.new(blob) }
let(:git_blob) { blob.__getobj__ }
it 'returns highlighted content' do
expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: nil, language: nil)
subject.highlight
presenter.highlight
end
it 'returns plain content when :plain is true' do
expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: true, language: nil)
subject.highlight(plain: true)
presenter.highlight(plain: true)
end
context '"to" param is present' do
......@@ -60,7 +67,7 @@ RSpec.describe BlobPresenter, :seed_helper do
it 'returns limited highlighted content' do
expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', "line one\n", plain: nil, language: nil)
subject.highlight(to: 1)
presenter.highlight(to: 1)
end
end
......@@ -72,7 +79,7 @@ RSpec.describe BlobPresenter, :seed_helper do
it 'passes language to inner call' do
expect(Gitlab::Highlight).to receive(:highlight).with('files/ruby/regex.rb', git_blob.data, plain: nil, language: 'ruby')
subject.highlight
presenter.highlight
end
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment