diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index 4930ef71e7d699e2b7a7a27ba5c9780a5cd14f49..a085840ee84aba8af357de6f04a2856d0190efd4 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -171,6 +171,6 @@ = f.gitlab_ui_checkbox_component :enabled_following, s_('Preferences|Enable follow users') = render_if_exists 'profiles/preferences/code_suggestions_settings', form: f - + = render_if_exists 'profiles/preferences/zoekt_settings', form: f #js-profile-preferences-app{ data: data_attributes } diff --git a/db/migrate/20230524142655_add_enabled_zoekt_to_user_preferences.rb b/db/migrate/20230524142655_add_enabled_zoekt_to_user_preferences.rb new file mode 100644 index 0000000000000000000000000000000000000000..b92f424d5e112b4d13a3156e80b3f22107bde5dd --- /dev/null +++ b/db/migrate/20230524142655_add_enabled_zoekt_to_user_preferences.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddEnabledZoektToUserPreferences < Gitlab::Database::Migration[2.1] + enable_lock_retries! + + def change + add_column :user_preferences, :enabled_zoekt, :boolean, null: false, default: true + end +end diff --git a/db/schema_migrations/20230524142655 b/db/schema_migrations/20230524142655 new file mode 100644 index 0000000000000000000000000000000000000000..35aa474f5dd9d7ed4d5ccad85e0282b41e34e9fa --- /dev/null +++ b/db/schema_migrations/20230524142655 @@ -0,0 +1 @@ +81f0d62ff5c06d5c34984dae613535d7ffbe9f0f3b25d2a694c5607e5e590805 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 4ad846f49af29cac60728f5a084228fc96180163..b3e087759e2d685d8a12624131e99f67e167ee08 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -23690,6 +23690,7 @@ CREATE TABLE user_preferences ( enabled_following boolean DEFAULT true NOT NULL, visibility_pipeline_id_type smallint DEFAULT 0 NOT NULL, project_shortcut_buttons boolean DEFAULT true NOT NULL, + enabled_zoekt boolean DEFAULT true NOT NULL, CONSTRAINT check_89bf269f41 CHECK ((char_length(diffs_deletion_color) <= 7)), CONSTRAINT check_d07ccd35f7 CHECK ((char_length(diffs_addition_color) <= 7)) ); diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md index 35f6f07f0be4b2de23bb45d9995f972fd5665101..20ca57a540bf0b8bf6f7c644146e84b24e1ce7d4 100644 --- a/doc/user/profile/index.md +++ b/doc/user/profile/index.md @@ -324,6 +324,20 @@ You can disable following and being followed by other users. NOTE: When this feature is being disabled, all current followed/following connections are deleted. +## Advanced code search with zoekt + +### Disable searching code with zoekt + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/388519) as a beta feature [with a flag](../feature_flags.md) named `search_code_with_zoekt`. Enabled by default. + +You can disable searching with Zoekt and use Elasticsearch instead. + +1. On the top bar, in the upper-right corner, select your avatar. +1. Select **Edit profile**. +1. Select **Preferences**. +1. Clear the **Enable advanced code search** checkbox. +1. Select **Save changes**. + ## View your activity GitLab tracks [user contribution activity](contributions_calendar.md). diff --git a/ee/app/controllers/ee/profiles/preferences_controller.rb b/ee/app/controllers/ee/profiles/preferences_controller.rb index 33a4572b5132a6a252114dd6bd2448f2af84fbe7..c8f25cbdf3c6b5d64806f5d99835da530e5c3c2e 100644 --- a/ee/app/controllers/ee/profiles/preferences_controller.rb +++ b/ee/app/controllers/ee/profiles/preferences_controller.rb @@ -12,6 +12,7 @@ def preferences_param_names_ee params_ee = [] params_ee.push(:group_view) if License.feature_available?(:security_dashboard) params_ee.push(:code_suggestions) if user.namespace.ai_assist_ui_enabled? + params_ee.push(:enabled_zoekt) if user.has_zoekt_indexed_namespace? params_ee end diff --git a/ee/app/models/ee/user.rb b/ee/app/models/ee/user.rb index f89eb12c73986dee22c76c675f76456a9e9968ab..ab152e6ccc6d459d1ea35660d304896ce08219ba 100644 --- a/ee/app/models/ee/user.rb +++ b/ee/app/models/ee/user.rb @@ -49,6 +49,9 @@ module User delegate :code_suggestions_enabled?, :code_suggestions, :code_suggestions=, to: :namespace + delegate :enabled_zoekt?, :enabled_zoekt, :enabled_zoekt=, + to: :user_preference + has_many :epics, foreign_key: :author_id has_many :test_reports, foreign_key: :author_id, inverse_of: :author, class_name: 'RequirementsManagement::TestReport' has_many :assigned_epics, foreign_key: :assignee_id, class_name: "Epic" @@ -322,6 +325,19 @@ def owns_group_without_trial? .any? end + # Returns true if the user is a Reporter or higher on any namespace + # that is associated as a Zoekt::IndexedNamespace + def has_zoekt_indexed_namespace? + zoekt_indexed_namespaces.any? + end + + def zoekt_indexed_namespaces + ::Zoekt::IndexedNamespace.where( + namespace: ::Namespace + .from("(#{namespace_union_for_reporter_developer_maintainer_owned}) #{::Namespace.table_name}") + ) + end + # Returns true if the user is a Reporter or higher on any namespace # currently on a paid plan def has_paid_namespace?(plans: ::Plan::PAID_HOSTED_PLANS, exclude_trials: false) diff --git a/ee/app/services/concerns/search/zoekt_searchable.rb b/ee/app/services/concerns/search/zoekt_searchable.rb index d1c0a90ffe62d9f7dfbf504a9845ad60bbd3218e..bab51ab12abbeec8f79147cbaa36b582c460f58e 100644 --- a/ee/app/services/concerns/search/zoekt_searchable.rb +++ b/ee/app/services/concerns/search/zoekt_searchable.rb @@ -4,9 +4,9 @@ module Search module ZoektSearchable def use_zoekt? return false if params[:basic_search] - return false if params[:advanced_search] return false unless ::Feature.enabled?(:search_code_with_zoekt, current_user) return false unless ::License.feature_available?(:zoekt_code_search) + return false unless current_user&.enabled_zoekt? scope == 'blobs' && zoekt_searchable_scope.respond_to?(:use_zoekt?) && diff --git a/ee/app/views/profiles/preferences/_zoekt_settings.html.haml b/ee/app/views/profiles/preferences/_zoekt_settings.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..b1c36152a7c148e694b197153b73556a480c0a69 --- /dev/null +++ b/ee/app/views/profiles/preferences/_zoekt_settings.html.haml @@ -0,0 +1,17 @@ +- return unless @user.has_zoekt_indexed_namespace? + +.row.js-preferences-form.js-search-settings-section + .col-sm-12 + %hr + .col-lg-4.profile-settings-sidebar + %h4.gl-mt-0 + = s_('Preferences|Enable Zoekt code search') + = gl_badge_tag _('beta'), variant: :info, size: :sm + %p + = s_('Preferences|Turns on or off the preference to search with Zoekt instead of Elasticsearch.') + = link_to _('Learn more'), help_page_path('user/profile/index', anchor: 'advanced-code-search-with-zoekt'), target: '_blank', rel: 'noopener noreferrer' + + .col-lg-8 + .form-group + = form.gitlab_ui_checkbox_component :enabled_zoekt, + s_('Preferences|Enable Zoekt code search') diff --git a/ee/spec/controllers/ee/profiles/preferences_controller_spec.rb b/ee/spec/controllers/ee/profiles/preferences_controller_spec.rb index b25b85d185f234c676645d759307b8f24229b165..b9d9a17e98f822009a224e87e17ff6ab4ad4bda9 100644 --- a/ee/spec/controllers/ee/profiles/preferences_controller_spec.rb +++ b/ee/spec/controllers/ee/profiles/preferences_controller_spec.rb @@ -10,6 +10,16 @@ end describe 'PATCH update' do + def go(params: {}, format: :json) + params.reverse_merge!( + color_scheme_id: '1', + dashboard: 'stars', + theme_id: '1' + ) + + patch :update, params: { user: params }, format: format + end + context 'when updating security dashboard feature' do subject { patch :update, params: { user: { group_view: group_view } }, format: :json } @@ -76,5 +86,37 @@ end end end + + context 'on zoekt indexed namespaces', feature_category: :global_search do + context 'when user is not a member of any zoekt indexed namespaces' do + before do + allow(::Zoekt::IndexedNamespace).to receive(:where).and_return([]) + end + + it 'does not update enabled_zoekt preference of user' do + prefs = { enabled_zoekt: false } + + go params: prefs + user.reload + + expect(user.enabled_zoekt).to eq(true) + end + end + + context 'when user is a member of a zoekt indexed namespace' do + before do + allow(::Zoekt::IndexedNamespace).to receive(:where).and_return([::Zoekt::IndexedNamespace.new]) + end + + it 'updates enabled_zoekt preference of user' do + prefs = { enabled_zoekt: false } + + go params: prefs + user.reload + + expect(user.enabled_zoekt).to eq(false) + end + end + end end end diff --git a/ee/spec/models/ee/user_spec.rb b/ee/spec/models/ee/user_spec.rb index 1eac0cc0664e600df64a6b0079e797f52ee72da9..29c5ce001aa115abed94314337de3406f74bff81 100644 --- a/ee/spec/models/ee/user_spec.rb +++ b/ee/spec/models/ee/user_spec.rb @@ -1438,6 +1438,36 @@ end end + context 'zoekt namespaces', feature_category: :global_search do + let_it_be(:indexed_parent_namespace) { create(:group) } + let_it_be(:unindexed_namespace) { create(:namespace) } + let_it_be(:shard) { Zoekt::Shard.create!(index_base_url: 'http://example.com:1234/', search_base_url: 'http://example.com:4567/') } + let_it_be(:zoekt_indexed_namespace) { Zoekt::IndexedNamespace.create!(shard: shard, namespace: indexed_parent_namespace) } + + let(:user) { create(:user, namespace: create(:user_namespace)) } + + describe '#zoekt_indexed_namespaces' do + it 'returns zoekt indexed namespaces for user' do + indexed_parent_namespace.add_maintainer(user) + expect(user.zoekt_indexed_namespaces).to match_array([zoekt_indexed_namespace]) + end + + it 'returns empty array if there are user is not have access of reporter or above' do + expect(user.zoekt_indexed_namespaces).to match_array([]) + end + end + + describe '#has_zoekt_indexed_namespace?' do + it 'returns true if there are zoekt_indexed_namespaces' do + allow(user).to receive(:zoekt_indexed_namespaces).and_return([zoekt_indexed_namespace]) + expect(user).to be_has_zoekt_indexed_namespace + + allow(user).to receive(:zoekt_indexed_namespaces).and_return([]) + expect(user).not_to be_has_zoekt_indexed_namespace + end + end + end + context 'paid namespaces', :saas do using RSpec::Parameterized::TableSyntax diff --git a/ee/spec/services/search/project_service_spec.rb b/ee/spec/services/search/project_service_spec.rb index 5be3b14d938c7eec1e906965c7584169fd8b7750..65f8230db77d25de8aea4ca776c1f851e6aec88c 100644 --- a/ee/spec/services/search/project_service_spec.rb +++ b/ee/spec/services/search/project_service_spec.rb @@ -78,12 +78,14 @@ end let(:use_zoekt) { true } + let(:user_preference_enabled_zoekt) { true } let(:scope) { 'blobs' } let(:basic_search) { nil } let(:advanced_search) { nil } before do allow(project).to receive(:use_zoekt?).and_return(use_zoekt) + allow(user).to receive(:enabled_zoekt?).and_return(user_preference_enabled_zoekt) end it 'searches with Zoekt' do @@ -119,11 +121,11 @@ end end - context 'when advanced search is requested' do - let(:advanced_search) { true } + context 'when user set enabled_zoekt preference to false' do + let(:user_preference_enabled_zoekt) { false } it 'does not search with Zoekt' do - expect(service.use_zoekt?).to eq(false) + expect(service).not_to be_use_zoekt expect(service.execute).not_to be_kind_of(::Gitlab::Zoekt::SearchResults) end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index deaf1e2fa2dec9169b6e9e312c5cd4b62c7c8469..15a19fc212d0f7ac1955bd4c8d0397524000a28b 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -34308,6 +34308,9 @@ msgstr "" msgid "Preferences|Enable Gitpod integration" msgstr "" +msgid "Preferences|Enable Zoekt code search" +msgstr "" + msgid "Preferences|Enable follow users" msgstr "" @@ -34383,6 +34386,9 @@ msgstr "" msgid "Preferences|Turns on or off the ability to follow or be followed by other users." msgstr "" +msgid "Preferences|Turns on or off the preference to search with Zoekt instead of Elasticsearch." +msgstr "" + msgid "Preferences|Use relative times" msgstr ""