Add metadata about the GitLab server to GraphQL

parent 42d3117f
# 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
# 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
# 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 ...@@ -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
...@@ -68,6 +68,10 @@ class GlobalPolicy < BasePolicy ...@@ -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
......
---
title: Add metadata about the GitLab server to GraphQL
merge_request: 24636
author:
type: added
# 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
...@@ -2,13 +2,29 @@ ...@@ -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
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
require 'spec_helper'
describe GitlabSchema.types['Metadata'] do
it { expect(described_class.graphql_name).to eq('Metadata') }
end
...@@ -5,7 +5,7 @@ describe GitlabSchema.types['Query'] do ...@@ -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 ...@@ -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
...@@ -181,6 +181,18 @@ describe GlobalPolicy do ...@@ -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) }
......
# 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
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 ...@@ -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
...@@ -77,8 +77,9 @@ module GraphqlHelpers ...@@ -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
......
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