diff --git a/app/graphql/mutations/user_preferences/update.rb b/app/graphql/mutations/user_preferences/update.rb index 69c81655410d48a73a338418fdea190b30ae0fb2..4c1df3f22b4ee1dfc702502b42621ab72ecc6b1f 100644 --- a/app/graphql/mutations/user_preferences/update.rb +++ b/app/graphql/mutations/user_preferences/update.rb @@ -53,6 +53,12 @@ class Update < BaseMutation def resolve(**attributes) attributes.delete_if { |key, value| NON_NULLABLE_ARGS.include?(key) && value.nil? } + + if attributes.include?(:extensions_marketplace_opt_in_status) + attributes[:extensions_marketplace_opt_in_url] = + ::WebIde::ExtensionMarketplace.marketplace_home_url(user: current_user) + end + user_preferences = current_user.user_preference user_preferences.update(attributes) diff --git a/app/graphql/types/user_preferences_type.rb b/app/graphql/types/user_preferences_type.rb index 30ac5ae5df7fdb3bd225cf9bf63e54ee4cd33e14..cb8129f32a8757084cab2af1b125ce07fd1ed5d5 100644 --- a/app/graphql/types/user_preferences_type.rb +++ b/app/graphql/types/user_preferences_type.rb @@ -64,5 +64,14 @@ def projects_sort def organization_groups_projects_sort user_preference.organization_groups_projects_sort&.to_sym end + + def extensions_marketplace_opt_in_status + user = user_preference.user + + ::WebIde::ExtensionMarketplaceOptIn.opt_in_status( + user: user, + marketplace_home_url: ::WebIde::ExtensionMarketplace.marketplace_home_url(user: user) + ) + end end end diff --git a/app/models/user.rb b/app/models/user.rb index c405cf5c8ab09f2891c0705169c3bb45237b8472..53e02eccecdb3cd36da14a684448d7564a51510f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -443,9 +443,9 @@ def update_tracked_fields!(request) :sourcegraph_enabled, :sourcegraph_enabled=, :gitpod_enabled, :gitpod_enabled=, :extensions_marketplace_opt_in_status, :extensions_marketplace_opt_in_status=, + :extensions_marketplace_opt_in_url, :extensions_marketplace_opt_in_url=, :organization_groups_projects_sort, :organization_groups_projects_sort=, :organization_groups_projects_display, :organization_groups_projects_display=, - :extensions_marketplace_enabled, :extensions_marketplace_enabled=, :setup_for_company, :setup_for_company=, :project_shortcut_buttons, :project_shortcut_buttons=, :keyboard_shortcuts_enabled, :keyboard_shortcuts_enabled=, diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb index 9f2e6715c6a5fcffd25869e64c6920e2365a12ed..01259f993a947c7d528f85b6942d7394e9ecd0d4 100644 --- a/app/models/user_preference.rb +++ b/app/models/user_preference.rb @@ -29,6 +29,7 @@ class UserPreference < ApplicationRecord validates :pinned_nav_items, json_schema: { filename: 'pinned_nav_items' } validates :time_display_format, inclusion: { in: TIME_DISPLAY_FORMATS.values }, presence: true + validates :extensions_marketplace_opt_in_url, length: { maximum: 512 } validate :user_belongs_to_home_organization, if: :home_organization_changed? @@ -90,16 +91,9 @@ def early_access_event_tracking? early_access_program_participant? && early_access_program_tracking? end - # NOTE: Despite this returning a boolean, it does not end in `?` out of - # symmetry with the other integration fields like `gitpod_enabled` - def extensions_marketplace_enabled - extensions_marketplace_opt_in_status == "enabled" - end - - def extensions_marketplace_enabled=(value) - status = ActiveRecord::Type::Boolean.new.cast(value) ? 'enabled' : 'disabled' - - self.extensions_marketplace_opt_in_status = status + def extensions_marketplace_opt_in_url + # To support existing records, this can be `nil` and it defaults to `https://open-vsx.org` + super || 'https://open-vsx.org' end def dpop_enabled=(value) diff --git a/app/services/users/update_service.rb b/app/services/users/update_service.rb index 42ffc79b4c6c36e7fddea41b4eea24be9791c5fe..29559540defb6070b829f2e4b5b5a325284d3813 100644 --- a/app/services/users/update_service.rb +++ b/app/services/users/update_service.rb @@ -29,6 +29,7 @@ def execute(validate: true, check_password: false, &block) @user.user_detail # prevent assignment discard_read_only_attributes + append_extension_marketplace_attributes assign_attributes if check_password && require_password_check? && !@user.valid_password?(@validation_password) @@ -141,6 +142,19 @@ def synced_attributes end end + def append_extension_marketplace_attributes + return unless params.has_key?(:extensions_marketplace_enabled) + + value = params.delete(:extensions_marketplace_enabled) + marketplace_home_url = ::WebIde::ExtensionMarketplace.marketplace_home_url(user: @user) + update_params = ::WebIde::ExtensionMarketplaceOptIn.params( + enabled: value, + marketplace_home_url: marketplace_home_url + ) + + params.merge!(update_params) + end + def assign_attributes @user.assign_attributes(params.except(*identity_attributes)) unless params.empty? end diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index b448cf5443f2b869b59f25945bd694c548a8b3dd..bdd44063db2cd1d4e17d6e051083853d28bb60c1 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -3,7 +3,9 @@ - user_theme_id = Gitlab::Themes.for_user(@user).id - user_color_mode_id = Gitlab::ColorModes.for_user(@user).id - user_color_schema_id = Gitlab::ColorSchemes.for_user(@user).id -- user_fields = { color_mode_id: user_color_mode_id, theme: user_theme_id, gitpod_enabled: @user.gitpod_enabled, sourcegraph_enabled: @user.sourcegraph_enabled, extensions_marketplace_enabled: @user.extensions_marketplace_enabled }.to_json +- marketplace_home_url = ::WebIde::ExtensionMarketplace.marketplace_home_url(user: @user) +- extensions_marketplace_enabled = ::WebIde::ExtensionMarketplaceOptIn.enabled?(user: @user, marketplace_home_url: marketplace_home_url) +- user_fields = { color_mode_id: user_color_mode_id, theme: user_theme_id, gitpod_enabled: @user.gitpod_enabled, sourcegraph_enabled: @user.sourcegraph_enabled, extensions_marketplace_enabled: extensions_marketplace_enabled }.to_json - fixed_help_text = s_('Preferences|Content will be a maximum of 1280 pixels wide.') - fluid_help_text = s_('Preferences|Content will span %{percentage} of the page width.').html_safe % { percentage: '100%' } - plain_text_editor_help_text = s_('Preferences|Type in plain text, using Markdown.') diff --git a/db/migrate/20250313163310_add_extension_marketplace_opt_in_url_to_user_preference.rb b/db/migrate/20250313163310_add_extension_marketplace_opt_in_url_to_user_preference.rb new file mode 100644 index 0000000000000000000000000000000000000000..abe4b6571e1cc1296dece8f86e754a0581db4acc --- /dev/null +++ b/db/migrate/20250313163310_add_extension_marketplace_opt_in_url_to_user_preference.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class AddExtensionMarketplaceOptInUrlToUserPreference < Gitlab::Database::Migration[2.2] + milestone '17.10' + disable_ddl_transaction! + + def up + with_lock_retries do + add_column :user_preferences, :extensions_marketplace_opt_in_url, :text, null: true, if_not_exists: true + end + + # This is well above the 253 full domain name limit. We go ahead and overshoot because + # we may need to store paths in here in the future. + # https://webmasters.stackexchange.com/a/16997 + add_text_limit :user_preferences, :extensions_marketplace_opt_in_url, 512 + end + + def down + with_lock_retries do + remove_column :user_preferences, :extensions_marketplace_opt_in_url, if_exists: true + end + end +end diff --git a/db/schema_migrations/20250313163310 b/db/schema_migrations/20250313163310 new file mode 100644 index 0000000000000000000000000000000000000000..d8e5413b7d4abe5ef8a734f064754a40222f8a8e --- /dev/null +++ b/db/schema_migrations/20250313163310 @@ -0,0 +1 @@ +ee39402909b559e8c4a4bd1194513df4885bf3bee9e4b8f2392fdd125c27cf61 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 8d988c071aa853ca37dae95cc929090b87157963..1f58ddbbe943d73af848d22cdf82ad05e028744f 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -23500,8 +23500,10 @@ CREATE TABLE user_preferences ( use_work_items_view boolean DEFAULT false NOT NULL, text_editor_type smallint DEFAULT 0 NOT NULL, merge_request_dashboard_list_type smallint DEFAULT 0 NOT NULL, + extensions_marketplace_opt_in_url text, CONSTRAINT check_1d670edc68 CHECK ((time_display_relative IS NOT NULL)), CONSTRAINT check_89bf269f41 CHECK ((char_length(diffs_deletion_color) <= 7)), + CONSTRAINT check_9b50d9f942 CHECK ((char_length(extensions_marketplace_opt_in_url) <= 512)), CONSTRAINT check_b1306f8875 CHECK ((char_length(organization_groups_projects_sort) <= 64)), CONSTRAINT check_b22446f91a CHECK ((render_whitespace_in_code IS NOT NULL)), CONSTRAINT check_d07ccd35f7 CHECK ((char_length(diffs_addition_color) <= 7)), diff --git a/ee/lib/ee/web_ide/settings/extension_marketplace_metadata_generator.rb b/ee/lib/ee/web_ide/settings/extension_marketplace_metadata_generator.rb index b87c82a6bd126a59b9660b5f69dc63ecc73d447a..878cb83f3c4cc3462dcc96060538c3a9cabf71ea 100644 --- a/ee/lib/ee/web_ide/settings/extension_marketplace_metadata_generator.rb +++ b/ee/lib/ee/web_ide/settings/extension_marketplace_metadata_generator.rb @@ -20,7 +20,7 @@ def disabled_reasons end override :build_metadata_for_user - def build_metadata_for_user(user) + def build_metadata_for_user(user:, marketplace_home_url:) return metadata_disabled(:enterprise_group_disabled) unless enabled_for_enterprise_group?(user) super diff --git a/ee/spec/lib/ee/web_ide/settings/extension_marketplace_metadata_generator_spec.rb b/ee/spec/lib/ee/web_ide/settings/extension_marketplace_metadata_generator_spec.rb index 46fa65abcae089c81e2bd4e06065b351745d26c5..10c030a3f9630f3c97c4128b97e9774c6f9031c4 100644 --- a/ee/spec/lib/ee/web_ide/settings/extension_marketplace_metadata_generator_spec.rb +++ b/ee/spec/lib/ee/web_ide/settings/extension_marketplace_metadata_generator_spec.rb @@ -5,6 +5,7 @@ RSpec.describe WebIde::Settings::ExtensionMarketplaceMetadataGenerator, feature_category: :web_ide do using RSpec::Parameterized::TableSyntax + let(:marketplace_home_url) { "https://example.com" } let(:user_class) do stub_const( "User", @@ -26,7 +27,9 @@ def flipper_id user: user, vscode_extension_marketplace_feature_flag_enabled: true }, - settings: {} + settings: { + vscode_extension_marketplace_home_url: marketplace_home_url + } } end @@ -50,7 +53,8 @@ def flipper_id allow(user).to receive_messages( enterprise_user?: !!enterprise_group, enterprise_group: enterprise_group, - extensions_marketplace_opt_in_status: :unset + extensions_marketplace_opt_in_status: 'unset', + extensions_marketplace_opt_in_url: marketplace_home_url ) allow(group).to receive(:enterprise_users_extensions_marketplace_enabled?).and_return(enterprise_group_enabled) diff --git a/ee/spec/lib/web_ide/settings/settings_integration_spec.rb b/ee/spec/lib/web_ide/settings/settings_integration_spec.rb index 72219f8b1a12bc91d2cd324bd8625a5da37e1b01..19d457a520daf82a441bcaeb686502c29288426a 100644 --- a/ee/spec/lib/web_ide/settings/settings_integration_spec.rb +++ b/ee/spec/lib/web_ide/settings/settings_integration_spec.rb @@ -22,7 +22,7 @@ ) stub_licensed_features(disable_extensions_marketplace_for_enterprise_users: true) stub_application_setting(vscode_extension_marketplace: { enabled: true, preset: 'open_vsx' }) - user.update!(extensions_marketplace_enabled: true) + user.update!(extensions_marketplace_opt_in_status: "enabled") end describe "default - enterprise group has extensions marketplace disabled" do diff --git a/lib/web_ide/extension_marketplace_opt_in.rb b/lib/web_ide/extension_marketplace_opt_in.rb new file mode 100644 index 0000000000000000000000000000000000000000..45c37de30207101bf720a5e91c0ea8c7d75a66f5 --- /dev/null +++ b/lib/web_ide/extension_marketplace_opt_in.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module WebIde + class ExtensionMarketplaceOptIn + def self.opt_in_status(user:, marketplace_home_url:) + return 'unset' unless user && marketplace_home_url + return 'unset' unless user.extensions_marketplace_opt_in_url == marketplace_home_url + + user.extensions_marketplace_opt_in_status + end + + def self.enabled?(user:, marketplace_home_url:) + status = opt_in_status(user: user, marketplace_home_url: marketplace_home_url) + + status == 'enabled' + end + + def self.params(enabled:, marketplace_home_url:) + status = ::Gitlab::Utils.to_boolean(enabled) ? 'enabled' : 'disabled' + + { + extensions_marketplace_opt_in_status: status, + extensions_marketplace_opt_in_url: marketplace_home_url + } + end + end +end diff --git a/lib/web_ide/settings/extension_marketplace_metadata_generator.rb b/lib/web_ide/settings/extension_marketplace_metadata_generator.rb index 40fe374c4129f70303868fe58b7afc5bd671e273..fc53e9e1d06dab5bfa02861e17a46bba63309bb9 100644 --- a/lib/web_ide/settings/extension_marketplace_metadata_generator.rb +++ b/lib/web_ide/settings/extension_marketplace_metadata_generator.rb @@ -23,7 +23,12 @@ class ExtensionMarketplaceMetadataGenerator def self.generate(context) return context unless context.fetch(:requested_setting_names).include?(:vscode_extension_marketplace_metadata) - context => { options: Hash => options } + context => { + options: Hash => options, + settings: { + vscode_extension_marketplace_home_url: String => marketplace_home_url, + } + } options_with_defaults = { user: nil, vscode_extension_marketplace_feature_flag_enabled: nil }.merge(options) options_with_defaults => { user: ::User | NilClass => user, @@ -33,7 +38,8 @@ def self.generate(context) extension_marketplace_metadata = build_metadata( user: user, - flag_enabled: extension_marketplace_feature_flag_enabled + flag_enabled: extension_marketplace_feature_flag_enabled, + marketplace_home_url: marketplace_home_url ) context[:settings][:vscode_extension_marketplace_metadata] = extension_marketplace_metadata @@ -43,7 +49,7 @@ def self.generate(context) # @param [User, nil] user # @param [Boolean, nil] flag_enabled # @return [Hash] - def self.build_metadata(user:, flag_enabled:) + def self.build_metadata(user:, flag_enabled:, marketplace_home_url:) return metadata_disabled(:no_user) unless user return metadata_disabled(:no_flag) if flag_enabled.nil? return metadata_disabled(:instance_disabled) unless flag_enabled @@ -52,7 +58,7 @@ def self.build_metadata(user:, flag_enabled:) return metadata_disabled(:instance_disabled) end - build_metadata_for_user(user) + build_metadata_for_user(user: user, marketplace_home_url: marketplace_home_url) end def self.disabled_reasons @@ -63,9 +69,12 @@ def self.disabled_reasons # # @param [User] user # @return [Hash] - def self.build_metadata_for_user(user) + def self.build_metadata_for_user(user:, marketplace_home_url:) # noinspection RubyNilAnalysis -- RubyMine doesn't realize user can't be nil because of guard clause above - opt_in_status = user.extensions_marketplace_opt_in_status.to_sym + opt_in_status = ::WebIde::ExtensionMarketplaceOptIn.opt_in_status( + user: user, + marketplace_home_url: marketplace_home_url + ).to_sym case opt_in_status when :enabled diff --git a/lib/web_ide/settings/settings_initializer.rb b/lib/web_ide/settings/settings_initializer.rb index 26ae8b2e63fe04da936a18b35b4ff20d6c5aa6be..7e358cb183865af6d326b81ad463b597a36b32f1 100644 --- a/lib/web_ide/settings/settings_initializer.rb +++ b/lib/web_ide/settings/settings_initializer.rb @@ -8,6 +8,9 @@ class SettingsInitializer :vscode_extension_marketplace_metadata, :vscode_extension_marketplace ], + vscode_extension_marketplace_metadata: [ + :vscode_extension_marketplace_home_url + ], vscode_extension_marketplace_home_url: [ :vscode_extension_marketplace ] diff --git a/spec/controllers/profiles/preferences_controller_spec.rb b/spec/controllers/profiles/preferences_controller_spec.rb index 504c75aaf532a20d0a599604421e24ea794d3403..38607255a3e2fc4d0757e0c15126bbe8bb22d1d3 100644 --- a/spec/controllers/profiles/preferences_controller_spec.rb +++ b/spec/controllers/profiles/preferences_controller_spec.rb @@ -65,7 +65,13 @@ def go(params: {}, format: :json) extensions_marketplace_enabled: '1' }.with_indifferent_access - expect(user).to receive(:assign_attributes).with(ActionController::Parameters.new(prefs).permit!) + expected_params = prefs.except(:extensions_marketplace_enabled).merge( + extensions_marketplace_opt_in_status: "enabled", + # Default marketplace_home_url based on Open VSX + extensions_marketplace_opt_in_url: "https://open-vsx.org" + ) + + expect(user).to receive(:assign_attributes).with(ActionController::Parameters.new(expected_params).permit!) expect(user).to receive(:save) go params: prefs diff --git a/spec/lib/web_ide/extension_marketplace_opt_in_spec.rb b/spec/lib/web_ide/extension_marketplace_opt_in_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..be27cd1536391501736ea279bbd7236cc9dcbfeb --- /dev/null +++ b/spec/lib/web_ide/extension_marketplace_opt_in_spec.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' + +RSpec.describe WebIde::ExtensionMarketplaceOptIn, feature_category: :web_ide do + using RSpec::Parameterized::TableSyntax + + let(:user_class) do + stub_const("User", Struct.new( + :extensions_marketplace_opt_in_status, + :extensions_marketplace_opt_in_url, + keyword_init: true + )) + end + + let(:user) do + User.new( + extensions_marketplace_opt_in_status: opt_in_status, + extensions_marketplace_opt_in_url: opt_in_url + ) + end + + describe '.opt_in_status' do + subject(:opt_in_status) do + described_class.opt_in_status(user: user, marketplace_home_url: marketplace_home_url) + end + + where(:opt_in_status, :opt_in_url, :marketplace_home_url, :expectation) do + 'enabled' | 'https://example.com' | nil | 'unset' + 'enabled' | 'https://example.com' | 'https://open-vsx.org' | 'unset' + 'enabled' | 'https://open-vsx.org' | 'https://open-vsx.org' | 'enabled' + 'disabled' | 'https://open-vsx.org' | 'https://open-vsx.org' | 'disabled' + 'unset' | 'https://open-vsx.org' | 'https://open-vsx.org' | 'unset' + end + + with_them do + it { is_expected.to eq(expectation) } + end + end + + describe '.enabled?' do + subject(:enabled) do + described_class.enabled?(user: user, marketplace_home_url: marketplace_home_url) + end + + where(:opt_in_status, :opt_in_url, :marketplace_home_url, :expectation) do + 'enabled' | 'https://open-vsx.org' | nil | false + 'enabled' | 'https://open-vsx.org' | 'https://example.com' | false + 'enabled' | 'https://example.com' | 'https://example.com' | true + 'disabled' | 'https://example.com' | 'https://example.com' | false + 'unset' | 'https://example.com' | 'https://example.com' | false + end + + with_them do + it { is_expected.to eq(expectation) } + end + end + + describe '.params' do + subject(:params) do + described_class.params(enabled: enabled, marketplace_home_url: marketplace_home_url) + end + + where(:enabled, :marketplace_home_url, :expected_status) do + true | 'https://example.com' | 'enabled' + false | 'https://example.com' | 'disabled' + end + + with_them do + it 'returns params for updating user_preferences' do + is_expected.to match( + extensions_marketplace_opt_in_status: expected_status, + extensions_marketplace_opt_in_url: marketplace_home_url + ) + end + end + end +end diff --git a/spec/lib/web_ide/extension_marketplace_spec.rb b/spec/lib/web_ide/extension_marketplace_spec.rb index c3a001a8332247cb4bee66bb2f92b97d2eef8918..7d542d4c0724e6d56d1ac4334b1af6781c86391a 100644 --- a/spec/lib/web_ide/extension_marketplace_spec.rb +++ b/spec/lib/web_ide/extension_marketplace_spec.rb @@ -7,6 +7,7 @@ let(:help_url) { "/help/user/project/web_ide/_index.md#extension-marketplace" } let(:user_preferences_url) { "/-/profile/preferences#integrations" } + let(:custom_home_url) { 'https://example.com:8444' } let(:custom_app_setting) do { enabled: true, @@ -109,15 +110,17 @@ describe '#webide_extension_marketplace_settings' do # rubocop:disable Layout/LineLength -- last parameter extens past line but is preferable to rubocop's suggestion - where(:web_ide_extensions_marketplace, :vscode_extension_marketplace_settings, :app_setting, :opt_in_status, :expectation) do - true | false | {} | :enabled | lazy { { enabled: true, vscode_settings: ::WebIde::ExtensionMarketplacePreset.open_vsx.values } } - true | false | {} | :unset | lazy { { enabled: false, reason: :opt_in_unset, help_url: /#{help_url}/, user_preferences_url: /#{user_preferences_url}/ } } - true | false | {} | :disabled | lazy { { enabled: false, reason: :opt_in_disabled, help_url: /#{help_url}/, user_preferences_url: /#{user_preferences_url}/ } } - false | false | {} | :enabled | lazy { { enabled: false, reason: :instance_disabled, help_url: /#{help_url}/ } } - true | true | {} | :enabled | lazy { { enabled: false, reason: :instance_disabled, help_url: /#{help_url}/ } } - true | true | { enabled: false } | :enabled | lazy { { enabled: false, reason: :instance_disabled, help_url: /#{help_url}/ } } - true | true | ref(:custom_app_setting) | :enabled | lazy { { enabled: true, vscode_settings: custom_app_setting[:custom_values] } } - true | true | ref(:open_vsx_app_setting) | :enabled | lazy { { enabled: true, vscode_settings: ::WebIde::ExtensionMarketplacePreset.open_vsx.values } } + where(:web_ide_extensions_marketplace, :vscode_extension_marketplace_settings, :app_setting, :opt_in_status, :opt_in_url, :expectation) do + # web_ide_extensions_marketplace | vscode_extension_marketplace_settings | app_setting | opt_in_status | opt_in_url | expectation + true | false | {} | :enabled | nil | lazy { { enabled: true, vscode_settings: ::WebIde::ExtensionMarketplacePreset.open_vsx.values } } + true | false | {} | :unset | nil | lazy { { enabled: false, reason: :opt_in_unset, help_url: /#{help_url}/, user_preferences_url: /#{user_preferences_url}/ } } + true | false | {} | :disabled | nil | lazy { { enabled: false, reason: :opt_in_disabled, help_url: /#{help_url}/, user_preferences_url: /#{user_preferences_url}/ } } + false | false | {} | :enabled | nil | lazy { { enabled: false, reason: :instance_disabled, help_url: /#{help_url}/ } } + true | true | {} | :enabled | nil | lazy { { enabled: false, reason: :instance_disabled, help_url: /#{help_url}/ } } + true | true | { enabled: false } | :enabled | nil | lazy { { enabled: false, reason: :instance_disabled, help_url: /#{help_url}/ } } + true | true | ref(:custom_app_setting) | :enabled | nil | lazy { { enabled: false, reason: :opt_in_unset, help_url: /#{help_url}/, user_preferences_url: /#{user_preferences_url}/ } } + true | true | ref(:custom_app_setting) | :enabled | ref(:custom_home_url) | lazy { { enabled: true, vscode_settings: custom_app_setting[:custom_values] } } + true | true | ref(:open_vsx_app_setting) | :enabled | nil | lazy { { enabled: true, vscode_settings: ::WebIde::ExtensionMarketplacePreset.open_vsx.values } } end # rubocop:enable Layout/LineLength @@ -132,7 +135,10 @@ stub_application_setting(vscode_extension_marketplace: app_setting) - current_user.update!(extensions_marketplace_opt_in_status: opt_in_status) + current_user.update!( + extensions_marketplace_opt_in_status: opt_in_status, + extensions_marketplace_opt_in_url: opt_in_url + ) end with_them do diff --git a/spec/lib/web_ide/settings/extension_marketplace_metadata_generator_spec.rb b/spec/lib/web_ide/settings/extension_marketplace_metadata_generator_spec.rb index 6f768dce9f5b221e2e3e423831491452be9036d3..aae02ae3f6f12a9366df4a14419895a05bda5df7 100644 --- a/spec/lib/web_ide/settings/extension_marketplace_metadata_generator_spec.rb +++ b/spec/lib/web_ide/settings/extension_marketplace_metadata_generator_spec.rb @@ -5,6 +5,7 @@ RSpec.describe WebIde::Settings::ExtensionMarketplaceMetadataGenerator, feature_category: :web_ide do using RSpec::Parameterized::TableSyntax + let(:marketplace_home_url) { "https://example.com" } let(:input_context) do { requested_setting_names: [:vscode_extension_marketplace_metadata], @@ -13,6 +14,7 @@ # NOTE: default value of 'vscode_extension_marketplace_metadata' is an empty hash. Include it here to # ensure that it always gets overwritten with the generated value vscode_extension_marketplace_metadata: {}, + vscode_extension_marketplace_home_url: marketplace_home_url, some_other_existing_setting_that_should_not_be_overwritten: "some context" } } @@ -90,7 +92,10 @@ def flipper_id end before do - allow(user).to receive(:extensions_marketplace_opt_in_status) { opt_in_status.to_s } + allow(::WebIde::ExtensionMarketplaceOptIn).to receive(:opt_in_status) + .with(user: user, marketplace_home_url: marketplace_home_url) + .and_return(opt_in_status.to_s) + # EE feature has to be stubbed since we run EE code through CE tests allow(user).to receive(:enterprise_user?).and_return(false) allow(enums).to receive(:statuses).and_return({ unset: :unset, enabled: :enabled, disabled: :disabled }) diff --git a/spec/models/user_preference_spec.rb b/spec/models/user_preference_spec.rb index d11be69a4710e2f5e6c5b6d0810485b027fe1e45..5e4afb2b0c041efa86dd9df4333d8902f613da45 100644 --- a/spec/models/user_preference_spec.rb +++ b/spec/models/user_preference_spec.rb @@ -5,6 +5,7 @@ RSpec.describe UserPreference, feature_category: :user_profile do let_it_be(:user) { create(:user) } + let(:marketplace_home_url) { 'https://open-vsx.org' } let(:user_preference) { create(:user_preference, user: user) } describe 'validations' do @@ -82,6 +83,10 @@ end end + describe 'extensions_marketplace_opt_in_url' do + it { is_expected.to validate_length_of(:extensions_marketplace_opt_in_url).is_at_most(512) } + end + describe 'organization_groups_projects_display' do it 'is set to 0 by default' do pref = described_class.new @@ -254,41 +259,20 @@ end end - describe '#extensions_marketplace_enabled' do - where(:opt_in_status, :expected_value) do + describe '#extensions_marketplace_opt_in_url' do + where(:opt_in_url, :expectation) do [ - ["enabled", true], - ["disabled", false], - ["unset", false] + [nil, 'https://open-vsx.org'], + ['https://open-vsx.org', 'https://open-vsx.org'], + ['https://example.com', 'https://example.com'] ] end with_them do - it 'returns boolean from extensions_marketplace_opt_in_status' do - user_preference.update!(extensions_marketplace_opt_in_status: opt_in_status) - - expect(user_preference.extensions_marketplace_enabled).to be expected_value - end - end - end - - describe '#extensions_marketplace_enabled=' do - where(:value, :expected_opt_in_status) do - [ - [true, "enabled"], - [false, "disabled"], - [0, "disabled"], - [1, "enabled"] - ] - end - - with_them do - it 'updates extensions_marketplace_opt_in_status' do - user_preference.update!(extensions_marketplace_opt_in_status: 'unset') - - user_preference.extensions_marketplace_enabled = value + it 'reads attribute and defaults when nil' do + user_preference.update!(extensions_marketplace_opt_in_url: opt_in_url) - expect(user_preference.extensions_marketplace_opt_in_status).to be expected_opt_in_status + expect(user_preference.extensions_marketplace_opt_in_url).to eq expectation end end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 75c554714d962155c7b480020ab94567afe63f94..755a256172ab1ea0b4b8e0bb137697fd63d1c750 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -89,8 +89,11 @@ it { is_expected.to delegate_method(:use_new_navigation).to(:user_preference) } it { is_expected.to delegate_method(:use_new_navigation=).to(:user_preference).with_arguments(:args) } - it { is_expected.to delegate_method(:extensions_marketplace_enabled).to(:user_preference) } - it { is_expected.to delegate_method(:extensions_marketplace_enabled=).to(:user_preference).with_arguments(:args) } + it { is_expected.to delegate_method(:extensions_marketplace_opt_in_status).to(:user_preference) } + it { is_expected.to delegate_method(:extensions_marketplace_opt_in_status=).to(:user_preference).with_arguments(:args) } + + it { is_expected.to delegate_method(:extensions_marketplace_opt_in_url).to(:user_preference) } + it { is_expected.to delegate_method(:extensions_marketplace_opt_in_url=).to(:user_preference).with_arguments(:args) } it { is_expected.to delegate_method(:pinned_nav_items).to(:user_preference) } it { is_expected.to delegate_method(:pinned_nav_items=).to(:user_preference).with_arguments(:args) } diff --git a/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb b/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb index d7f852b722ed0eda55d621315e633549b44389b6..85be19c0027ca8fc6f3db235cdf7d5385bf2ba1f 100644 --- a/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb +++ b/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb @@ -25,6 +25,18 @@ let(:mutation) { graphql_mutation(:userPreferencesUpdate, input) } let(:mutation_response) { graphql_mutation_response(:userPreferencesUpdate) } + before do + stub_application_setting(vscode_extension_marketplace: { + enabled: false, + preset: 'custom', + custom_values: { + item_url: 'https://example.com/item/url', + service_url: 'https://example.com/service/url', + resource_url_template: 'https://example.com/resource/url/template' + } + }) + end + context 'when user has no existing preference' do it 'creates the user preference record' do post_graphql_mutation(mutation, current_user: current_user) @@ -41,6 +53,7 @@ expect(current_user.user_preference.persisted?).to eq(true) expect(current_user.user_preference.extensions_marketplace_opt_in_status).to eq('enabled') + expect(current_user.user_preference.extensions_marketplace_opt_in_url).to eq('https://example.com') expect(current_user.user_preference.issues_sort).to eq(Types::IssueSortEnum.values[sort_value].value.to_s) expect(current_user.user_preference.visibility_pipeline_id_type).to eq('iid') expect(current_user.user_preference.use_work_items_view).to eq(true)