Commit da5a9dc9 authored by Mikolaj Wawrzyniak's avatar Mikolaj Wawrzyniak

Add annotation services

To allow managing metrics dashboard
annotations entries we need to provide
two services, for create and delete actions
parent 8bf5081a
# frozen_string_literal: true
# Create Metrics::Dashboard::Annotation entry based on matched dashboard_path, environment, cluster
module Metrics
module Dashboard
module Annotations
class CreateService < ::BaseService
include Stepable
steps :authorize_environment_access,
:authorize_cluster_access,
:parse_dashboard_path,
:create
def initialize(user, params)
@user, @params = user, params
end
def execute
execute_steps
end
private
attr_reader :user, :params
def authorize_environment_access(options)
if environment.nil? || Ability.allowed?(user, :create_metrics_dashboard_annotation, project)
options[:environment] = environment
success(options)
else
error(s_('Metrics::Dashboard::Annotation|You are not authorized to create annotation for selected environment'))
end
end
def authorize_cluster_access(options)
if cluster.nil? || Ability.allowed?(user, :create_metrics_dashboard_annotation, cluster)
options[:cluster] = cluster
success(options)
else
error(s_('Metrics::Dashboard::Annotation|You are not authorized to create annotation for selected cluster'))
end
end
def parse_dashboard_path(options)
dashboard_path = params[:dashboard_path]
Gitlab::Metrics::Dashboard::Finder.find_raw(project, dashboard_path: dashboard_path)
options[:dashboard_path] = dashboard_path
success(options)
rescue Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError
error(s_('Metrics::Dashboard::Annotation|Dashboard with requested path can not be found'))
end
def create(options)
annotation = Annotation.new(options.slice(:environment, :cluster, :dashboard_path).merge(params.slice(:description, :starting_at, :ending_at)))
if annotation.save
success(annotation: annotation)
else
error(annotation.errors)
end
end
def environment
params[:environment]
end
def cluster
params[:cluster]
end
def project
(environment || cluster)&.project
end
end
end
end
end
# frozen_string_literal: true
# Delete Metrics::Dashboard::Annotation entry
module Metrics
module Dashboard
module Annotations
class DeleteService < ::BaseService
include Stepable
steps :authorize_action,
:delete
def initialize(user, annotation)
@user, @annotation = user, annotation
end
def execute
execute_steps
end
private
attr_reader :user, :annotation
def authorize_action(_options)
if Ability.allowed?(user, :delete_metrics_dashboard_annotation, annotation)
success
else
error(s_('Metrics::Dashboard::Annotation|You are not authorized to delete this annotation'))
end
end
def delete(_options)
if annotation.destroy
success
else
error(s_('Metrics::Dashboard::Annotation|Annotation has not been deleted'))
end
end
end
end
end
end
......@@ -12758,9 +12758,24 @@ msgstr ""
msgid "Metrics::Dashboard::Annotation|Annotation can't belong to both a cluster and an environment at the same time"
msgstr ""
msgid "Metrics::Dashboard::Annotation|Annotation has not been deleted"
msgstr ""
msgid "Metrics::Dashboard::Annotation|Annotation must belong to a cluster or an environment"
msgstr ""
msgid "Metrics::Dashboard::Annotation|Dashboard with requested path can not be found"
msgstr ""
msgid "Metrics::Dashboard::Annotation|You are not authorized to create annotation for selected cluster"
msgstr ""
msgid "Metrics::Dashboard::Annotation|You are not authorized to create annotation for selected environment"
msgstr ""
msgid "Metrics::Dashboard::Annotation|You are not authorized to delete this annotation"
msgstr ""
msgid "Metrics|Add metric"
msgstr ""
......
# frozen_string_literal: true
require 'spec_helper'
describe Metrics::Dashboard::Annotations::CreateService do
let_it_be(:user) { create(:user) }
let(:description) { 'test annotation' }
let(:dashboard_path) { 'config/prometheus/common_metrics.yml' }
let(:starting_at) { 15.minutes.ago }
let(:ending_at) { nil }
let(:service_instance) { described_class.new(user, annotation_params) }
let(:annotation_params) do
{
environment: environment,
cluster: cluster,
description: description,
dashboard_path: dashboard_path,
starting_at: starting_at,
ending_at: ending_at
}
end
shared_examples 'executed annotation creation' do
it 'returns success response', :aggregate_failures do
annotation = instance_double(::Metrics::Dashboard::Annotation)
allow(::Metrics::Dashboard::Annotation).to receive(:new).and_return(annotation)
allow(annotation).to receive(:save).and_return(true)
response = service_instance.execute
expect(response[:status]).to be :success
expect(response[:annotation]).to be annotation
end
it 'creates annotation', :aggregate_failures do
annotation = instance_double(::Metrics::Dashboard::Annotation)
expect(::Metrics::Dashboard::Annotation)
.to receive(:new).with(annotation_params).and_return(annotation)
expect(annotation).to receive(:save).and_return(true)
service_instance.execute
end
end
shared_examples 'prevented annotation creation' do |message|
it 'returns error response', :aggregate_failures do
response = service_instance.execute
expect(response[:status]).to be :error
expect(response[:message]).to eql message
end
it 'does not change db state' do
expect(::Metrics::Dashboard::Annotation).not_to receive(:new)
service_instance.execute
end
end
shared_examples 'annotation creation failure' do
it 'returns error response', :aggregate_failures do
annotation = instance_double(::Metrics::Dashboard::Annotation)
expect(annotation).to receive(:errors).and_return('Model validation error')
expect(::Metrics::Dashboard::Annotation)
.to receive(:new).with(annotation_params).and_return(annotation)
expect(annotation).to receive(:save).and_return(false)
response = service_instance.execute
expect(response[:status]).to be :error
expect(response[:message]).to eql 'Model validation error'
end
end
describe '.execute' do
context 'with environment' do
let(:environment) { create(:environment) }
let(:cluster) { nil }
context 'with anonymous user' do
it_behaves_like 'prevented annotation creation', 'You are not authorized to create annotation for selected environment'
end
context 'with maintainer user' do
before do
environment.project.add_maintainer(user)
end
it_behaves_like 'executed annotation creation'
end
end
context 'with cluster' do
let(:environment) { nil }
context 'with anonymous user' do
let(:cluster) { create(:cluster, :project) }
it_behaves_like 'prevented annotation creation', 'You are not authorized to create annotation for selected cluster'
end
context 'with maintainer user' do
let(:cluster) { create(:cluster, :project) }
before do
cluster.project.add_maintainer(user)
end
it_behaves_like 'executed annotation creation'
end
context 'with owner user' do
let(:cluster) { create(:cluster, :group) }
before do
cluster.group.add_owner(user)
end
it_behaves_like 'executed annotation creation'
end
end
context 'non cluster nor environment is supplied' do
let(:environment) { nil }
let(:cluster) { nil }
it_behaves_like 'annotation creation failure'
end
context 'missing dashboard_path' do
let(:cluster) { create(:cluster, :project) }
let(:environment) { nil }
let(:dashboard_path) { nil }
context 'with maintainer user' do
before do
cluster.project.add_maintainer(user)
end
it_behaves_like 'annotation creation failure'
end
end
context 'incorrect dashboard_path' do
let(:cluster) { create(:cluster, :project) }
let(:environment) { nil }
let(:dashboard_path) { 'something_incorrect.yml' }
context 'with maintainer user' do
before do
cluster.project.add_maintainer(user)
end
it_behaves_like 'prevented annotation creation', 'Dashboard with requested path can not be found'
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Metrics::Dashboard::Annotations::DeleteService do
let(:user) { create(:user) }
let(:service_instance) { described_class.new(user, annotation) }
shared_examples 'executed annotation deletion' do
it 'returns success response', :aggregate_failures do
expect(annotation).to receive(:destroy).and_return(true)
response = service_instance.execute
expect(response[:status]).to be :success
end
end
shared_examples 'prevented annotation deletion' do |message|
it 'returns error response', :aggregate_failures do
response = service_instance.execute
expect(response[:status]).to be :error
expect(response[:message]).to eql message
end
it 'does not change db state' do
expect(annotation).not_to receive(:destroy)
service_instance.execute
end
end
describe '.execute' do
context 'with specific environment' do
let(:annotation) { create(:metrics_dashboard_annotation, environment: environment) }
let(:environment) { create(:environment) }
context 'with anonymous user' do
it_behaves_like 'prevented annotation deletion', 'You are not authorized to delete this annotation'
end
context 'with maintainer user' do
before do
environment.project.add_maintainer(user)
end
it_behaves_like 'executed annotation deletion'
context 'annotation failed to delete' do
it 'returns error response', :aggregate_failures do
allow(annotation).to receive(:destroy).and_return(false)
response = service_instance.execute
expect(response[:status]).to be :error
expect(response[:message]).to eql 'Annotation has not been deleted'
end
end
end
end
context 'with specific cluster' do
let(:annotation) { create(:metrics_dashboard_annotation, cluster: cluster, environment: nil) }
context 'with anonymous user' do
let(:cluster) { create(:cluster, :project) }
it_behaves_like 'prevented annotation deletion', 'You are not authorized to delete this annotation'
end
context 'with maintainer user' do
let(:cluster) { create(:cluster, :project) }
before do
cluster.project.add_maintainer(user)
end
it_behaves_like 'executed annotation deletion'
end
context 'with owner user' do
let(:cluster) { create(:cluster, :group) }
before do
cluster.group.add_owner(user)
end
it_behaves_like 'executed annotation deletion'
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