Commit d7b64118 authored by Jose Ivan Vargas's avatar Jose Ivan Vargas Committed by Mikołaj Wawrzyniak

Add namespace to the ci_minutes_usage resolver

parent 0ecee668
......@@ -73,7 +73,7 @@ Returns [`CiConfig`](#ciconfig).
### `Query.ciMinutesUsage`
Monthly CI minutes usage data for the current user.
CI/CD minutes usage data for a namespace.
Returns [`CiMinutesNamespaceMonthlyUsageConnection`](#ciminutesnamespacemonthlyusageconnection).
......@@ -81,6 +81,12 @@ This field returns a [connection](#connections). It accepts the
four standard [pagination arguments](#connection-pagination-arguments):
`before: String`, `after: String`, `first: Int`, `last: Int`.
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="queryciminutesusagenamespaceid"></a>`namespaceId` | [`NamespaceID`](#namespaceid) | Global ID of the Namespace for the monthly CI/CD minutes usage. |
### `Query.containerRepository`
Find a container repository.
......@@ -4,7 +4,6 @@ module EE
module Types
module QueryType
extend ActiveSupport::Concern
prepended do
field :iteration, ::Types::IterationType,
null: true,
......@@ -70,7 +69,12 @@ module EE
field :ci_minutes_usage, ::Types::Ci::Minutes::NamespaceMonthlyUsageType.connection_type,
null: true,
description: 'Monthly CI minutes usage data for the current user.'
description: 'CI/CD minutes usage data for a namespace.' do
argument :namespace_id,
::Types::GlobalIDType[::Namespace],
required: false,
description: 'Global ID of the Namespace for the monthly CI/CD minutes usage.'
end
end
def vulnerability(id:)
......@@ -87,8 +91,20 @@ module EE
::GitlabSchema.find_by_gid(id)
end
def ci_minutes_usage
::Ci::Minutes::NamespaceMonthlyUsage.for_namespace(current_user.namespace)
def ci_minutes_usage(namespace_id: nil)
root_namespace = find_root_namespace(namespace_id)
::Ci::Minutes::NamespaceMonthlyUsage.for_namespace(root_namespace)
end
private
def find_root_namespace(namespace_id)
return current_user&.namespace unless namespace_id
namespace = ::Gitlab::Graphql::Lazy.force(::GitlabSchema.find_by_gid(namespace_id))
return unless namespace&.root?
namespace
end
end
end
......
......@@ -7,6 +7,7 @@ module Types
# this type only exposes data related to the current user
class NamespaceMonthlyUsageType < BaseObject
graphql_name 'CiMinutesNamespaceMonthlyUsage'
authorize :read_usage
field :month, ::GraphQL::Types::String, null: true,
description: 'Month related to the usage data.'
......
# frozen_string_literal: true
module Ci
module Minutes
class NamespaceMonthlyUsagePolicy < BasePolicy
delegate { @subject.namespace }
rule { can?(:owner_access) }.enable :read_usage
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::Minutes::NamespaceMonthlyUsagePolicy do
let(:group) { create(:group, :private, name: 'test') }
let(:current_user) { create(:user) }
let(:namespace_monthly_usage) do
create(:ci_namespace_monthly_usage, namespace: group)
end
subject(:policy) do
described_class.new(current_user, namespace_monthly_usage)
end
context 'with an owner' do
before do
group.add_owner(current_user)
end
it { is_expected.to be_allowed(:read_usage) }
end
context 'with a developer' do
before do
group.add_developer(current_user)
end
it { is_expected.not_to be_allowed(:read_usage) }
end
context "with a user's namespace" do
let(:namespace_monthly_usage) do
create(:ci_namespace_monthly_usage, namespace: current_user.namespace)
end
it { is_expected.to be_allowed(:read_usage) }
end
context 'with a different namespace' do
let(:namespace_monthly_usage) do
create(:ci_namespace_monthly_usage, namespace: create(:user).namespace)
end
it { is_expected.not_to be_allowed(:read_usage) }
end
end
......@@ -6,70 +6,130 @@ RSpec.describe 'Query.ciMinutesUsage' do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, name: 'Project 1', namespace: user.namespace) }
let_it_be(:user_project) { create(:project, name: 'Project 1', namespace: user.namespace) }
let_it_be_with_refind(:group) { create(:group, :public, name: 'test') }
before(:all) do
create(:ci_namespace_monthly_usage, namespace: user.namespace, amount_used: 50, date: Date.new(2021, 5, 1))
create(:ci_project_monthly_usage, project: project, amount_used: 50, date: Date.new(2021, 5, 1))
create(:ci_project_monthly_usage, project: user_project, amount_used: 40, date: Date.new(2021, 5, 1))
create(:ci_namespace_monthly_usage, namespace: group, amount_used: 100, date: Date.new(2021, 6, 1))
end
it 'returns usage data by month for the current user' do
query = <<-QUERY
{
ciMinutesUsage {
nodes {
minutes
month
projects {
nodes {
name
minutes
subject(:result) { post_graphql(query, current_user: user) }
context 'when no namespace_id is provided' do
let(:query) do
<<-QUERY
{
ciMinutesUsage {
nodes {
minutes
month
projects {
nodes {
name
minutes
}
}
}
}
}
}
QUERY
post_graphql(query, current_user: user)
monthly_usage = graphql_data_at(:ci_minutes_usage, :nodes)
expect(monthly_usage).to contain_exactly({
'month' => 'May',
'minutes' => 50,
'projects' => { 'nodes' => [{
'name' => 'Project 1',
'minutes' => 50
}] }
})
QUERY
end
it 'returns usage data by month for the current user' do
subject
monthly_usage = graphql_data_at(:ci_minutes_usage, :nodes)
expect(monthly_usage).to contain_exactly({
'month' => 'May',
'minutes' => 50,
'projects' => { 'nodes' => [{
'name' => 'Project 1',
'minutes' => 40
}] }
})
end
it 'does not create N+1 queries' do
control_count = ActiveRecord::QueryRecorder.new do
post_graphql(query, current_user: user)
end
expect(graphql_errors).to be_nil
project_2 = create(:project, name: 'Project 2', namespace: user.namespace)
create(:ci_project_monthly_usage, project: project_2, amount_used: 50, date: Date.new(2021, 5, 1))
expect do
post_graphql(query, current_user: user)
end.not_to exceed_query_limit(control_count)
expect(graphql_errors).to be_nil
end
end
it 'does not create N+1 queries' do
query = <<-QUERY
{
ciMinutesUsage {
nodes {
projects {
nodes {
name
}
context 'when namespace_id is provided' do
let(:namespace) { group }
let(:query) do
<<-QUERY
{
ciMinutesUsage(namespaceId: "#{namespace.to_global_id}") {
nodes {
minutes
month
}
}
}
}
QUERY
QUERY
end
context 'when group is root' do
context 'when user is an owner' do
before do
group.add_owner(user)
end
it 'returns the usage data' do
subject
monthly_usage = graphql_data_at(:ci_minutes_usage, :nodes)
expect(monthly_usage).to contain_exactly({
'month' => 'June',
'minutes' => 100
})
end
end
control_count = ActiveRecord::QueryRecorder.new do
post_graphql(query, current_user: user)
context 'when user is not an owner' do
before do
group.add_developer(user)
end
it 'does not return usage data' do
subject
monthly_usage = graphql_data_at(:ci_minutes_usage, :nodes)
expect(monthly_usage).to be_empty
end
end
end
expect(graphql_errors).to be_nil
project_2 = create(:project, name: 'Project 2', namespace: user.namespace)
create(:ci_project_monthly_usage, project: project_2, amount_used: 50, date: Date.new(2021, 5, 1))
context 'when group is a subgroup' do
let(:subgroup) { create(:group, :public, parent: group) }
let(:namespace) { subgroup }
before do
create(:ci_namespace_monthly_usage, namespace: subgroup)
subgroup.add_owner(user)
end
expect do
post_graphql(query, current_user: user)
end.not_to exceed_query_limit(control_count)
expect(graphql_errors).to be_nil
it 'does not return usage data' do
subject
monthly_usage = graphql_data_at(:ci_minutes_usage, :nodes)
expect(monthly_usage).to be_empty
end
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