Add metadata about the GitLab server to GraphQL

This commit is contained in:
Nick Thomas 2019-01-24 16:23:57 +00:00
parent 42d3117f9c
commit 21779d0018
No known key found for this signature in database
GPG key ID: 2A313A47AFADACE9
14 changed files with 171 additions and 5 deletions

View file

@ -0,0 +1,11 @@
# frozen_string_literal: true
module Resolvers
class MetadataResolver < BaseResolver
type Types::MetadataType, null: false
def resolve(**args)
{ version: Gitlab::VERSION, revision: Gitlab.revision }
end
end
end

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
module Types
class MetadataType < ::Types::BaseObject
graphql_name 'Metadata'
field :version, GraphQL::STRING_TYPE, null: false
field :revision, GraphQL::STRING_TYPE, null: false
end
end

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
module Types module Types
class QueryType < BaseObject class QueryType < ::Types::BaseObject
graphql_name 'Query' graphql_name 'Query'
field :project, Types::ProjectType, field :project, Types::ProjectType,
@ -10,6 +10,14 @@ module Types
description: "Find a project", description: "Find a project",
authorize: :read_project authorize: :read_project
field :metadata, Types::MetadataType,
null: true,
resolver: Resolvers::MetadataResolver,
description: 'Metadata about GitLab' do |*args|
authorize :read_instance_metadata
end
field :echo, GraphQL::STRING_TYPE, null: false, function: Functions::Echo.new field :echo, GraphQL::STRING_TYPE, null: false, function: Functions::Echo.new
end end
end end

View file

@ -68,6 +68,10 @@ class GlobalPolicy < BasePolicy
enable :read_users_list enable :read_users_list
end end
rule { ~anonymous }.policy do
enable :read_instance_metadata
end
rule { admin }.policy do rule { admin }.policy do
enable :read_custom_attribute enable :read_custom_attribute
enable :update_custom_attribute enable :update_custom_attribute

View file

@ -0,0 +1,5 @@
---
title: Add metadata about the GitLab server to GraphQL
merge_request: 24636
author:
type: added

View file

@ -0,0 +1,22 @@
# frozen_string_literal: true
module API
module Helpers
# GraphqlHelpers is used by the REST API when it is acting like a client
# against the graphql API. Helper code for the graphql server implementation
# should be in app/graphql/ or lib/gitlab/graphql/
module GraphqlHelpers
def conditionally_graphql!(fallback:, query:, context: {}, transform: nil)
return fallback.call unless Feature.enabled?(:graphql)
result = GitlabSchema.execute(query, context: context)
if transform
transform.call(result)
else
result
end
end
end
end
end

View file

@ -2,13 +2,29 @@
module API module API
class Version < Grape::API class Version < Grape::API
helpers ::API::Helpers::GraphqlHelpers
before { authenticate! } before { authenticate! }
METADATA_QUERY = <<~EOF
{
metadata {
version
revision
}
}
EOF
desc 'Get the version information of the GitLab instance.' do desc 'Get the version information of the GitLab instance.' do
detail 'This feature was introduced in GitLab 8.13.' detail 'This feature was introduced in GitLab 8.13.'
end end
get '/version' do get '/version' do
{ version: Gitlab::VERSION, revision: Gitlab.revision } conditionally_graphql!(
query: METADATA_QUERY,
context: { current_user: current_user },
transform: ->(result) { result.dig('data', 'metadata') },
fallback: -> { { version: Gitlab::VERSION, revision: Gitlab.revision } }
)
end end
end end
end end

View file

@ -0,0 +1,11 @@
require 'spec_helper'
describe Resolvers::MetadataResolver do
include GraphqlHelpers
describe '#resolve' do
it 'returns version and revision' do
expect(resolve(described_class)).to eq(version: Gitlab::VERSION, revision: Gitlab.revision)
end
end
end

View file

@ -0,0 +1,5 @@
require 'spec_helper'
describe GitlabSchema.types['Metadata'] do
it { expect(described_class.graphql_name).to eq('Metadata') }
end

View file

@ -5,7 +5,7 @@ describe GitlabSchema.types['Query'] do
expect(described_class.graphql_name).to eq('Query') expect(described_class.graphql_name).to eq('Query')
end end
it { is_expected.to have_graphql_fields(:project, :echo) } it { is_expected.to have_graphql_fields(:project, :echo, :metadata) }
describe 'project field' do describe 'project field' do
subject { described_class.fields['project'] } subject { described_class.fields['project'] }
@ -20,4 +20,17 @@ describe GitlabSchema.types['Query'] do
is_expected.to require_graphql_authorizations(:read_project) is_expected.to require_graphql_authorizations(:read_project)
end end
end end
describe 'metadata field' do
subject { described_class.fields['metadata'] }
it 'returns metadata' do
is_expected.to have_graphql_type(Types::MetadataType)
is_expected.to have_graphql_resolver(Resolvers::MetadataResolver)
end
it 'authorizes with log_in' do
is_expected.to require_graphql_authorizations(:read_instance_metadata)
end
end
end end

View file

@ -181,6 +181,18 @@ describe GlobalPolicy do
end end
end end
describe 'read instance metadata' do
context 'regular user' do
it { is_expected.to be_allowed(:read_instance_metadata) }
end
context 'anonymous' do
let(:current_user) { nil }
it { is_expected.not_to be_allowed(:read_instance_metadata) }
end
end
describe 'read instance statistics' do describe 'read instance statistics' do
context 'regular user' do context 'regular user' do
it { is_expected.to be_allowed(:read_instance_statistics) } it { is_expected.to be_allowed(:read_instance_statistics) }

View file

@ -0,0 +1,32 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'getting project information' do
include GraphqlHelpers
let(:query) { graphql_query_for('metadata', {}, all_graphql_fields_for('Metadata')) }
context 'logged in' do
it 'returns version and revision' do
post_graphql(query, current_user: create(:user))
expect(graphql_errors).to be_nil
expect(graphql_data).to eq(
'metadata' => {
'version' => Gitlab::VERSION,
'revision' => Gitlab.revision
}
)
end
end
context 'anonymous user' do
it 'returns nothing' do
post_graphql(query, current_user: nil)
expect(graphql_errors).to be_nil
expect(graphql_data).to eq('metadata' => nil)
end
end
end

View file

@ -1,7 +1,7 @@
require 'spec_helper' require 'spec_helper'
describe API::Version do describe API::Version do
describe 'GET /version' do shared_examples_for 'GET /version' do
context 'when unauthenticated' do context 'when unauthenticated' do
it 'returns authentication error' do it 'returns authentication error' do
get api('/version') get api('/version')
@ -22,4 +22,20 @@ describe API::Version do
end end
end end
end end
context 'with graphql enabled' do
before do
stub_feature_flags(graphql: true)
end
include_examples 'GET /version'
end
context 'with graphql disabled' do
before do
stub_feature_flags(graphql: false)
end
include_examples 'GET /version'
end
end end

View file

@ -77,8 +77,9 @@ module GraphqlHelpers
def query_graphql_field(name, attributes = {}, fields = nil) def query_graphql_field(name, attributes = {}, fields = nil)
fields ||= all_graphql_fields_for(name.classify) fields ||= all_graphql_fields_for(name.classify)
attributes = attributes_to_graphql(attributes) attributes = attributes_to_graphql(attributes)
attributes = "(#{attributes})" if attributes.present?
<<~QUERY <<~QUERY
#{name}(#{attributes}) { #{name}#{attributes} {
#{fields} #{fields}
} }
QUERY QUERY