diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 32b187c32606c99a4b41e58214c423a07b059164..aefbb43f3df46d3a7d207be3758135b4b6a2382b 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -276,6 +276,7 @@ def group_params_attributes :avatar, :description, :emails_disabled, + :show_diff_preview_in_email, :mentions_disabled, :lfs_enabled, :name, diff --git a/app/models/namespace.rb b/app/models/namespace.rb index c678dbfa6311ff879cc7fa8ea443ab3cb4c559d4..d519ceb658d6dd364b02c7cad3339a8ebe152bd5 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -128,6 +128,8 @@ class Namespace < ApplicationRecord delegate :avatar_url, to: :owner, allow_nil: true delegate :prevent_sharing_groups_outside_hierarchy, :prevent_sharing_groups_outside_hierarchy=, to: :namespace_settings, allow_nil: true + delegate :show_diff_preview_in_email, :show_diff_preview_in_email?, :show_diff_preview_in_email=, + to: :namespace_settings after_save :schedule_sync_event_worker, if: -> { saved_change_to_id? || saved_change_to_parent_id? } after_save :reload_namespace_details diff --git a/app/models/namespace_setting.rb b/app/models/namespace_setting.rb index fa9b525d4fe99aee71235c9183d612108ac37f35..6a87fba57acfdd27568eec4cfb991fd988fc887c 100644 --- a/app/models/namespace_setting.rb +++ b/app/models/namespace_setting.rb @@ -58,8 +58,18 @@ def prevent_sharing_groups_outside_hierarchy namespace.root_ancestor.prevent_sharing_groups_outside_hierarchy end + def show_diff_preview_in_email? + return show_diff_preview_in_email unless namespace.has_parent? + + all_ancestors_allow_diff_preview_in_email? + end + private + def all_ancestors_allow_diff_preview_in_email? + !self.class.where(namespace_id: namespace.self_and_ancestors, show_diff_preview_in_email: false).exists? + end + def normalize_default_branch_name self.default_branch_name = default_branch_name.presence end diff --git a/app/models/project.rb b/app/models/project.rb index 3053055bd771eb0d81074cdb74f58c9130dcc651..d8d08cbffcccc7495578f9374464580349774d4a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -462,6 +462,9 @@ def self.integration_association_name(name) :warn_about_potentially_unwanted_characters, :warn_about_potentially_unwanted_characters=, to: :project_setting, allow_nil: true + delegate :show_diff_preview_in_email, :show_diff_preview_in_email=, :show_diff_preview_in_email?, + to: :project_setting + delegate :squash_always?, :squash_never?, :squash_enabled_by_default?, :squash_readonly?, to: :project_setting delegate :squash_option, :squash_option=, to: :project_setting delegate :mr_default_target_self, :mr_default_target_self=, to: :project_setting diff --git a/app/models/project_setting.rb b/app/models/project_setting.rb index 59d2e3deb4f033810e095fdb4ae9882354ab1308..f5c346eda3056602ca00b739e622456bd950cc8e 100644 --- a/app/models/project_setting.rb +++ b/app/models/project_setting.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class ProjectSetting < ApplicationRecord + include ::Gitlab::Utils::StrongMemoize + ALLOWED_TARGET_PLATFORMS = %w(ios osx tvos watchos android).freeze belongs_to :project, inverse_of: :project_setting @@ -47,6 +49,15 @@ def human_squash_option end end + def show_diff_preview_in_email? + if project.group + super && project.group&.show_diff_preview_in_email? + else + !!super + end + end + strong_memoize_attr :show_diff_preview_in_email + private def validates_mr_default_target_self diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index a264ff48c08e163b5cedec5c982795b656a1b253..bee32d789aca457d34bc1c57dfdb233e43c0a860 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -193,6 +193,7 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy enable :set_note_created_at enable :set_emails_disabled enable :change_prevent_sharing_groups_outside_hierarchy + enable :set_show_diff_preview_in_email enable :change_new_user_signups_cap enable :update_default_branch_protection enable :create_deploy_token diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index f4f7275a78a35e246e7e4d37778f684bccaee4e2..ccdad8e6be1716e8cd5b3920a9152f7f3f577ee6 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -267,6 +267,7 @@ class ProjectPolicy < BasePolicy enable :set_note_created_at enable :set_emails_disabled enable :set_show_default_award_emojis + enable :set_show_diff_preview_in_email enable :set_warn_about_potentially_unwanted_characters enable :register_project_runners diff --git a/db/migrate/20220603125200_add_show_diff_preview_in_email_to_namespace_settings.rb b/db/migrate/20220603125200_add_show_diff_preview_in_email_to_namespace_settings.rb new file mode 100644 index 0000000000000000000000000000000000000000..ad32d58984075f73716f7c28179da534732e208f --- /dev/null +++ b/db/migrate/20220603125200_add_show_diff_preview_in_email_to_namespace_settings.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddShowDiffPreviewInEmailToNamespaceSettings < Gitlab::Database::Migration[2.0] + enable_lock_retries! + + def change + add_column :namespace_settings, :show_diff_preview_in_email, :boolean, default: true, null: false + end +end diff --git a/db/migrate/20220817122907_re_add_show_diff_preview_in_email_to_project_settings.rb b/db/migrate/20220817122907_re_add_show_diff_preview_in_email_to_project_settings.rb new file mode 100644 index 0000000000000000000000000000000000000000..bb5649e3a99774480a0935526ffac4920f0467bf --- /dev/null +++ b/db/migrate/20220817122907_re_add_show_diff_preview_in_email_to_project_settings.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class ReAddShowDiffPreviewInEmailToProjectSettings < Gitlab::Database::Migration[2.0] + enable_lock_retries! + + def change + add_column :project_settings, :show_diff_preview_in_email, :boolean, default: true, null: false + end +end diff --git a/db/schema_migrations/20220603125200 b/db/schema_migrations/20220603125200 new file mode 100644 index 0000000000000000000000000000000000000000..5da1d1992ab70e3421075404268cf7071b848735 --- /dev/null +++ b/db/schema_migrations/20220603125200 @@ -0,0 +1 @@ +7631f2c1f9b2647ae6de47675305a2d5c1b213229c85b6f161412f83884bad87 \ No newline at end of file diff --git a/db/schema_migrations/20220817122907 b/db/schema_migrations/20220817122907 new file mode 100644 index 0000000000000000000000000000000000000000..fb6951e19d54e2a22b873beeb273da2a3745ef7f --- /dev/null +++ b/db/schema_migrations/20220817122907 @@ -0,0 +1 @@ +4db4f50d2e23527516eccdeae60059803df7add21ca7a2c40f1670dba9744496 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 9a2b98050460dcab614d333d4d75d34e534418f7..b558d104ee973dcc412102a04e038726de1d66e6 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -17818,6 +17818,7 @@ CREATE TABLE namespace_settings ( subgroup_runner_token_expiration_interval integer, project_runner_token_expiration_interval integer, exclude_from_free_user_cap boolean DEFAULT false NOT NULL, + show_diff_preview_in_email boolean DEFAULT true NOT NULL, enabled_git_access_protocol smallint DEFAULT 0 NOT NULL, unique_project_download_limit smallint DEFAULT 0 NOT NULL, unique_project_download_limit_interval_in_seconds integer DEFAULT 0 NOT NULL, @@ -19936,6 +19937,7 @@ CREATE TABLE project_settings ( target_platforms character varying[] DEFAULT '{}'::character varying[] NOT NULL, enforce_auth_checks_on_uploads boolean DEFAULT true NOT NULL, selective_code_owner_removals boolean DEFAULT false NOT NULL, + show_diff_preview_in_email boolean DEFAULT true NOT NULL, CONSTRAINT check_3a03e7557a CHECK ((char_length(previous_default_branch) <= 4096)), CONSTRAINT check_b09644994b CHECK ((char_length(squash_commit_template) <= 500)), CONSTRAINT check_bde223416c CHECK ((show_default_award_emojis IS NOT NULL)), diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb index 40da4a4f8e82b9fdfe7f08f9e6738c84988228fa..7ca3f55b5a2c7e7e8dd8c51515eff87cc5c73c91 100644 --- a/lib/api/helpers/projects_helpers.rb +++ b/lib/api/helpers/projects_helpers.rb @@ -39,6 +39,7 @@ module ProjectsHelpers optional :emails_disabled, type: Boolean, desc: 'Disable email notifications' optional :show_default_award_emojis, type: Boolean, desc: 'Show default award emojis' + optional :show_diff_preview_in_email, type: Boolean, desc: 'Include the code diff preview in merge request notification emails' optional :warn_about_potentially_unwanted_characters, type: Boolean, desc: 'Warn about Potentially Unwanted Characters' optional :enforce_auth_checks_on_uploads, type: Boolean, desc: 'Enforce auth check on uploads' optional :shared_runners_enabled, type: Boolean, desc: 'Flag indication if shared runners are enabled for that project' @@ -159,6 +160,7 @@ def self.update_params_at_least_one_of :request_access_enabled, :resolve_outdated_diff_discussions, :restrict_user_defined_variables, + :show_diff_preview_in_email, :security_and_compliance_access_level, :squash_option, :shared_runners_enabled, diff --git a/spec/models/namespace_setting_spec.rb b/spec/models/namespace_setting_spec.rb index 25234db5734e0cb89e4f136d2ed71a3a465c72bc..9fce65c9a5fb878831399691ddc068b97e4c51f2 100644 --- a/spec/models/namespace_setting_spec.rb +++ b/spec/models/namespace_setting_spec.rb @@ -127,4 +127,50 @@ end end end + + describe '#show_diff_preview_in_email?' do + context 'when not a subgroup' do + it 'returns false' do + settings = create(:namespace_settings, show_diff_preview_in_email: false) + group = create(:group, namespace_settings: settings ) + + expect(group.show_diff_preview_in_email?).to be_falsey + end + + it 'returns true' do + settings = create(:namespace_settings, show_diff_preview_in_email: true) + group = create(:group, namespace_settings: settings ) + + expect(group.show_diff_preview_in_email?).to be_truthy + end + + it 'does not query the db when there is no parent group' do + group = create(:group) + + expect { group.show_diff_preview_in_email? }.not_to exceed_query_limit(0) + end + end + + context 'when a group has parent groups' do + let(:grandparent) { create(:group, namespace_settings: settings) } + let(:parent) { create(:group, parent: grandparent) } + let!(:group) { create(:group, parent: parent) } + + context "when a parent group has disabled diff previews" do + let(:settings) { create(:namespace_settings, show_diff_preview_in_email: false) } + + it 'returns false' do + expect(group.show_diff_preview_in_email?).to be_falsey + end + end + + context 'when all parent groups have enabled diff previews' do + let(:settings) { create(:namespace_settings, show_diff_preview_in_email: true) } + + it 'returns true' do + expect(group.show_diff_preview_in_email?).to be_truthy + end + end + end + end end diff --git a/spec/models/project_setting_spec.rb b/spec/models/project_setting_spec.rb index fb1601a5f9c1c7873bf21b93132e714430086572..a09ae7ec7aed6e2b2f1d23605b1bf5e95907331b 100644 --- a/spec/models/project_setting_spec.rb +++ b/spec/models/project_setting_spec.rb @@ -63,4 +63,51 @@ def valid_target_platform_combinations target_platforms.permutation(n).to_a end end + + describe '#show_diff_preview_in_email?' do + context 'when a project is a top-level namespace' do + let(:project_settings ) { create(:project_setting, show_diff_preview_in_email: false) } + let(:project) { create(:project, project_setting: project_settings) } + + context 'when show_diff_preview_in_email is disabled' do + it 'returns false' do + expect(project).not_to be_show_diff_preview_in_email + end + end + + context 'when show_diff_preview_in_email is enabled' do + let(:project_settings ) { create(:project_setting, show_diff_preview_in_email: true) } + + it 'returns true' do + settings = create(:project_setting, show_diff_preview_in_email: true) + project = create(:project, project_setting: settings) + + expect(project).to be_show_diff_preview_in_email + end + end + end + + context 'when a parent group has a parent group' do + let(:namespace_settings) { create(:namespace_settings, show_diff_preview_in_email: false) } + let(:project_settings) { create(:project_setting, show_diff_preview_in_email: true) } + let(:group) { create(:group, namespace_settings: namespace_settings) } + let!(:project) { create(:project, namespace_id: group.id, project_setting: project_settings) } + + context 'when show_diff_preview_in_email is disabled for the parent group' do + it 'returns false' do + expect(project).not_to be_show_diff_preview_in_email + end + end + + context 'when all ancestors have enabled diff previews' do + let(:namespace_settings) { create(:namespace_settings, show_diff_preview_in_email: true) } + + it 'returns true' do + group.update_attribute(:show_diff_preview_in_email, true) + + expect(project).to be_show_diff_preview_in_email + end + end + end + end end diff --git a/spec/requests/api/project_attributes.yml b/spec/requests/api/project_attributes.yml index 670035187cb96a3139879c6ef44bb83f1f3fd15d..1335fa02aaf49fad722955ce2bfc34da32dcea3d 100644 --- a/spec/requests/api/project_attributes.yml +++ b/spec/requests/api/project_attributes.yml @@ -154,11 +154,13 @@ project_setting: - project_id - push_rule_id - show_default_award_emojis + - show_diff_preview_in_email - updated_at - cve_id_request_enabled - mr_default_target_self - target_platforms - selective_code_owner_removals + - show_diff_preview_in_email build_service_desk_setting: # service_desk_setting unexposed_attributes: