From 161336f65a544b2ed7a68823e924f617f4ee62d8 Mon Sep 17 00:00:00 2001 From: dakotadux Date: Wed, 15 Oct 2025 17:25:49 -0600 Subject: [PATCH] Add Observability to projects nav section This will allow users to access Observability from a project. --- ee/spec/features/projects/navbar_spec.rb | 5 + .../lib/sidebars/menus_shared_examples.rb | 3 + lib/sidebars/projects/menus/monitor_menu.rb | 29 ++++++ .../super_sidebar_menus/monitor_menu.rb | 3 +- .../projects/menus/monitor_menu_spec.rb | 99 ++++++++++++++++++- .../super_sidebar_menus/monitor_menu_spec.rb | 3 +- 6 files changed, 139 insertions(+), 3 deletions(-) diff --git a/ee/spec/features/projects/navbar_spec.rb b/ee/spec/features/projects/navbar_spec.rb index 1d9a572d43ce5c..491dd902576f98 100644 --- a/ee/spec/features/projects/navbar_spec.rb +++ b/ee/spec/features/projects/navbar_spec.rb @@ -74,6 +74,11 @@ within: _('Plan'), new_sub_nav_item_name: _('Iterations') ) + insert_after_sub_nav_item( + _('Service Desk'), + within: _('Monitor'), + new_sub_nav_item_name: _('Observability') + ) visit project_path(project) end diff --git a/ee/spec/support/shared_examples/lib/sidebars/menus_shared_examples.rb b/ee/spec/support/shared_examples/lib/sidebars/menus_shared_examples.rb index 59fb39064e4146..ec2d2c5994b5bf 100644 --- a/ee/spec/support/shared_examples/lib/sidebars/menus_shared_examples.rb +++ b/ee/spec/support/shared_examples/lib/sidebars/menus_shared_examples.rb @@ -14,6 +14,9 @@ .select { |item| item.is_a?(::Sidebars::NilMenuItem) } .map(&:item_id) + # Observability won't show up unless the group is present + nil_items.delete(:observability) + expect(nil_items).to be_empty end end diff --git a/lib/sidebars/projects/menus/monitor_menu.rb b/lib/sidebars/projects/menus/monitor_menu.rb index 76e1865202026c..811f0c19f7b258 100644 --- a/lib/sidebars/projects/menus/monitor_menu.rb +++ b/lib/sidebars/projects/menus/monitor_menu.rb @@ -11,6 +11,7 @@ def configure_menu_items add_item(error_tracking_menu_item) add_item(alert_management_menu_item) add_item(incidents_menu_item) + add_item(observability_menu_item) true end @@ -48,6 +49,14 @@ def feature_enabled? context.project.feature_available?(:monitor, context.current_user) end + def observability_access? + Ability.allowed?(context.current_user, :read_observability_portal, context.project.group) + end + + def observability_feature_enabled? + ::Feature.enabled?(:observability_sass_features, context.project.group) + end + def error_tracking_menu_item should_hide_menu = Feature.enabled?(:hide_error_tracking_features, context.project) || !can?(context.current_user, :read_sentry_issue, context.project) @@ -92,6 +101,26 @@ def incidents_menu_item item_id: :incidents ) end + + def observability_menu_item + unless observability_access? && observability_feature_enabled? && context.project.group.present? + return ::Sidebars::NilMenuItem.new(item_id: :observability) + end + + link = if context.project.group.observability_group_o11y_setting&.persisted? + group_observability_path(context.project.group, 'services') + else + group_observability_setup_path(context.project.group) + end + + ::Sidebars::MenuItem.new( + title: _('Observability'), + link: link, + super_sidebar_parent: ::Sidebars::Projects::SuperSidebarMenus::MonitorMenu, + active_routes: { controller: :observability }, + item_id: :observability + ) + end end end end diff --git a/lib/sidebars/projects/super_sidebar_menus/monitor_menu.rb b/lib/sidebars/projects/super_sidebar_menus/monitor_menu.rb index 6cdfa7d9c37684..819d8cb5257221 100644 --- a/lib/sidebars/projects/super_sidebar_menus/monitor_menu.rb +++ b/lib/sidebars/projects/super_sidebar_menus/monitor_menu.rb @@ -25,7 +25,8 @@ def configure_menu_items :incidents, :on_call_schedules, :escalation_policies, - :service_desk + :service_desk, + :observability ].each { |id| add_item(::Sidebars::NilMenuItem.new(item_id: id)) } end end diff --git a/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb b/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb index 0d7cdb96a8368e..34f3aa6aed3d56 100644 --- a/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb +++ b/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb @@ -64,7 +64,7 @@ end context 'Menu items' do - subject { described_class.new(context).renderable_items.index { |e| e.item_id == item_id } } + subject { described_class.new(context).renderable_items.find { |e| e.item_id == item_id } } shared_examples 'access rights checks' do it { is_expected.not_to be_nil } @@ -105,5 +105,102 @@ it_behaves_like 'access rights checks' end + + describe 'Observability' do + include Rails.application.routes.url_helpers + + let(:item_id) { :observability } + let_it_be(:group) { create(:group) } + let_it_be(:project_with_group) { create(:project, group: group) } + + before do + allow(context).to receive(:project).and_return(project_with_group) + allow(Ability).to receive(:allowed?).with(user, instance_of(Symbol), instance_of(Project)).and_return(true) + allow(Ability).to receive(:allowed?).with(user, :read_observability_portal, group).and_return(true) + allow(Ability).to receive(:allowed?).with(nil, :read_observability_portal, group).and_return(false) + allow(Ability).to receive(:allowed?).with(nil, instance_of(Symbol), instance_of(Project)).and_return(false) + end + + shared_examples 'returns nil when conditions not met' do + it { is_expected.to be_nil } + end + + shared_examples 'returns observability menu item' do |link_type| + it 'returns menu item with correct properties' do + menu_item = subject + expected_link = case link_type + when :services + group_observability_path(group, 'services') + when :setup + group_observability_setup_path(group) + end + + expect(menu_item).not_to be_nil + expect(menu_item.link).to eq(expected_link) + expect(menu_item.title).to eq('Observability') + expect(menu_item.item_id).to eq(:observability) + expect(menu_item.super_sidebar_parent).to eq(::Sidebars::Projects::SuperSidebarMenus::MonitorMenu) + expect(menu_item.active_routes).to eq({ controller: :observability }) + end + end + + context 'when all conditions are met' do + before do + stub_feature_flags(observability_sass_features: group) + end + + it_behaves_like 'access rights checks' + + context 'with persisted observability setting' do + before do + allow(group).to receive(:observability_group_o11y_setting).and_return( + instance_double(Observability::GroupO11ySetting, persisted?: true) + ) + end + + it_behaves_like 'returns observability menu item', :services + end + + context 'with non-persisted observability setting' do + before do + allow(group).to receive(:observability_group_o11y_setting).and_return( + instance_double(Observability::GroupO11ySetting, persisted?: false) + ) + end + + it_behaves_like 'returns observability menu item', :setup + end + + context 'with nil observability setting' do + before do + allow(group).to receive(:observability_group_o11y_setting).and_return(nil) + end + + it_behaves_like 'returns observability menu item', :setup + end + end + + context 'when conditions are not met' do + using RSpec::Parameterized::TableSyntax + + where(:description, :feature_flag, :permission, :project_group) do + 'feature flag disabled' | false | true | ref(:group) + 'no permission' | ref(:group) | false | ref(:group) + 'no group' | true | true | nil + end + + with_them do + before do + stub_feature_flags(observability_sass_features: feature_flag) + allow(Ability).to receive(:allowed?).with(user, :read_observability_portal, + project_group).and_return(permission) + allow(Ability).to receive(:allowed?).with(user, instance_of(Symbol), project_with_group).and_return(true) + allow(context).to receive(:project).and_return(project_group ? project_with_group : create(:project)) + end + + it_behaves_like 'returns nil when conditions not met' + end + end + end end end diff --git a/spec/lib/sidebars/projects/super_sidebar_menus/monitor_menu_spec.rb b/spec/lib/sidebars/projects/super_sidebar_menus/monitor_menu_spec.rb index 5cf9353363994b..69303a6a55302b 100644 --- a/spec/lib/sidebars/projects/super_sidebar_menus/monitor_menu_spec.rb +++ b/spec/lib/sidebars/projects/super_sidebar_menus/monitor_menu_spec.rb @@ -23,7 +23,8 @@ :incidents, :on_call_schedules, :escalation_policies, - :service_desk + :service_desk, + :observability ]) end end -- GitLab