From ae99d823734ca61f2ad4013e87772a7ad457d91d Mon Sep 17 00:00:00 2001 From: Allen Cook <acook@gitlab.com> Date: Tue, 12 Dec 2023 19:39:32 +0000 Subject: [PATCH] Add code completions enabled API --- app/models/project.rb | 6 +++ app/models/project_setting.rb | 1 + ...add_code_suggestions_to_project_setting.rb | 10 ++++ db/schema_migrations/20231205163658 | 1 + db/structure.sql | 1 + ee/lib/api/code_suggestions.rb | 29 ++++++++++++ ee/spec/requests/api/code_suggestions_spec.rb | 47 +++++++++++++++++++ lib/api/entities/project.rb | 1 + spec/factories/projects.rb | 12 +++++ 9 files changed, 108 insertions(+) create mode 100644 db/migrate/20231205163658_add_code_suggestions_to_project_setting.rb create mode 100644 db/schema_migrations/20231205163658 diff --git a/app/models/project.rb b/app/models/project.rb index 35e38cd37d1ef..26d5ca6c69b47 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -551,6 +551,7 @@ def self.integration_association_name(name) delegate :show_default_award_emojis, :show_default_award_emojis= delegate :enforce_auth_checks_on_uploads, :enforce_auth_checks_on_uploads= delegate :warn_about_potentially_unwanted_characters, :warn_about_potentially_unwanted_characters= + delegate :code_suggestions, :code_suggestions= end end @@ -3199,6 +3200,11 @@ def instance_runner_running_jobs_count end strong_memoize_attr :instance_runner_running_jobs_count + def code_suggestions_enabled? + code_suggestions && (group.nil? || group.code_suggestions) + end + strong_memoize_attr :code_suggestions_enabled? + private # overridden in EE diff --git a/app/models/project_setting.rb b/app/models/project_setting.rb index f684feacace14..e3ffe2347d868 100644 --- a/app/models/project_setting.rb +++ b/app/models/project_setting.rb @@ -39,6 +39,7 @@ class ProjectSetting < ApplicationRecord validates :issue_branch_template, length: { maximum: Issue::MAX_BRANCH_TEMPLATE } validates :target_platforms, inclusion: { in: ALLOWED_TARGET_PLATFORMS } validates :suggested_reviewers_enabled, inclusion: { in: [true, false] } + validates :code_suggestions, allow_nil: false, inclusion: { in: [true, false] } validates :pages_unique_domain, uniqueness: { if: -> { pages_unique_domain.present? } }, diff --git a/db/migrate/20231205163658_add_code_suggestions_to_project_setting.rb b/db/migrate/20231205163658_add_code_suggestions_to_project_setting.rb new file mode 100644 index 0000000000000..53c303963a55c --- /dev/null +++ b/db/migrate/20231205163658_add_code_suggestions_to_project_setting.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class AddCodeSuggestionsToProjectSetting < Gitlab::Database::Migration[2.2] + enable_lock_retries! + milestone '16.7' + + def change + add_column :project_settings, :code_suggestions, :boolean, default: true, null: false + end +end diff --git a/db/schema_migrations/20231205163658 b/db/schema_migrations/20231205163658 new file mode 100644 index 0000000000000..c2ecfa8486bcc --- /dev/null +++ b/db/schema_migrations/20231205163658 @@ -0,0 +1 @@ +94118057fe8e0d4ed9ac6590e3aa48088f26524f02dead72f338ff58c078ef33 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index dc6e8c4cef9a6..c1ef64251e1dc 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -22195,6 +22195,7 @@ CREATE TABLE project_settings ( encrypted_product_analytics_configurator_connection_string_iv bytea, pages_multiple_versions_enabled boolean DEFAULT false NOT NULL, allow_merge_without_pipeline boolean DEFAULT false NOT NULL, + code_suggestions boolean DEFAULT true NOT NULL, CONSTRAINT check_1a30456322 CHECK ((char_length(pages_unique_domain) <= 63)), CONSTRAINT check_3a03e7557a CHECK ((char_length(previous_default_branch) <= 4096)), CONSTRAINT check_3ca5cbffe6 CHECK ((char_length(issue_branch_template) <= 255)), diff --git a/ee/lib/api/code_suggestions.rb b/ee/lib/api/code_suggestions.rb index 69e2a29e68ee7..e6fb68c3d3ad0 100644 --- a/ee/lib/api/code_suggestions.rb +++ b/ee/lib/api/code_suggestions.rb @@ -173,6 +173,35 @@ def gitlab_realm body '' end end + + resources :enabled do + desc 'Code suggestions enabled for a project' do + success code: 200 + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 403, message: '403 Code Suggestions Disabled' }, + { code: 404, message: 'Not found' } + ] + end + params do + requires :project_path, type: String, desc: 'The path of the project', + documentation: { example: 'namespace/project' } + end + + post do + path = declared_params[:project_path] + + not_found! if path.empty? + + projects = ::ProjectsFinder.new(params: { full_paths: [path] }, current_user: current_user).execute + + not_found! if projects.none? + + forbidden! unless projects.first.code_suggestions_enabled? + + status :ok + end + end end end end diff --git a/ee/spec/requests/api/code_suggestions_spec.rb b/ee/spec/requests/api/code_suggestions_spec.rb index b8a76901a6b43..253a0dca3dd33 100644 --- a/ee/spec/requests/api/code_suggestions_spec.rb +++ b/ee/spec/requests/api/code_suggestions_spec.rb @@ -725,4 +725,51 @@ def is_even(n: int) -> end end end + + context 'when checking in project has code suggestions enabled' do + let_it_be(:enabled_project) { create(:project, :with_code_suggestions_enabled) } + let(:current_user) { authorized_user } + let_it_be(:disabled_project) { create(:project, :with_code_suggestions_disabled) } + let_it_be(:secret_project) { create(:project, :with_code_suggestions_enabled) } + + before_all do + enabled_project.add_maintainer(authorized_user) + disabled_project.add_maintainer(authorized_user) + end + + subject { post api("/code_suggestions/enabled", current_user), params: { project_path: project_path } } + + context 'when not logged in' do + let(:current_user) { nil } + let(:project_path) { enabled_project.full_path } + + it { is_expected.to eq(401) } + end + + context 'when authorized' do + context 'when enabled' do + let(:project_path) { enabled_project.full_path } + + it { is_expected.to eq(200) } + end + + context 'when disabled' do + let(:project_path) { disabled_project.full_path } + + it { is_expected.to eq(403) } + end + + context 'when user cannot access project' do + let(:project_path) { secret_project.full_path } + + it { is_expected.to eq(404) } + end + + context 'when does not exist' do + let(:project_path) { 'not_a_real_project' } + + it { is_expected.to eq(404) } + end + end + end end diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb index 12e022bfb2044..b8e489c9d5192 100644 --- a/lib/api/entities/project.rb +++ b/lib/api/entities/project.rb @@ -41,6 +41,7 @@ class Project < BasicProjectDetails end end + expose :code_suggestions, documentation: { type: 'boolean' } expose :packages_enabled, documentation: { type: 'boolean' } expose :empty_repo?, as: :empty_repo, documentation: { type: 'boolean' } expose :archived?, as: :archived, documentation: { type: 'boolean' } diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 4da7dcba7635d..a2848bd025613 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -608,4 +608,16 @@ path { 'gitlab-profile' } files { { 'README.md' => 'Hello World' } } end + + trait :with_code_suggestions_enabled do + after(:create) do |project| + project.project_setting.update!(code_suggestions: true) + end + end + + trait :with_code_suggestions_disabled do + after(:create) do |project| + project.project_setting.update!(code_suggestions: false) + end + end end -- GitLab