diff --git a/doc/development/internal_analytics/internal_event_instrumentation/introduction.md b/doc/development/internal_analytics/internal_event_instrumentation/introduction.md deleted file mode 100644 index 1becf2da6aec93c37a1b4b219cf9ac045e22e3c9..0000000000000000000000000000000000000000 --- a/doc/development/internal_analytics/internal_event_instrumentation/introduction.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -stage: Monitor -group: Analytics Instrumentation -info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review. ---- - -# Internal event tracking - -This page is under construction. It serves as placeholder for the following information: - -- High level introduction -- Difference between Events and Metrics -- Basic overview of the architecture diff --git a/ee/elastic/docs/20240119130539_reindex_notes_to_update_analyzer.yml b/ee/elastic/docs/20240119130539_reindex_notes_to_update_analyzer.yml new file mode 100644 index 0000000000000000000000000000000000000000..71d7a9d1ea95a20acb25dfb7b6b202912950462c --- /dev/null +++ b/ee/elastic/docs/20240119130539_reindex_notes_to_update_analyzer.yml @@ -0,0 +1,10 @@ +--- +name: ReindexNotesToUpdateAnalyzer +version: '20240119130539' +description: This migration reindexes the notes index to start using new analyzer for notes.note +group: group::global search +milestone: '16.9' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142301 +obsolete: false +marked_obsolete_by_url: +marked_obsolete_in_milestone: diff --git a/ee/elastic/migrate/20240119130539_reindex_notes_to_update_analyzer.rb b/ee/elastic/migrate/20240119130539_reindex_notes_to_update_analyzer.rb new file mode 100644 index 0000000000000000000000000000000000000000..ddb293cf7d943f60c7e63523237ac28b02733585 --- /dev/null +++ b/ee/elastic/migrate/20240119130539_reindex_notes_to_update_analyzer.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class ReindexNotesToUpdateAnalyzer < Elastic::Migration + def migrate + Elastic::ReindexingTask.create!(targets: %w[Note], options: { skip_pending_migrations_check: true }) + end + + def completed? + true + end +end diff --git a/ee/lib/elastic/latest/note_config.rb b/ee/lib/elastic/latest/note_config.rb index fcb55b5f6152a2c5f21a11f3f55b817c9098d03b..4313ee1217bb33cdc0ae5e2e53a0aa412bdf7328 100644 --- a/ee/lib/elastic/latest/note_config.rb +++ b/ee/lib/elastic/latest/note_config.rb @@ -21,7 +21,7 @@ module NoteConfig indexes :id, type: :integer - indexes :note, type: :text, index_options: 'positions' + indexes :note, type: :text, index_options: 'positions', analyzer: :code_analyzer indexes :project_id, type: :integer indexes :noteable_type, type: :keyword diff --git a/ee/spec/elastic/migrate/20240119130539_reindex_notes_to_update_analyzer_spec.rb b/ee/spec/elastic/migrate/20240119130539_reindex_notes_to_update_analyzer_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..b6e0ea081910ca03ace3aa05f0ae9563983278e7 --- /dev/null +++ b/ee/spec/elastic/migrate/20240119130539_reindex_notes_to_update_analyzer_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_relative 'migration_shared_examples' +require File.expand_path('ee/elastic/migrate/20240119130539_reindex_notes_to_update_analyzer.rb') + +RSpec.describe ReindexNotesToUpdateAnalyzer, feature_category: :global_search do + let(:version) { 20240119130539 } + let(:migration) { described_class.new(version) } + + it 'does not have migration options set', :aggregate_failures do + expect(migration).not_to be_batched + expect(migration).not_to be_retry_on_failure + end + + describe '#migrate' do + it 'creates reindexing task with correct target and options' do + expect { migration.migrate }.to change { Elastic::ReindexingTask.count }.by(1) + task = Elastic::ReindexingTask.last + expect(task.targets).to eq(%w[Note]) + expect(task.options).to eq({ 'skip_pending_migrations_check' => true }) + end + end + + describe '#completed?' do + it 'always returns true' do + expect(migration.completed?).to eq(true) + end + end +end diff --git a/ee/spec/lib/gitlab/elastic/search_results_spec.rb b/ee/spec/lib/gitlab/elastic/search_results_spec.rb index ae813aef486b129636e21eafa979654ded2cccef..0faa50f57542e0fb24d3cfeb2992d0e4aeff4142 100644 --- a/ee/spec/lib/gitlab/elastic/search_results_spec.rb +++ b/ee/spec/lib/gitlab/elastic/search_results_spec.rb @@ -14,53 +14,6 @@ let(:project_2) { create(:project, :public, :repository, :wiki_repo) } let(:limit_project_ids) { [project_1.id] } - shared_context 'with elastic code examples' do - let(:code_examples) do - { - 'perlMethodCall' => '$my_perl_object->perlMethodCall', - '"absolute_with_specials.txt"' => '/a/longer/file-path/absolute_with_specials.txt', - '"components-within-slashes"' => '/file-path/components-within-slashes/', - 'bar\(x\)' => 'Foo.bar(x)', - 'someSingleColonMethodCall' => 'LanguageWithSingleColon:someSingleColonMethodCall', - 'javaLangStaticMethodCall' => 'MyJavaClass::javaLangStaticMethodCall', - 'IllegalStateException' => 'java.lang.IllegalStateException', - 'tokenAfterParentheses' => 'ParenthesesBetweenTokens)tokenAfterParentheses', - 'ruby_call_method_123' => 'RubyClassInvoking.ruby_call_method_123(with_arg)', - 'ruby_method_call' => 'RubyClassInvoking.ruby_method_call(with_arg)', - '#ambitious-planning' => 'We [plan ambitiously](#ambitious-planning).', - 'ambitious-planning' => 'We [plan ambitiously](#ambitious-planning).', - 'tokenAfterCommaWithNoSpace' => 'WouldHappenInManyLanguages,tokenAfterCommaWithNoSpace', - 'missing_token_around_equals' => 'a.b.c=missing_token_around_equals', - 'and;colons:too$' => 'and;colons:too$', - '"differeñt-lønguage.txt"' => 'another/file-path/differeñt-lønguage.txt', - '"relative-with-specials.txt"' => 'another/file-path/relative-with-specials.txt', - 'ruby_method_123' => 'def self.ruby_method_123(ruby_another_method_arg)', - 'ruby_method_name' => 'def self.ruby_method_name(ruby_method_arg)', - '"dots.also.need.testing"' => 'dots.also.need.testing', - '.testing' => 'dots.also.need.testing', - 'dots' => 'dots.also.need.testing', - 'also.need' => 'dots.also.need.testing', - 'need' => 'dots.also.need.testing', - 'tests-image' => 'extends: .gitlab-tests-image', - 'gitlab-tests' => 'extends: .gitlab-tests-image', - 'gitlab-tests-image' => 'extends: .gitlab-tests-image', - 'foo/bar' => 'https://s3.amazonaws.com/foo/bar/baz.png', - 'https://test.or.dev.com/repository' => 'https://test.or.dev.com/repository/maven-all', - 'test.or.dev.com/repository/maven-all' => 'https://test.or.dev.com/repository/maven-all', - 'repository/maven-all' => 'https://test.or.dev.com/repository/maven-all', - 'https://test.or.dev.com/repository/maven-all' => 'https://test.or.dev.com/repository/maven-all', - 'bar-baz-conventions' => 'id("foo.bar-baz-conventions")', - 'baz-conventions' => 'id("foo.bar-baz-conventions")', - 'baz' => 'id("foo.bar-baz-conventions")', - 'bikes-3.4' => 'include "bikes-3.4"', - 'sql_log_bin' => 'q = "SET @@session.sql_log_bin=0;"', - 'sql_log_bin=0' => 'q = "SET @@session.sql_log_bin=0;"', - 'v3/delData' => 'uri: "v3/delData"', - '"us-east-2"' => 'us-east-2' - } - end - end - describe '#highlight_map' do using RSpec::Parameterized::TableSyntax @@ -533,6 +486,26 @@ expect(results.notes_count).to eq 2 end + context 'when comment has some code snippet' do + before do + code_examples.values.uniq.each do |note| + sha = Digest::SHA256.hexdigest(note) + create(:note_on_issue, noteable: issue, project: project_1, commit_id: sha, note: note) + end + ensure_elasticsearch_index! + end + + include_context 'with code examples' do + it 'finds all examples' do + code_examples.each do |query, description| + sha = Digest::SHA256.hexdigest(description) + notes = described_class.new(user, query, limit_project_ids).objects('notes') + expect(notes.map(&:commit_id)).to include(sha) + end + end + end + end + it 'returns empty list when notes are not found' do results = described_class.new(user, 'security', limit_project_ids) @@ -1096,7 +1069,7 @@ def search_for(term) ensure_elasticsearch_index! end - include_context 'with elastic code examples' do + include_context 'with code examples' do it 'finds all examples' do code_examples.each do |search_term, file_content| file_name = Digest::SHA256.hexdigest(file_content) @@ -1308,7 +1281,7 @@ def search_for(term) ) end - include_context 'with elastic code examples' do + include_context 'with code examples' do before do examples.values.uniq.each do |description| sha = Digest::SHA256.hexdigest(description) diff --git a/ee/spec/requests/api/graphql/project/branch_rules_spec.rb b/ee/spec/requests/api/graphql/project/branch_rules_spec.rb index 420038e35aab601d61c497c5ca577ae1dd60d32f..3e16ab22031c19b42284d2db075f2bdfbbd264d4 100644 --- a/ee/spec/requests/api/graphql/project/branch_rules_spec.rb +++ b/ee/spec/requests/api/graphql/project/branch_rules_spec.rb @@ -81,26 +81,35 @@ def expect_n_matching_branches_count_fields(count) describe 'response' do let_it_be(:branch_name_a) { TestEnv::BRANCH_SHA.each_key.first } let_it_be(:branch_name_b) { 'diff-*' } - let_it_be(:branch_rule_a) do + let_it_be(:protected_branch_a) do create(:protected_branch, project: project, name: branch_name_a) end - let_it_be(:branch_rule_b) do + let_it_be(:protected_branch_b) do create(:protected_branch, project: project, name: branch_name_b) end - let_it_be(:external_status_check) do + let_it_be(:all_branches_external_status_check) do create(:external_status_check, project: project) end - let_it_be(:approval_project_rule) do + let_it_be(:all_branches_approval_rule) do create(:approval_project_rule, project: project) end + let_it_be(:all_protected_branches_approval_rule) do + create(:approval_project_rule, project: project, applies_to_all_protected_branches: true) + end + + let(:branch_rule_a) { Projects::BranchRule.new(project, protected_branch_a) } + let(:branch_rule_b) { Projects::BranchRule.new(project, protected_branch_b) } + let(:all_branches_rule) { Projects::AllBranchesRule.new(project) } + let(:all_protected_branches_rule) { Projects::AllProtectedBranchesRule.new(project) } # branchRules are returned in alphabetical order let(:all_branches_rule_data) { branch_rules_data.first } - let(:branch_rule_b_data) { branch_rules_data.second } - let(:branch_rule_a_data) { branch_rules_data.third } + let(:all_protected_branches_rule_data) { branch_rules_data.second } + let(:branch_rule_b_data) { branch_rules_data.third } + let(:branch_rule_a_data) { branch_rules_data.fourth } before do post_graphql(query, current_user: current_user, variables: variables) @@ -110,59 +119,74 @@ def expect_n_matching_branches_count_fields(count) it 'includes all fields', :use_sql_query_cache, :aggregate_failures do expect(all_branches_rule_data).to include( - 'name' => 'All branches', - 'isDefault' => false, - 'isProtected' => false, - 'matchingBranchesCount' => project.repository.branch_count, - 'branchProtection' => nil, - 'createdAt' => be_kind_of(String), - 'updatedAt' => be_kind_of(String), + 'name' => all_branches_rule.name, + 'isDefault' => all_branches_rule.default_branch?, + 'isProtected' => all_branches_rule.protected?, + 'matchingBranchesCount' => all_branches_rule.matching_branches_count, + 'branchProtection' => all_branches_rule.branch_protection, + 'createdAt' => all_branches_rule.created_at.iso8601, + 'updatedAt' => all_branches_rule.updated_at.iso8601, 'approvalRules' => be_kind_of(Hash), 'externalStatusChecks' => be_kind_of(Hash) ) approval_rules_data = all_branches_rule_data['approvalRules']['nodes'] expect(approval_rules_data).to eq([{ - 'id' => approval_project_rule.to_global_id.to_s, - 'name' => approval_project_rule.name, + 'id' => all_branches_approval_rule.to_global_id.to_s, + 'name' => all_branches_approval_rule.name, 'type' => 'REGULAR', 'approvalsRequired' => 0 }]) external_checks_data = all_branches_rule_data['externalStatusChecks']['nodes'] expect(external_checks_data).to eq([{ - 'id' => external_status_check.to_global_id.to_s, - 'name' => external_status_check.name, - 'externalUrl' => external_status_check.external_url + 'id' => all_branches_external_status_check.to_global_id.to_s, + 'name' => all_branches_external_status_check.name, + 'externalUrl' => all_branches_external_status_check.external_url + }]) + expect(all_protected_branches_rule_data).to include( + 'name' => all_protected_branches_rule.name, + 'isDefault' => all_protected_branches_rule.default_branch?, + 'isProtected' => all_protected_branches_rule.protected?, + 'matchingBranchesCount' => all_protected_branches_rule.matching_branches_count, + 'branchProtection' => all_protected_branches_rule.branch_protection, + 'createdAt' => all_protected_branches_rule.created_at.iso8601, + 'updatedAt' => all_protected_branches_rule.updated_at.iso8601, + 'approvalRules' => be_kind_of(Hash), + 'externalStatusChecks' => be_kind_of(Hash) + ) + approval_rules_data = all_protected_branches_rule_data['approvalRules']['nodes'] + expect(approval_rules_data).to eq([{ + 'id' => all_protected_branches_approval_rule.to_global_id.to_s, + 'name' => all_protected_branches_approval_rule.name, + 'type' => 'REGULAR', + 'approvalsRequired' => 0 }]) expect(branch_rule_a_data).to include( - 'name' => branch_name_a, - 'isDefault' => be_boolean, - 'isProtected' => true, - 'matchingBranchesCount' => 1, + 'name' => branch_rule_a.name, + 'isDefault' => branch_rule_a.default_branch?, + 'isProtected' => branch_rule_a.protected?, + 'matchingBranchesCount' => branch_rule_a.matching_branches_count, 'branchProtection' => { "allowForcePush" => false, "codeOwnerApprovalRequired" => false }, - 'createdAt' => be_kind_of(String), - 'updatedAt' => be_kind_of(String), + 'createdAt' => branch_rule_a.created_at.iso8601, + 'updatedAt' => branch_rule_a.updated_at.iso8601, 'approvalRules' => be_kind_of(Hash), 'externalStatusChecks' => be_kind_of(Hash) ) - wildcard_count = TestEnv::BRANCH_SHA.keys.count do |branch_name| - branch_name.starts_with?('diff-') - end expect(branch_rule_b_data).to include( - 'name' => branch_name_b, - 'isDefault' => be_boolean, - 'isProtected' => true, - 'matchingBranchesCount' => wildcard_count, + 'name' => branch_rule_b.name, + 'isDefault' => branch_rule_b.default_branch?, + 'isProtected' => branch_rule_b.protected?, + 'matchingBranchesCount' => branch_rule_b.matching_branches_count, 'branchProtection' => { "allowForcePush" => false, "codeOwnerApprovalRequired" => false }, - 'createdAt' => be_kind_of(String), - 'updatedAt' => be_kind_of(String), + 'createdAt' => branch_rule_a.created_at.iso8601, + 'updatedAt' => branch_rule_a.updated_at.iso8601, 'approvalRules' => be_kind_of(Hash), 'externalStatusChecks' => be_kind_of(Hash) ) diff --git a/ee/spec/support/shared_contexts/search/elastic/with_code_examples_shared_context.rb b/ee/spec/support/shared_contexts/search/elastic/with_code_examples_shared_context.rb new file mode 100644 index 0000000000000000000000000000000000000000..98b5e6ef6131357dcfc8052e6314a41b60d10be6 --- /dev/null +++ b/ee/spec/support/shared_contexts/search/elastic/with_code_examples_shared_context.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +RSpec.shared_context 'with code examples' do + let(:code_examples) do + { + 'perlMethodCall' => '$my_perl_object->perlMethodCall', + '"absolute_with_specials.txt"' => '/a/longer/file-path/absolute_with_specials.txt', + '"components-within-slashes"' => '/file-path/components-within-slashes/', + 'bar\(x\)' => 'Foo.bar(x)', + 'someSingleColonMethodCall' => 'LanguageWithSingleColon:someSingleColonMethodCall', + 'javaLangStaticMethodCall' => 'MyJavaClass::javaLangStaticMethodCall', + 'IllegalStateException' => 'java.lang.IllegalStateException', + 'tokenAfterParentheses' => 'ParenthesesBetweenTokens)tokenAfterParentheses', + 'ruby_call_method_123' => 'RubyClassInvoking.ruby_call_method_123(with_arg)', + 'ruby_method_call' => 'RubyClassInvoking.ruby_method_call(with_arg)', + '#ambitious-planning' => 'We [plan ambitiously](#ambitious-planning).', + 'ambitious-planning' => 'We [plan ambitiously](#ambitious-planning).', + 'tokenAfterCommaWithNoSpace' => 'WouldHappenInManyLanguages,tokenAfterCommaWithNoSpace', + 'missing_token_around_equals' => 'a.b.c=missing_token_around_equals', + 'and;colons:too$' => 'and;colons:too$', + '"differeñt-lønguage.txt"' => 'another/file-path/differeñt-lønguage.txt', + '"relative-with-specials.txt"' => 'another/file-path/relative-with-specials.txt', + 'ruby_method_123' => 'def self.ruby_method_123(ruby_another_method_arg)', + 'ruby_method_name' => 'def self.ruby_method_name(ruby_method_arg)', + '"dots.also.need.testing"' => 'dots.also.need.testing', + '.testing' => 'dots.also.need.testing', + 'dots' => 'dots.also.need.testing', + 'also.need' => 'dots.also.need.testing', + 'need' => 'dots.also.need.testing', + 'tests-image' => 'extends: .gitlab-tests-image', + 'gitlab-tests' => 'extends: .gitlab-tests-image', + 'gitlab-tests-image' => 'extends: .gitlab-tests-image', + 'foo/bar' => 'https://s3.amazonaws.com/foo/bar/baz.png', + 'https://test.or.dev.com/repository' => 'https://test.or.dev.com/repository/maven-all', + 'test.or.dev.com/repository/maven-all' => 'https://test.or.dev.com/repository/maven-all', + 'repository/maven-all' => 'https://test.or.dev.com/repository/maven-all', + 'https://test.or.dev.com/repository/maven-all' => 'https://test.or.dev.com/repository/maven-all', + 'bar-baz-conventions' => 'id("foo.bar-baz-conventions")', + 'baz-conventions' => 'id("foo.bar-baz-conventions")', + 'baz' => 'id("foo.bar-baz-conventions")', + 'bikes-3.4' => 'include "bikes-3.4"', + 'sql_log_bin' => 'q = "SET @@session.sql_log_bin=0;"', + 'sql_log_bin=0' => 'q = "SET @@session.sql_log_bin=0;"', + 'v3/delData' => 'uri: "v3/delData"', + '"us-east-2"' => 'us-east-2' + } + end +end diff --git a/qa/Gemfile b/qa/Gemfile index ec0537249067508131057b18b9efd760fa875229..7e6aa64258870941609c7bda406f884f8708b28b 100644 --- a/qa/Gemfile +++ b/qa/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' -gem 'gitlab-qa', '~> 14', require: 'gitlab/qa' +gem 'gitlab-qa', '~> 14', '>= 14.0.1', require: 'gitlab/qa' gem 'gitlab_quality-test_tooling', '~> 1.11.0', require: false gem 'gitlab-utils', path: '../gems/gitlab-utils' gem 'activesupport', '~> 7.0.8' # This should stay in sync with the root's Gemfile diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock index 26dae330ef6ad8ab40603eb8f8f40b9e92a9d28f..16306299bfe3a05ddfa8c1771755832f8547b41d 100644 --- a/qa/Gemfile.lock +++ b/qa/Gemfile.lock @@ -118,7 +118,7 @@ GEM gitlab (4.19.0) httparty (~> 0.20) terminal-table (>= 1.5.1) - gitlab-qa (14.0.0) + gitlab-qa (14.0.1) activesupport (>= 6.1, < 7.2) gitlab (~> 4.19) http (~> 5.0) @@ -354,7 +354,7 @@ DEPENDENCIES faraday-retry (~> 2.2) fog-core (= 2.1.0) fog-google (~> 1.19) - gitlab-qa (~> 14) + gitlab-qa (~> 14, >= 14.0.1) gitlab-utils! gitlab_quality-test_tooling (~> 1.11.0) influxdb-client (~> 3.0) diff --git a/spec/requests/api/graphql/project/branch_rules_spec.rb b/spec/requests/api/graphql/project/branch_rules_spec.rb index 2ca37a491490bd890b3bd0a24f5e7f33d24c14c1..63537ba5750ce21f95db9d4f8d9f5fd1a921fd96 100644 --- a/spec/requests/api/graphql/project/branch_rules_spec.rb +++ b/spec/requests/api/graphql/project/branch_rules_spec.rb @@ -101,15 +101,17 @@ def expect_n_matching_branches_count_fields(count) describe 'response' do let_it_be(:branch_name_a) { TestEnv::BRANCH_SHA.each_key.first } let_it_be(:branch_name_b) { 'diff-*' } - let_it_be(:branch_rules) { [branch_rule_a, branch_rule_b] } - let_it_be(:branch_rule_a) do + let_it_be(:protected_branch_a) do create(:protected_branch, project: project, name: branch_name_a) end - let_it_be(:branch_rule_b) do + let_it_be(:protected_branch_b) do create(:protected_branch, project: project, name: branch_name_b) end + let(:branch_rule_a) { Projects::BranchRule.new(project, protected_branch_a) } + let(:branch_rule_b) { Projects::BranchRule.new(project, protected_branch_b) } + let(:branch_rules) { [branch_rule_a, branch_rule_b] } # branchRules are returned in alphabetical order let(:branch_rule_a_data) { branch_rules_data.dig(1, 'node') } let(:branch_rule_b_data) { branch_rules_data.dig(0, 'node') }