Commit ec167d69 authored by Markus Koller's avatar Markus Koller

Merge branch '323195-make-blob-info-available-through-graphql' into 'master'

Make blobs directly accessible through the graphql repository

See merge request gitlab-org/gitlab!58677
parents 3b4398ba 4bb609f3
# frozen_string_literal: true
module Resolvers
class BlobsResolver < BaseResolver
include Gitlab::Graphql::Authorize::AuthorizeResource
type Types::Tree::BlobType.connection_type, null: true
authorize :download_code
calls_gitaly!
alias_method :repository, :object
argument :paths, [GraphQL::STRING_TYPE],
required: true,
description: 'Array of desired blob paths.'
argument :ref, GraphQL::STRING_TYPE,
required: false,
default_value: nil,
description: 'The commit ref to get the blobs from. Default value is HEAD.'
# We fetch blobs from Gitaly efficiently but it still scales O(N) with the
# number of paths being fetched, so apply a scaling limit to that.
def self.resolver_complexity(args, child_complexity:)
super + args.fetch(:paths, []).size
end
def resolve(paths:, ref:)
authorize!(repository.container)
return [] if repository.empty?
ref ||= repository.root_ref
repository.blobs_at(paths.map { |path| [ref, path] })
end
end
end
...@@ -14,5 +14,7 @@ module Types ...@@ -14,5 +14,7 @@ module Types
description: 'Indicates a corresponding Git repository exists on disk.' description: 'Indicates a corresponding Git repository exists on disk.'
field :tree, Types::Tree::TreeType, null: true, resolver: Resolvers::TreeResolver, calls_gitaly: true, field :tree, Types::Tree::TreeType, null: true, resolver: Resolvers::TreeResolver, calls_gitaly: true,
description: 'Tree of the repository.' description: 'Tree of the repository.'
field :blobs, Types::Tree::BlobType.connection_type, null: true, resolver: Resolvers::BlobsResolver, calls_gitaly: true,
description: 'Blobs contained within the repository'
end end
end end
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
# Blob is a Rails-specific wrapper around Gitlab::Git::Blob, SnippetBlob and Ci::ArtifactBlob # Blob is a Rails-specific wrapper around Gitlab::Git::Blob, SnippetBlob and Ci::ArtifactBlob
class Blob < SimpleDelegator class Blob < SimpleDelegator
include GlobalID::Identification
include Presentable include Presentable
include BlobLanguageFromGitAttributes include BlobLanguageFromGitAttributes
include BlobActiveModel include BlobActiveModel
......
---
title: Make blobs directly accessible through the graphql repository
merge_request: 58677
author:
type: added
...@@ -5443,6 +5443,7 @@ Autogenerated return type of RepositionImageDiffNote. ...@@ -5443,6 +5443,7 @@ Autogenerated return type of RepositionImageDiffNote.
| Field | Type | Description | | Field | Type | Description |
| ----- | ---- | ----------- | | ----- | ---- | ----------- |
| `blobs` | [`BlobConnection`](#blobconnection) | Blobs contained within the repository. |
| `empty` | [`Boolean!`](#boolean) | Indicates repository has no visible content. | | `empty` | [`Boolean!`](#boolean) | Indicates repository has no visible content. |
| `exists` | [`Boolean!`](#boolean) | Indicates a corresponding Git repository exists on disk. | | `exists` | [`Boolean!`](#boolean) | Indicates a corresponding Git repository exists on disk. |
| `rootRef` | [`String`](#string) | Default branch of the repository. | | `rootRef` | [`String`](#string) | Default branch of the repository. |
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Resolvers::BlobsResolver do
include GraphqlHelpers
describe '.resolver_complexity' do
it 'adds one per path being resolved' do
control = described_class.resolver_complexity({}, child_complexity: 1)
expect(described_class.resolver_complexity({ paths: %w[a b c] }, child_complexity: 1))
.to eq(control + 3)
end
end
describe '#resolve' do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:args) { { paths: paths, ref: ref } }
let(:paths) { [] }
let(:ref) { nil }
subject(:resolve_blobs) { resolve(described_class, obj: repository, args: args, ctx: { current_user: user }) }
context 'when unauthorized' do
it 'raises an exception' do
expect { resolve_blobs }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when authorized' do
before do
project.add_developer(user)
end
context 'using no filter' do
it 'returns nothing' do
is_expected.to be_empty
end
end
context 'using paths filter' do
let(:paths) { ['README.md'] }
it 'returns the specified blobs for HEAD' do
is_expected.to contain_exactly(have_attributes(path: 'README.md'))
end
context 'specifying a non-existent blob' do
let(:paths) { ['non-existent'] }
it 'returns nothing' do
is_expected.to be_empty
end
end
context 'specifying a different ref' do
let(:ref) { 'add-pdf-file' }
let(:paths) { ['files/pdf/test.pdf', 'README.md'] }
it 'returns the specified blobs for that ref' do
is_expected.to contain_exactly(
have_attributes(path: 'files/pdf/test.pdf'),
have_attributes(path: 'README.md')
)
end
end
end
end
end
end
...@@ -12,4 +12,6 @@ RSpec.describe GitlabSchema.types['Repository'] do ...@@ -12,4 +12,6 @@ RSpec.describe GitlabSchema.types['Repository'] do
specify { expect(described_class).to have_graphql_field(:tree) } specify { expect(described_class).to have_graphql_field(:tree) }
specify { expect(described_class).to have_graphql_field(:exists, calls_gitaly?: true, complexity: 2) } specify { expect(described_class).to have_graphql_field(:exists, calls_gitaly?: true, complexity: 2) }
specify { expect(described_class).to have_graphql_field(:blobs) }
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