Merge branch 'graphql-tree-last-commit' into 'master'

Added commit type to tree GraphQL type

See merge request gitlab-org/gitlab-ce!29412
This commit is contained in:
Lin Jen-Shin 2019-06-28 10:02:57 +00:00
commit 2321b337f1
12 changed files with 139 additions and 3 deletions

View file

@ -0,0 +1,30 @@
# frozen_string_literal: true
module Types
class CommitType < BaseObject
graphql_name 'Commit'
authorize :download_code
present_using CommitPresenter
field :id, type: GraphQL::ID_TYPE, null: false
field :sha, type: GraphQL::STRING_TYPE, null: false
field :title, type: GraphQL::STRING_TYPE, null: true
field :description, type: GraphQL::STRING_TYPE, null: true
field :message, type: GraphQL::STRING_TYPE, null: true
field :authored_date, type: Types::TimeType, null: true
field :web_url, type: GraphQL::STRING_TYPE, null: false
# models/commit lazy loads the author by email
field :author, type: Types::UserType, null: true
field :latest_pipeline,
type: Types::Ci::PipelineType,
null: true,
description: "Latest pipeline for this commit",
resolve: -> (obj, ctx, args) do
Gitlab::Graphql::Loaders::PipelineForShaLoader.new(obj.project, obj.sha).find_last
end
end
end

View file

@ -4,6 +4,11 @@ module Types
class TreeType < BaseObject class TreeType < BaseObject
graphql_name 'Tree' graphql_name 'Tree'
# Complexity 10 as it triggers a Gitaly call on each render
field :last_commit, Types::CommitType, null: true, complexity: 10, resolve: -> (tree, args, ctx) do
tree.repository.last_commit_for_path(tree.sha, tree.path)
end
field :trees, Types::Tree::TreeEntryType.connection_type, null: false, resolve: -> (obj, args, ctx) do field :trees, Types::Tree::TreeEntryType.connection_type, null: false, resolve: -> (obj, args, ctx) do
Gitlab::Graphql::Representation::TreeEntry.decorate(obj.trees, obj.repository) Gitlab::Graphql::Representation::TreeEntry.decorate(obj.trees, obj.repository)
end end

View file

@ -295,6 +295,11 @@ module Ci
end end
end end
def self.latest_for_shas(shas)
max_id_per_sha = for_sha(shas).group(:sha).select("max(id)")
where(id: max_id_per_sha)
end
def self.latest_successful_ids_per_project def self.latest_successful_ids_per_project
success.group(:project_id).select('max(id) as id') success.group(:project_id).select('max(id) as id')
end end

View file

@ -1,6 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
class CommitPresenter < Gitlab::View::Presenter::Simple class CommitPresenter < Gitlab::View::Presenter::Delegated
include GlobalID::Identification
presents :commit presents :commit
def status_for(ref) def status_for(ref)
@ -10,4 +12,8 @@ class CommitPresenter < Gitlab::View::Presenter::Simple
def any_pipelines? def any_pipelines?
can?(current_user, :read_pipeline, commit.project) && commit.pipelines.any? can?(current_user, :read_pipeline, commit.project) && commit.pipelines.any?
end end
def web_url
Gitlab::UrlBuilder.new(commit).url
end
end end

View file

@ -0,0 +1,5 @@
---
title: Added commit type to tree GraphQL response
merge_request: 29412
author:
type: added

View file

@ -0,0 +1,25 @@
# frozen_string_literal: true
module Gitlab
module Graphql
module Loaders
class PipelineForShaLoader
attr_accessor :project, :sha
def initialize(project, sha)
@project, @sha = project, sha
end
def find_last
BatchLoader.for(sha).batch(key: project) do |shas, loader, args|
pipelines = args[:key].ci_pipelines.latest_for_shas(shas)
pipelines.each do |pipeline|
loader.call(pipeline.sha, pipeline)
end
end
end
end
end
end
end

View file

@ -113,7 +113,7 @@ describe GitlabSchema do
end end
it "raises a meaningful error if a global id couldn't be generated" do it "raises a meaningful error if a global id couldn't be generated" do
expect { described_class.id_from_object(build(:commit)) } expect { described_class.id_from_object(build(:wiki_directory)) }
.to raise_error(RuntimeError, /include `GlobalID::Identification` into/i) .to raise_error(RuntimeError, /include `GlobalID::Identification` into/i)
end end
end end

View file

@ -0,0 +1,11 @@
# frozen_string_literal: true
require 'spec_helper'
describe GitlabSchema.types['Commit'] do
it { expect(described_class.graphql_name).to eq('Commit') }
it { expect(described_class).to require_graphql_authorizations(:download_code) }
it { expect(described_class).to have_graphql_fields(:id, :sha, :title, :description, :message, :authored_date, :author, :web_url, :latest_pipeline) }
end

View file

@ -5,5 +5,5 @@ require 'spec_helper'
describe Types::Tree::TreeType do describe Types::Tree::TreeType do
it { expect(described_class.graphql_name).to eq('Tree') } it { expect(described_class.graphql_name).to eq('Tree') }
it { expect(described_class).to have_graphql_fields(:trees, :submodules, :blobs) } it { expect(described_class).to have_graphql_fields(:trees, :submodules, :blobs, :last_commit) }
end end

View file

@ -0,0 +1,20 @@
require 'spec_helper'
describe Gitlab::Graphql::Loaders::PipelineForShaLoader do
include GraphqlHelpers
describe '#find_last' do
it 'batch-resolves latest pipeline' do
project = create(:project, :repository)
pipeline1 = create(:ci_pipeline, project: project, ref: project.default_branch, sha: project.commit.sha)
pipeline2 = create(:ci_pipeline, project: project, ref: project.default_branch, sha: project.commit.sha)
pipeline3 = create(:ci_pipeline, project: project, ref: 'improve/awesome', sha: project.commit('improve/awesome').sha)
result = batch(max_queries: 1) do
[pipeline1.sha, pipeline3.sha].map { |sha| described_class.new(project, sha).find_last }
end
expect(result).to contain_exactly(pipeline2, pipeline3)
end
end
end

View file

@ -1886,6 +1886,17 @@ describe Ci::Pipeline, :mailer do
end end
end end
describe '.latest_for_shas' do
let(:sha) { 'abc' }
it 'returns latest pipeline for sha' do
create(:ci_pipeline, sha: sha)
pipeline2 = create(:ci_pipeline, sha: sha)
expect(described_class.latest_for_shas(sha)).to contain_exactly(pipeline2)
end
end
describe '.latest_successful_ids_per_project' do describe '.latest_successful_ids_per_project' do
let(:projects) { create_list(:project, 2) } let(:projects) { create_list(:project, 2) }
let!(:pipeline1) { create(:ci_pipeline, :success, project: projects[0]) } let!(:pipeline1) { create(:ci_pipeline, :success, project: projects[0]) }

View file

@ -33,6 +33,12 @@ describe 'getting a tree in a project' do
expect(graphql_data['project']['repository']['tree']['submodules']['edges']).to eq([]) expect(graphql_data['project']['repository']['tree']['submodules']['edges']).to eq([])
expect(graphql_data['project']['repository']['tree']['blobs']['edges']).to eq([]) expect(graphql_data['project']['repository']['tree']['blobs']['edges']).to eq([])
end end
it 'returns null commit' do
post_graphql(query, current_user: current_user)
expect(graphql_data['project']['repository']['last_commit']).to be_nil
end
end end
context 'when ref does not exist' do context 'when ref does not exist' do
@ -45,6 +51,12 @@ describe 'getting a tree in a project' do
expect(graphql_data['project']['repository']['tree']['submodules']['edges']).to eq([]) expect(graphql_data['project']['repository']['tree']['submodules']['edges']).to eq([])
expect(graphql_data['project']['repository']['tree']['blobs']['edges']).to eq([]) expect(graphql_data['project']['repository']['tree']['blobs']['edges']).to eq([])
end end
it 'returns null commit' do
post_graphql(query, current_user: current_user)
expect(graphql_data['project']['repository']['last_commit']).to be_nil
end
end end
context 'when ref and path exist' do context 'when ref and path exist' do
@ -61,6 +73,12 @@ describe 'getting a tree in a project' do
expect(graphql_data['project']['repository']['tree']['blobs']['edges'].size).to be > 0 expect(graphql_data['project']['repository']['tree']['blobs']['edges'].size).to be > 0
expect(graphql_data['project']['repository']['tree']['submodules']['edges'].size).to be > 0 expect(graphql_data['project']['repository']['tree']['submodules']['edges'].size).to be > 0
end end
it 'returns tree latest commit' do
post_graphql(query, current_user: current_user)
expect(graphql_data['project']['repository']['tree']['lastCommit']).to be_present
end
end end
context 'when current user is nil' do context 'when current user is nil' do