Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/ci/rails/rspec-foss-impact.gitlab-ci.yml.erb2
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb11
-rw-r--r--app/models/hooks/project_hook.rb7
-rw-r--r--app/models/hooks/web_hook.rb6
-rw-r--r--app/models/user.rb6
-rw-r--r--app/models/user_detail.rb40
-rw-r--r--app/policies/issuable_policy.rb6
-rw-r--r--app/policies/todo_policy.rb2
-rw-r--r--app/services/web_hooks/log_execution_service.rb2
-rw-r--r--config/feature_flags/development/gitlab_shell_jwt_token.yml8
-rw-r--r--db/migrate/20220802200719_add_user_details_profile_fields.rb17
-rw-r--r--db/migrate/20220802202505_add_user_details_field_limits.rb25
-rw-r--r--db/migrate/20221005103010_add_index_project_id_on_scan_finding_approval_project_rules.rb22
-rw-r--r--db/schema_migrations/202208022007191
-rw-r--r--db/schema_migrations/202208022025051
-rw-r--r--db/schema_migrations/202210051030101
-rw-r--r--db/structure.sql16
-rw-r--r--doc/api/graphql/reference/index.md52
-rw-r--r--doc/development/database/efficient_in_operator_queries.md58
-rw-r--r--doc/development/feature_development.md6
-rw-r--r--doc/development/value_stream_analytics.md1
-rw-r--r--lib/api/ci/variables.rb11
-rw-r--r--lib/api/helpers.rb21
-rw-r--r--lib/api/projects.rb2
-rw-r--r--lib/api/settings.rb2
-rw-r--r--lib/api/users.rb4
-rw-r--r--lib/api/wikis.rb2
-rw-r--r--lib/gitlab/auth/o_auth/user.rb15
-rw-r--r--lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml1
-rw-r--r--lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml1
-rw-r--r--lib/gitlab/ci/templates/Pages/SwaggerUI.gitlab-ci.yml1
-rw-r--r--qa/qa/specs/features/api/1_manage/integrations/webhook_events_spec.rb (renamed from qa/qa/specs/features/api/3_create/integrations/webhook_events_spec.rb)7
-rw-r--r--qa/qa/specs/features/api/1_manage/rate_limits_spec.rb2
-rw-r--r--qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb2
-rw-r--r--qa/qa/specs/features/api/1_manage/users_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/integrations/jenkins/jenkins_build_status_spec.rb (renamed from qa/qa/specs/features/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb)10
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/integrations/jira/jira_basic_integration_spec.rb (renamed from qa/qa/specs/features/browser_ui/3_create/jira/jira_basic_integration_spec.rb)15
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/integrations/jira/jira_issue_import_spec.rb (renamed from qa/qa/specs/features/browser_ui/2_plan/issue/jira_issue_import_spec.rb)4
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/integrations/pipeline_status_emails_spec.rb (renamed from qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_status_emails_spec.rb)2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/create_project_badge_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/invite_group_to_project_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/project_owner_permissions_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/user/follow_user_activity_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/user/parent_group_access_termination_spec.rb (renamed from qa/qa/specs/features/browser_ui/1_manage/user/user_access_termination_spec.rb)19
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/user/user_inherited_access_spec.rb2
-rwxr-xr-xscripts/packages/automated_cleanup.rb6
-rwxr-xr-xscripts/review_apps/automated_cleanup.rb6
-rw-r--r--spec/lib/api/helpers_spec.rb37
-rw-r--r--spec/lib/gitlab/auth/o_auth/user_spec.rb44
-rw-r--r--spec/lib/gitlab/pagination/keyset/in_operator_optimization/query_builder_spec.rb144
-rw-r--r--spec/models/hooks/web_hook_spec.rb10
-rw-r--r--spec/models/user_detail_spec.rb135
-rw-r--r--spec/models/user_spec.rb35
-rw-r--r--spec/policies/issuable_policy_spec.rb4
-rw-r--r--spec/requests/api/internal/base_spec.rb18
-rw-r--r--spec/services/web_hooks/log_execution_service_spec.rb8
-rw-r--r--workhorse/go.mod2
-rw-r--r--workhorse/go.sum4
64 files changed, 679 insertions, 209 deletions
diff --git a/.gitlab/ci/rails/rspec-foss-impact.gitlab-ci.yml.erb b/.gitlab/ci/rails/rspec-foss-impact.gitlab-ci.yml.erb
index eae144634c6..4ae4cb75a25 100644
--- a/.gitlab/ci/rails/rspec-foss-impact.gitlab-ci.yml.erb
+++ b/.gitlab/ci/rails/rspec-foss-impact.gitlab-ci.yml.erb
@@ -42,7 +42,7 @@ rspec foss-impact:
<% end %>
script:
- !reference [.base-script, script]
- - rspec_paralellized_job "--tag ~quarantine"
+ - rspec_paralellized_job "--tag ~quarantine --tag ~level:migration"
artifacts:
expire_in: 7d
paths:
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 92f06ddc9eb..0c4434b1d80 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-d897d27c602d80b247af46a4ce672c2cd9b591ba
+b72279fe5192513468d8fdc0648dc698231b7534
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index 817f272d458..f3f0ddd968a 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -181,6 +181,8 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
end
accept_pending_invitations(user: user) if new_user
+ persist_accepted_terms_if_required(user) if new_user
+
store_after_sign_up_path_for_user if intent_to_register?
sign_in_and_redirect(user, event: :authentication)
end
@@ -301,6 +303,15 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
redirect_to new_admin_session_path, alert: _('Invalid login or password')
end
+ def persist_accepted_terms_if_required(user)
+ return unless Feature.enabled?(:update_oauth_registration_flow)
+ return unless user.persisted?
+ return unless Gitlab::CurrentSettings.current_application_settings.enforce_terms?
+
+ terms = ApplicationSetting::Term.latest
+ Users::RespondToTermsService.new(user, terms).execute(accepted: true)
+ end
+
def store_after_sign_up_path_for_user
store_location_for(:user, users_sign_up_welcome_path)
end
diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb
index bcbf43ee38b..dcba136d163 100644
--- a/app/models/hooks/project_hook.rb
+++ b/app/models/hooks/project_hook.rb
@@ -55,13 +55,6 @@ class ProjectHook < WebHook
redis.set(key, time) if !prev || prev < time
end
end
-
- private
-
- override :web_hooks_disable_failed?
- def web_hooks_disable_failed?
- Feature.enabled?(:web_hooks_disable_failed, project)
- end
end
ProjectHook.prepend_mod_with('ProjectHook')
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index ed04b0c3d1f..71794964c99 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -53,6 +53,10 @@ class WebHook < ApplicationRecord
where('recent_failures > ? OR disabled_until >= ?', FAILURE_THRESHOLD, Time.current)
end
+ def self.web_hooks_disable_failed?(hook)
+ Feature.enabled?(:web_hooks_disable_failed, hook.parent)
+ end
+
def executable?
!temporarily_disabled? && !permanently_disabled?
end
@@ -197,7 +201,7 @@ class WebHook < ApplicationRecord
private
def web_hooks_disable_failed?
- Feature.enabled?(:web_hooks_disable_failed)
+ self.class.web_hooks_disable_failed?(self)
end
def initialize_url_variables
diff --git a/app/models/user.rb b/app/models/user.rb
index 16ed3205d93..74035414970 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -301,6 +301,7 @@ class User < ApplicationRecord
before_save :check_for_verified_email, if: ->(user) { user.email_changed? && !user.new_record? }
before_validation :ensure_namespace_correct
before_save :ensure_namespace_correct # in case validation is skipped
+ before_save :ensure_user_detail_assigned
after_validation :set_username_errors
after_update :username_changed_hook, if: :saved_change_to_username?
after_destroy :post_destroy_hook
@@ -1589,6 +1590,11 @@ class User < ApplicationRecord
end
end
+ # Temporary, will be removed when user_detail fields are fully migrated
+ def ensure_user_detail_assigned
+ user_detail.assign_changed_fields_from_user if UserDetail.user_fields_changed?(self)
+ end
+
def set_username_errors
namespace_path_errors = self.errors.delete(:"namespace.path")
diff --git a/app/models/user_detail.rb b/app/models/user_detail.rb
index 3787ad1c380..2e662faea6a 100644
--- a/app/models/user_detail.rb
+++ b/app/models/user_detail.rb
@@ -12,15 +12,55 @@ class UserDetail < ApplicationRecord
validates :job_title, length: { maximum: 200 }
validates :bio, length: { maximum: 255 }, allow_blank: true
+ DEFAULT_FIELD_LENGTH = 500
+
+ validates :linkedin, length: { maximum: DEFAULT_FIELD_LENGTH }, allow_blank: true
+ validates :twitter, length: { maximum: DEFAULT_FIELD_LENGTH }, allow_blank: true
+ validates :skype, length: { maximum: DEFAULT_FIELD_LENGTH }, allow_blank: true
+ validates :location, length: { maximum: DEFAULT_FIELD_LENGTH }, allow_blank: true
+ validates :organization, length: { maximum: DEFAULT_FIELD_LENGTH }, allow_blank: true
+ validates :website_url, length: { maximum: DEFAULT_FIELD_LENGTH }, url: true, allow_blank: true
+
+ before_validation :sanitize_attrs
before_save :prevent_nil_bio
enum registration_objective: REGISTRATION_OBJECTIVE_PAIRS, _suffix: true
+ def self.user_fields_changed?(user)
+ (%w[linkedin skype twitter website_url location organization] & user.changed).any?
+ end
+
+ def sanitize_attrs
+ %i[linkedin skype twitter website_url].each do |attr|
+ value = self[attr]
+ self[attr] = Sanitize.clean(value) if value.present?
+ end
+ %i[location organization].each do |attr|
+ value = self[attr]
+ self[attr] = Sanitize.clean(value).gsub('&amp;', '&') if value.present?
+ end
+ end
+
+ def assign_changed_fields_from_user
+ self.linkedin = trim_field(user.linkedin) if user.linkedin_changed?
+ self.twitter = trim_field(user.twitter) if user.twitter_changed?
+ self.skype = trim_field(user.skype) if user.skype_changed?
+ self.website_url = trim_field(user.website_url) if user.website_url_changed?
+ self.location = trim_field(user.location) if user.location_changed?
+ self.organization = trim_field(user.organization) if user.organization_changed?
+ end
+
private
def prevent_nil_bio
self.bio = '' if bio_changed? && bio.nil?
end
+
+ def trim_field(value)
+ return '' unless value
+
+ value.first(DEFAULT_FIELD_LENGTH)
+ end
end
UserDetail.prepend_mod_with('UserDetail')
diff --git a/app/policies/issuable_policy.rb b/app/policies/issuable_policy.rb
index a412c97b219..df065b24e64 100644
--- a/app/policies/issuable_policy.rb
+++ b/app/policies/issuable_policy.rb
@@ -22,12 +22,6 @@ class IssuablePolicy < BasePolicy
enable :reopen_issue
end
- # This rule replicates permissions in NotePolicy#can_read_confidential and it's used in
- # TodoPolicy for performance reasons
- rule { can?(:reporter_access) | assignee_or_author | admin }.policy do
- enable :read_confidential_notes
- end
-
rule { can?(:read_merge_request) & assignee_or_author }.policy do
enable :update_merge_request
enable :reopen_merge_request
diff --git a/app/policies/todo_policy.rb b/app/policies/todo_policy.rb
index 5c24964f24a..d63eb9407f8 100644
--- a/app/policies/todo_policy.rb
+++ b/app/policies/todo_policy.rb
@@ -16,7 +16,7 @@ class TodoPolicy < BasePolicy
desc "User can read the todo's confidential note"
condition(:can_read_todo_confidential_note) do
- @user && @user.can?(:read_confidential_notes, @subject.target)
+ @user && @user.can?(:read_internal_note, @subject.target)
end
rule { own_todo & can_read_target }.enable :read_todo
diff --git a/app/services/web_hooks/log_execution_service.rb b/app/services/web_hooks/log_execution_service.rb
index 5be8aee3ae8..1a40c877bda 100644
--- a/app/services/web_hooks/log_execution_service.rb
+++ b/app/services/web_hooks/log_execution_service.rb
@@ -17,7 +17,7 @@ module WebHooks
end
def execute
- update_hook_failure_state
+ update_hook_failure_state if WebHook.web_hooks_disable_failed?(hook)
log_execution
end
diff --git a/config/feature_flags/development/gitlab_shell_jwt_token.yml b/config/feature_flags/development/gitlab_shell_jwt_token.yml
deleted file mode 100644
index ac12ff8df47..00000000000
--- a/config/feature_flags/development/gitlab_shell_jwt_token.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: gitlab_shell_jwt_token
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86148
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/360808
-milestone: '15.3'
-type: development
-group: group::source code
-default_enabled: true
diff --git a/db/migrate/20220802200719_add_user_details_profile_fields.rb b/db/migrate/20220802200719_add_user_details_profile_fields.rb
new file mode 100644
index 00000000000..952b0bc1d9a
--- /dev/null
+++ b/db/migrate/20220802200719_add_user_details_profile_fields.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddUserDetailsProfileFields < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ # rubocop:disable Migration/AddLimitToTextColumns
+ # limits are added in 20220802202505_add_user_details_field_limits
+ def change
+ add_column :user_details, :linkedin, :text, null: false, default: ''
+ add_column :user_details, :twitter, :text, null: false, default: ''
+ add_column :user_details, :skype, :text, null: false, default: ''
+ add_column :user_details, :website_url, :text, null: false, default: ''
+ add_column :user_details, :location, :text, null: false, default: ''
+ add_column :user_details, :organization, :text, null: false, default: ''
+ end
+ # rubocop:enable Migration/AddLimitToTextColumns
+end
diff --git a/db/migrate/20220802202505_add_user_details_field_limits.rb b/db/migrate/20220802202505_add_user_details_field_limits.rb
new file mode 100644
index 00000000000..83b6a4528d3
--- /dev/null
+++ b/db/migrate/20220802202505_add_user_details_field_limits.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class AddUserDetailsFieldLimits < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ USER_DETAILS_FIELD_LIMIT = 500
+
+ def up
+ add_text_limit :user_details, :linkedin, USER_DETAILS_FIELD_LIMIT
+ add_text_limit :user_details, :twitter, USER_DETAILS_FIELD_LIMIT
+ add_text_limit :user_details, :skype, USER_DETAILS_FIELD_LIMIT
+ add_text_limit :user_details, :website_url, USER_DETAILS_FIELD_LIMIT
+ add_text_limit :user_details, :location, USER_DETAILS_FIELD_LIMIT
+ add_text_limit :user_details, :organization, USER_DETAILS_FIELD_LIMIT
+ end
+
+ def down
+ remove_text_limit :user_details, :linkedin
+ remove_text_limit :user_details, :twitter
+ remove_text_limit :user_details, :skype
+ remove_text_limit :user_details, :website_url
+ remove_text_limit :user_details, :location
+ remove_text_limit :user_details, :organization
+ end
+end
diff --git a/db/migrate/20221005103010_add_index_project_id_on_scan_finding_approval_project_rules.rb b/db/migrate/20221005103010_add_index_project_id_on_scan_finding_approval_project_rules.rb
new file mode 100644
index 00000000000..990188aa418
--- /dev/null
+++ b/db/migrate/20221005103010_add_index_project_id_on_scan_finding_approval_project_rules.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class AddIndexProjectIdOnScanFindingApprovalProjectRules < Gitlab::Database::Migration[2.0]
+ INDEX_NAME_ALL = 'scan_finding_approval_project_rule_index_project_id'
+ INDEX_NAME_28D = 'scan_finding_approval_project_rule_index_created_at_project_id'
+ SCAN_FINDING_REPORT_TYPE = 4
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :approval_project_rules, %i[created_at project_id],
+ where: "report_type = #{SCAN_FINDING_REPORT_TYPE}", name: INDEX_NAME_28D
+
+ add_concurrent_index :approval_project_rules, :project_id,
+ where: "report_type = #{SCAN_FINDING_REPORT_TYPE}", name: INDEX_NAME_ALL
+ end
+
+ def down
+ remove_concurrent_index_by_name :approval_project_rules, INDEX_NAME_ALL
+ remove_concurrent_index_by_name :approval_project_rules, INDEX_NAME_28D
+ end
+end
diff --git a/db/schema_migrations/20220802200719 b/db/schema_migrations/20220802200719
new file mode 100644
index 00000000000..a4798a7e79e
--- /dev/null
+++ b/db/schema_migrations/20220802200719
@@ -0,0 +1 @@
+e27e9430a06f0586dd4ed9419c8f5747d64509e89f4f3481d8341261cfec82c5 \ No newline at end of file
diff --git a/db/schema_migrations/20220802202505 b/db/schema_migrations/20220802202505
new file mode 100644
index 00000000000..f6a72f8c3c5
--- /dev/null
+++ b/db/schema_migrations/20220802202505
@@ -0,0 +1 @@
+aacf5e416de30f594ea012c3dd3cb1a3bd10d2edd2d312aedf7147a8cde32928 \ No newline at end of file
diff --git a/db/schema_migrations/20221005103010 b/db/schema_migrations/20221005103010
new file mode 100644
index 00000000000..9bf2bba87ca
--- /dev/null
+++ b/db/schema_migrations/20221005103010
@@ -0,0 +1 @@
+85deb0dcce9b1d43b3c978f2615afabe1f29304f65cf01a82d9b116d3276337f \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 15af16ada65..960c736090d 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -22092,7 +22092,19 @@ CREATE TABLE user_details (
phone text,
requires_credit_card_verification boolean DEFAULT false NOT NULL,
password_last_changed_at timestamp with time zone,
+ linkedin text DEFAULT ''::text NOT NULL,
+ twitter text DEFAULT ''::text NOT NULL,
+ skype text DEFAULT ''::text NOT NULL,
+ website_url text DEFAULT ''::text NOT NULL,
+ location text DEFAULT ''::text NOT NULL,
+ organization text DEFAULT ''::text NOT NULL,
CONSTRAINT check_245664af82 CHECK ((char_length(webauthn_xid) <= 100)),
+ CONSTRAINT check_444573ee52 CHECK ((char_length(skype) <= 500)),
+ CONSTRAINT check_466a25be35 CHECK ((char_length(twitter) <= 500)),
+ CONSTRAINT check_7b246dad73 CHECK ((char_length(organization) <= 500)),
+ CONSTRAINT check_7d6489f8f3 CHECK ((char_length(linkedin) <= 500)),
+ CONSTRAINT check_7fe2044093 CHECK ((char_length(website_url) <= 500)),
+ CONSTRAINT check_8a7fcf8a60 CHECK ((char_length(location) <= 500)),
CONSTRAINT check_a73b398c60 CHECK ((char_length(phone) <= 50)),
CONSTRAINT check_eeeaf8d4f0 CHECK ((char_length(pronouns) <= 50)),
CONSTRAINT check_f932ed37db CHECK ((char_length(pronunciation) <= 255))
@@ -30972,6 +30984,10 @@ CREATE INDEX partial_index_user_id_app_id_created_at_token_not_revoked ON oauth_
CREATE INDEX scan_finding_approval_mr_rule_index_merge_request_id ON approval_merge_request_rules USING btree (merge_request_id) WHERE (report_type = 4);
+CREATE INDEX scan_finding_approval_project_rule_index_created_at_project_id ON approval_project_rules USING btree (created_at, project_id) WHERE (report_type = 4);
+
+CREATE INDEX scan_finding_approval_project_rule_index_project_id ON approval_project_rules USING btree (project_id) WHERE (report_type = 4);
+
CREATE INDEX security_findings_confidence_idx ON ONLY security_findings USING btree (confidence);
CREATE INDEX security_findings_project_fingerprint_idx ON ONLY security_findings USING btree (project_fingerprint);
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index f6a4cf46b6c..544251668f9 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -13019,6 +13019,21 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="groupepicsupdatedafter"></a>`updatedAfter` | [`Time`](#time) | Epics updated after this date. |
| <a id="groupepicsupdatedbefore"></a>`updatedBefore` | [`Time`](#time) | Epics updated before this date. |
+##### `Group.gitlabSubscriptionsPreviewBillableUserChange`
+
+Preview Billable User Changes.
+
+Returns [`PreviewBillableUserChange`](#previewbillableuserchange).
+
+###### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="groupgitlabsubscriptionspreviewbillableuserchangeaddgroupid"></a>`addGroupId` | [`Int`](#int) | Group ID to add. |
+| <a id="groupgitlabsubscriptionspreviewbillableuserchangeadduseremails"></a>`addUserEmails` | [`[String!]`](#string) | User emails to add. |
+| <a id="groupgitlabsubscriptionspreviewbillableuserchangeadduserids"></a>`addUserIds` | [`[Int!]`](#int) | User IDs to add. |
+| <a id="groupgitlabsubscriptionspreviewbillableuserchangerole"></a>`role` | [`GitlabSubscriptionsUserRole!`](#gitlabsubscriptionsuserrole) | Role of users being added to group. |
+
##### `Group.groupMembers`
A membership of a user within this group.
@@ -16133,6 +16148,16 @@ Represents vulnerability finding of a security report on the pipeline.
| <a id="pipelinesecurityreportfindingtitle"></a>`title` | [`String`](#string) | Title of the vulnerability finding. |
| <a id="pipelinesecurityreportfindinguuid"></a>`uuid` | [`String`](#string) | Name of the vulnerability finding. |
+### `PreviewBillableUserChange`
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="previewbillableuserchangehasoverage"></a>`hasOverage` | [`Boolean`](#boolean) | If the group has an overage after change. |
+| <a id="previewbillableuserchangenewbillableusercount"></a>`newBillableUserCount` | [`Int`](#int) | Total number of billable users after change. |
+| <a id="previewbillableuserchangeseatsinsubscription"></a>`seatsInSubscription` | [`Int`](#int) | Number of seats in subscription. |
+
### `Project`
#### Fields
@@ -16542,6 +16567,21 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="projectforktargetssearch"></a>`search` | [`String`](#string) | Search query for path or name. |
+##### `Project.gitlabSubscriptionsPreviewBillableUserChange`
+
+Preview Billable User Changes.
+
+Returns [`PreviewBillableUserChange`](#previewbillableuserchange).
+
+###### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="projectgitlabsubscriptionspreviewbillableuserchangeaddgroupid"></a>`addGroupId` | [`Int`](#int) | Group ID to add. |
+| <a id="projectgitlabsubscriptionspreviewbillableuserchangeadduseremails"></a>`addUserEmails` | [`[String!]`](#string) | User emails to add. |
+| <a id="projectgitlabsubscriptionspreviewbillableuserchangeadduserids"></a>`addUserIds` | [`[Int!]`](#int) | User IDs to add. |
+| <a id="projectgitlabsubscriptionspreviewbillableuserchangerole"></a>`role` | [`GitlabSubscriptionsUserRole!`](#gitlabsubscriptionsuserrole) | Role of users being added to group. |
+
##### `Project.incidentManagementEscalationPolicies`
Incident Management escalation policies of the project.
@@ -20574,6 +20614,18 @@ Event action.
| <a id="eventactionreopened"></a>`REOPENED` | Reopened action. |
| <a id="eventactionupdated"></a>`UPDATED` | Updated action. |
+### `GitlabSubscriptionsUserRole`
+
+Role of User.
+
+| Value | Description |
+| ----- | ----------- |
+| <a id="gitlabsubscriptionsuserroledeveloper"></a>`DEVELOPER` | Developer. |
+| <a id="gitlabsubscriptionsuserroleguest"></a>`GUEST` | Guest. |
+| <a id="gitlabsubscriptionsuserrolemaintainer"></a>`MAINTAINER` | Maintainer. |
+| <a id="gitlabsubscriptionsuserroleowner"></a>`OWNER` | Owner. |
+| <a id="gitlabsubscriptionsuserrolereporter"></a>`REPORTER` | Reporter. |
+
### `GroupMemberRelation`
Group member relation.
diff --git a/doc/development/database/efficient_in_operator_queries.md b/doc/development/database/efficient_in_operator_queries.md
index 74b896225f9..fb7ff3c1cc2 100644
--- a/doc/development/database/efficient_in_operator_queries.md
+++ b/doc/development/database/efficient_in_operator_queries.md
@@ -670,6 +670,64 @@ records_by_id.each do |id, _|
end
```
+#### Ordering by `JOIN` columns
+
+Ordering records by mixed columns where one or more columns are coming from `JOIN` tables
+works with limitations. It requires extra configuration (CTE). The trick is to use a
+non-materialized CTE to act as a "fake" table which exposes all required columns.
+
+NOTE:
+The query performance might not improve compared to the standard `IN` query. Always
+check the query plan.
+
+Example: order issues by `projects.name, issues.id` within the group hierarchy
+
+The first step is to create a CTE, where all required columns are collected in the `SELECT`
+clause.
+
+```ruby
+cte_query = Issue
+ .select('issues.id AS id', 'issues.project_id AS project_id', 'projects.name AS projects_name')
+ .joins(:project)
+
+cte = Gitlab::SQL::CTE.new(:issue_with_projects, cte_query, materialized: false)
+```
+
+Custom order object configuration:
+
+```ruby
+order = Gitlab::Pagination::Keyset::Order.build([
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'projects_name',
+ order_expression: Issue.arel_table[:projects_name].asc,
+ sql_type: 'character varying',
+ nullable: :nulls_last,
+ distinct: false
+ ),
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: :id,
+ order_expression: Issue.arel_table[:id].asc
+ )
+ ])
+```
+
+Generate the query:
+
+```ruby
+scope = cte.apply_to(Issue.where({}).reorder(order))
+
+opts = {
+ scope: scope,
+ array_scope: Project.where(namespace_id: top_level_group.self_and_descendants.select(:id)).select(:id),
+ array_mapping_scope: -> (id_expression) { Issue.where(Issue.arel_table[:project_id].eq(id_expression)) }
+}
+
+records = Gitlab::Pagination::Keyset::InOperatorOptimization::QueryBuilder
+ .new(**opts)
+ .execute
+ .limit(20)
+```
+
#### Batch iteration
Batch iteration over the records is possible via the keyset `Iterator` class.
diff --git a/doc/development/feature_development.md b/doc/development/feature_development.md
index 357eb3dc679..32ed54f6fd8 100644
--- a/doc/development/feature_development.md
+++ b/doc/development/feature_development.md
@@ -128,6 +128,12 @@ See [database guidelines](database/index.md).
- [How to run Jenkins in development environment](integrations/jenkins.md)
- [How to run local `Codesandbox` integration for Web IDE Live Preview](integrations/codesandbox.md)
+The following integration guides are internal. Some integrations require access to administrative accounts of third-party services and are available only for GitLab team members to contribute to:
+
+- [Jira app development](https://gitlab.com/gitlab-org/manage/integrations/team/-/blob/main/integrations/jira.md)
+- [Slack app development](https://gitlab.com/gitlab-org/manage/integrations/team/-/blob/main/integrations/slack.md)
+- [ZenTao development](https://gitlab.com/gitlab-org/manage/integrations/team/-/blob/main/integrations/zentao.md)
+
## Testing guides
- [Testing standards and style guidelines](testing_guide/index.md)
diff --git a/doc/development/value_stream_analytics.md b/doc/development/value_stream_analytics.md
index b7e5a86d491..0d321133705 100644
--- a/doc/development/value_stream_analytics.md
+++ b/doc/development/value_stream_analytics.md
@@ -347,3 +347,4 @@ Seed DORA daily metrics for value stream, insights and CI/CD analytics:
```shell
rails c
+ ```
diff --git a/lib/api/ci/variables.rb b/lib/api/ci/variables.rb
index f9707960b9d..c9e1d115d03 100644
--- a/lib/api/ci/variables.rb
+++ b/lib/api/ci/variables.rb
@@ -33,6 +33,9 @@ module API
end
params do
requires :key, type: String, desc: 'The key of the variable'
+ optional :filter, type: Hash, desc: 'Available filters: [environment_scope]. Example: filter[environment_scope]=production' do
+ optional :environment_scope, type: String, desc: 'The environment scope of the variable'
+ end
end
# rubocop: disable CodeReuse/ActiveRecord
get ':id/variables/:key', urgency: :low do
@@ -78,7 +81,9 @@ module API
optional :masked, type: Boolean, desc: 'Whether the variable is masked'
optional :variable_type, type: String, values: ::Ci::Variable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file'
optional :environment_scope, type: String, desc: 'The environment_scope of the variable'
- optional :filter, type: Hash, desc: 'Available filters: [environment_scope]. Example: filter[environment_scope]=production'
+ optional :filter, type: Hash, desc: 'Available filters: [environment_scope]. Example: filter[environment_scope]=production' do
+ optional :environment_scope, type: String, desc: 'The environment scope of the variable'
+ end
end
# rubocop: disable CodeReuse/ActiveRecord
put ':id/variables/:key' do
@@ -104,7 +109,9 @@ module API
end
params do
requires :key, type: String, desc: 'The key of the variable'
- optional :filter, type: Hash, desc: 'Available filters: [environment_scope]. Example: filter[environment_scope]=production'
+ optional :filter, type: Hash, desc: 'Available filters: [environment_scope]. Example: filter[environment_scope]=production' do
+ optional :environment_scope, type: String, desc: 'The environment scope of the variable'
+ end
end
# rubocop: disable CodeReuse/ActiveRecord
delete ':id/variables/:key' do
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index bf1da849cf1..0eb4fbb196c 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -287,22 +287,11 @@ module API
end
def authenticate_by_gitlab_shell_token!
- if Feature.enabled?(:gitlab_shell_jwt_token)
- begin
- payload, _ = JSONWebToken::HMACToken.decode(headers[GITLAB_SHELL_API_HEADER], secret_token)
- unauthorized! unless payload['iss'] == GITLAB_SHELL_JWT_ISSUER
- rescue JWT::DecodeError, JWT::ExpiredSignature, JWT::ImmatureSignature => ex
- Gitlab::ErrorTracking.track_exception(ex)
- unauthorized!
- end
- else
- input = params['secret_token']
- input ||= Base64.decode64(headers[GITLAB_SHARED_SECRET_HEADER]) if headers.key?(GITLAB_SHARED_SECRET_HEADER)
-
- input&.chomp!
-
- unauthorized! unless Devise.secure_compare(secret_token, input)
- end
+ payload, _ = JSONWebToken::HMACToken.decode(headers[GITLAB_SHELL_API_HEADER], secret_token)
+ unauthorized! unless payload['iss'] == GITLAB_SHELL_JWT_ISSUER
+ rescue JWT::DecodeError, JWT::ExpiredSignature, JWT::ImmatureSignature => ex
+ Gitlab::ErrorTracking.track_exception(ex)
+ unauthorized!
end
def authenticated_with_can_read_all_resources!
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 8c58cc585d8..bb97f4fa7ce 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -375,7 +375,7 @@ module API
optional :name, type: String, desc: 'The name that will be assigned to the fork'
optional :description, type: String, desc: 'The description that will be assigned to the fork'
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the fork'
- optional :mr_default_target_self, Boolean, desc: 'Merge requests of this forked project targets itself by default'
+ optional :mr_default_target_self, type: Boolean, desc: 'Merge requests of this forked project targets itself by default'
end
post ':id/fork', feature_category: :source_code_management do
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20759')
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index f393f862f55..8c8b6c0a1ba 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -132,7 +132,7 @@ module API
requires :recaptcha_private_key, type: String, desc: 'Generate private key at http://www.google.com/recaptcha'
end
optional :repository_checks_enabled, type: Boolean, desc: "GitLab will periodically run 'git fsck' in all project and wiki repositories to look for silent disk corruption issues."
- optional :repository_storages_weighted, type: Hash, coerce_with: Validations::Types::HashOfIntegerValues.coerce, desc: 'Storage paths for new projects with a weighted value ranging from 0 to 100'
+ optional :repository_storages_weighted, type: Hash, coerce_with: Validations::Types::HashOfIntegerValues.coerce, desc: 'Storage paths for new projects with a weighted value ranging from 0 to 100', documentation: { type: 'Object', additional_properties: Integer }
optional :require_two_factor_authentication, type: Boolean, desc: 'Require all users to set up Two-factor authentication'
given require_two_factor_authentication: ->(val) { val } do
requires :two_factor_grace_period, type: Integer, desc: 'Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication'
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 0654c6540f6..7f44e46f1ca 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -56,7 +56,7 @@ module API
optional :admin, type: Boolean, desc: 'Flag indicating the user is an administrator'
optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups'
optional :external, type: Boolean, desc: 'Flag indicating the user is an external user'
- optional :avatar, type: ::API::Validations::Types::WorkhorseFile, desc: 'Avatar image for user'
+ optional :avatar, type: ::API::Validations::Types::WorkhorseFile, desc: 'Avatar image for user', documentation: { type: 'file' }
optional :theme_id, type: Integer, desc: 'The GitLab theme for the user'
optional :color_scheme_id, type: Integer, desc: 'The color scheme for the file viewer'
optional :private_profile, type: Boolean, desc: 'Flag indicating the user has a private profile'
@@ -890,7 +890,7 @@ module API
params do
requires :name, type: String, desc: 'The name of the impersonation token'
optional :expires_at, type: Date, desc: 'The expiration date in the format YEAR-MONTH-DAY of the impersonation token'
- optional :scopes, type: Array, desc: 'The array of scopes of the impersonation token'
+ optional :scopes, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The array of scopes of the impersonation token'
end
post feature_category: :authentication_and_authorization do
impersonation_token = finder.build(declared_params(include_missing: false))
diff --git a/lib/api/wikis.rb b/lib/api/wikis.rb
index 082be1f7e11..bb8ad5c4285 100644
--- a/lib/api/wikis.rb
+++ b/lib/api/wikis.rb
@@ -133,7 +133,7 @@ module API
success Entities::WikiAttachment
end
params do
- requires :file, types: [Rack::Multipart::UploadedFile, ::API::Validations::Types::WorkhorseFile], desc: 'The attachment file to be uploaded'
+ requires :file, types: [Rack::Multipart::UploadedFile, ::API::Validations::Types::WorkhorseFile], desc: 'The attachment file to be uploaded', documentation: { type: 'file' }
optional :branch, type: String, desc: 'The name of the branch'
end
post ":id/wikis/attachments" do
diff --git a/lib/gitlab/auth/o_auth/user.rb b/lib/gitlab/auth/o_auth/user.rb
index 1fed2b263da..26be7c8aa60 100644
--- a/lib/gitlab/auth/o_auth/user.rb
+++ b/lib/gitlab/auth/o_auth/user.rb
@@ -217,11 +217,7 @@ module Gitlab
def build_new_user(skip_confirmation: true)
user_params = user_attributes.merge(skip_confirmation: skip_confirmation)
- new_user = Users::AuthorizedBuildService.new(nil, user_params).execute
-
- persist_accepted_terms_if_required(new_user)
-
- new_user
+ Users::AuthorizedBuildService.new(nil, user_params).execute
end
def user_attributes
@@ -249,15 +245,6 @@ module Gitlab
}
end
- def persist_accepted_terms_if_required(new_user)
- if Feature.enabled?(:update_oauth_registration_flow) &&
- Gitlab::CurrentSettings.current_application_settings.enforce_terms?
-
- terms = ApplicationSetting::Term.latest
- Users::RespondToTermsService.new(new_user, terms).execute(accepted: true)
- end
- end
-
def sync_profile_from_provider?
Gitlab::Auth::OAuth::Provider.sync_profile_from_provider?(auth_hash.provider)
end
diff --git a/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml
index 55cf22b6601..51d2273d41d 100644
--- a/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml
@@ -18,3 +18,4 @@ pages:
- public
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+ environment: production
diff --git a/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml
index 26fac92d0dc..ad083fcc5db 100644
--- a/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml
@@ -40,3 +40,4 @@ pages:
- public
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+ environment: production
diff --git a/lib/gitlab/ci/templates/Pages/SwaggerUI.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/SwaggerUI.gitlab-ci.yml
index 961941ac4d0..00efcfa1b32 100644
--- a/lib/gitlab/ci/templates/Pages/SwaggerUI.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/SwaggerUI.gitlab-ci.yml
@@ -32,3 +32,4 @@ pages:
- public
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+ environment: production
diff --git a/qa/qa/specs/features/api/3_create/integrations/webhook_events_spec.rb b/qa/qa/specs/features/api/1_manage/integrations/webhook_events_spec.rb
index aae0329003b..a2d66ffe8d3 100644
--- a/qa/qa/specs/features/api/3_create/integrations/webhook_events_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/integrations/webhook_events_spec.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create' do
- describe 'WebHooks integration', :requires_admin, :integrations, :orchestrated do
+ RSpec.describe 'Manage' do
+ describe 'WebHooks integration', :requires_admin, :integrations, :orchestrated, product_group: :integrations do
before(:context) do
toggle_local_requests(true)
end
@@ -70,7 +70,8 @@ module QA
end
end
- it 'sends an issues and note event', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349723' do
+ it 'sends an issues and note event',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349723' do
setup_webhook(issues: true, note: true) do |webhook, smocker|
issue = Resource::Issue.fabricate_via_api! do |issue_init|
issue_init.project = webhook.project
diff --git a/qa/qa/specs/features/api/1_manage/rate_limits_spec.rb b/qa/qa/specs/features/api/1_manage/rate_limits_spec.rb
index 874626e01f1..24088057abc 100644
--- a/qa/qa/specs/features/api/1_manage/rate_limits_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/rate_limits_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Manage', :requires_admin, :skip_live_env, except: { job: 'review-qa-*' } do
- describe 'rate limits', :reliable do
+ describe 'rate limits', :reliable, product_group: :integrations do
let(:rate_limited_user) { Resource::User.fabricate_via_api! }
let(:api_client) { Runtime::API::Client.new(:gitlab, user: rate_limited_user) }
let!(:request) { Runtime::API::Request.new(api_client, '/users') }
diff --git a/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb b/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb
index 9a65f7342a7..16d4fd35b69 100644
--- a/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Manage' do
- describe 'User', :requires_admin do
+ describe 'User', :requires_admin, product_group: :workspace do
let(:admin_api_client) { Runtime::API::Client.as_admin }
let!(:sub_group) do
diff --git a/qa/qa/specs/features/api/1_manage/users_spec.rb b/qa/qa/specs/features/api/1_manage/users_spec.rb
index 531419e8d0f..ff036c18671 100644
--- a/qa/qa/specs/features/api/1_manage/users_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/users_spec.rb
@@ -4,7 +4,7 @@ require 'airborne'
module QA
RSpec.describe 'Manage' do
- describe 'Users API', :reliable do
+ describe 'Users API', :reliable, product_group: :workspace do
let(:api_client) { Runtime::API::Client.new(:gitlab) }
let(:request) { Runtime::API::Request.new(api_client, '/users') }
diff --git a/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb
index 3e7e4b16dd5..d684eabe644 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/group/transfer_project_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Manage' do
- describe 'Project transfer between groups', :reliable do
+ describe 'Project transfer between groups', :reliable, product_group: :workspace do
let(:source_group) do
Resource::Group.fabricate_via_api! do |group|
group.path = "source-group-#{SecureRandom.hex(8)}"
diff --git a/qa/qa/specs/features/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/integrations/jenkins/jenkins_build_status_spec.rb
index 4bfd253c992..b8d00c2faee 100644
--- a/qa/qa/specs/features/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/integrations/jenkins/jenkins_build_status_spec.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', :requires_admin, :skip_live_env, except: { job: 'review-qa-*' } do
- describe 'Jenkins integration' do
+ RSpec.describe 'Manage', :requires_admin, :skip_live_env, except: { job: 'review-qa-*' } do
+ describe 'Jenkins integration', product_group: :integrations do
let(:jenkins_server) { Service::DockerRun::Jenkins.new }
let(:jenkins_client) do
@@ -48,7 +48,8 @@ module QA
toggle_local_requests(false)
end
- it 'integrates and displays build status for MR pipeline in GitLab', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347788' do
+ it 'integrates and displays build status for MR pipeline in GitLab',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347788' do
setup_project_integration
jenkins_integration = project.find_integration('jenkins')
@@ -133,7 +134,8 @@ module QA
def patch_host_name(host_name, container_name)
return host_name unless host_name.include?('localhost')
- ip_address = `docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' #{container_name}`.strip
+ ip_address = `docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' #{container_name}`
+ .strip
host_name.gsub('localhost', ip_address)
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/jira/jira_basic_integration_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/integrations/jira/jira_basic_integration_spec.rb
index 088556a3981..5a4031b4305 100644
--- a/qa/qa/specs/features/browser_ui/3_create/jira/jira_basic_integration_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/integrations/jira/jira_basic_integration_spec.rb
@@ -1,10 +1,10 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create' do
+ RSpec.describe 'Manage' do
include Support::API
- describe 'Jira integration', :jira, :orchestrated, :requires_admin do
+ describe 'Jira integration', :jira, :orchestrated, :requires_admin, product_group: :integrations do
let(:jira_project_key) { 'JITP' }
let(:project) do
Resource::Project.fabricate_via_api! do |project|
@@ -15,7 +15,8 @@ module QA
before do
page.visit Vendor::Jira::JiraAPI.perform(&:base_url)
- QA::Support::Retrier.retry_until(sleep_interval: 3, reload_page: page, max_attempts: 20, raise_on_failure: true) do
+ QA::Support::Retrier
+ .retry_until(sleep_interval: 3, reload_page: page, max_attempts: 20, raise_on_failure: true) do
page.has_text? 'Welcome to Jira'
end
@@ -33,10 +34,11 @@ module QA
jira.setup_service_with(url: Vendor::Jira::JiraAPI.perform(&:base_url))
end
- expect(page).not_to have_text("Requests to the local network are not allowed")
+ expect(page).not_to have_text("Requests to the local network are not allowed") # rubocop:disable RSpec/ExpectInHook
end
- it 'closes an issue via pushing a commit', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347794' do
+ it 'closes an issue via pushing a commit',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347794' do
issue_key = Vendor::Jira::JiraAPI.perform do |jira_api|
jira_api.create_issue(jira_project_key)
end
@@ -46,7 +48,8 @@ module QA
expect_issue_done(issue_key)
end
- it 'closes an issue via a merge request', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347795' do
+ it 'closes an issue via a merge request',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347795' do
issue_key = Vendor::Jira::JiraAPI.perform do |jira_api|
jira_api.create_issue(jira_project_key)
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/jira_issue_import_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/integrations/jira/jira_issue_import_spec.rb
index d8435407296..7e46276be92 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/jira_issue_import_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/integrations/jira/jira_issue_import_spec.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Plan', :reliable do
- describe 'Jira issue import', :jira, :orchestrated, :requires_admin do
+ RSpec.describe 'Manage', :reliable do
+ describe 'Jira issue import', :jira, :orchestrated, :requires_admin, product_group: :integrations do
let(:jira_project_key) { "JITD" }
let(:jira_issue_title) { "[#{jira_project_key}-1] Jira to GitLab Test Issue" }
let(:jira_issue_description) { "This issue is for testing importing Jira issues to GitLab." }
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_status_emails_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/integrations/pipeline_status_emails_spec.rb
index f4794b3a904..4495d83f336 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_status_emails_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/integrations/pipeline_status_emails_spec.rb
@@ -24,7 +24,7 @@ module QA
end
end
- RSpec.describe 'Verify', :orchestrated, :runner, :requires_admin, :smtp do
+ RSpec.describe 'Manage', :orchestrated, :runner, :requires_admin, :smtp, product_group: :integrations do
describe 'Pipeline status emails' do
let(:executor) { "qa-runner-#{Time.now.to_i}" }
let(:emails) { %w[foo@bar.com baz@buzz.com] }
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
index 90fbff3261e..3f461e9247f 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Manage', :reliable do
+ RSpec.describe 'Manage', :reliable, product_group: :workspace do
describe 'Add project member' do
it 'user adds project member', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347887' do
Flow::Login.sign_in
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_badge_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_badge_spec.rb
index f624f2fb44f..b251b3075dd 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_badge_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_badge_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Manage' do
- describe 'Create project badge', :reliable do
+ describe 'Create project badge', :reliable, product_group: :workspace do
let(:badge_name) { "project-badge-#{SecureRandom.hex(8)}" }
let(:expected_badge_link_url) { "#{Runtime::Scenario.gitlab_address}/#{project.path_with_namespace}" }
let(:expected_badge_image_url) { "#{Runtime::Scenario.gitlab_address}/#{project.path_with_namespace}/badges/main/pipeline.svg" }
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb
index d07fff80b19..7c6b0d77219 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/create_project_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Manage', :smoke do
+ RSpec.describe 'Manage', :smoke, product_group: :workspace do
describe 'Project' do
shared_examples 'successful project creation' do
it 'creates a new project' do
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb
index d299997dd3c..2abbb6ca73c 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Manage' do
+ RSpec.describe 'Manage', product_group: :workspace do
shared_examples 'loads all images' do |admin|
let(:api_client) { Runtime::API::Client.as_admin }
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/invite_group_to_project_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/invite_group_to_project_spec.rb
index dbfb114dc82..164f86bffce 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/invite_group_to_project_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/invite_group_to_project_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Manage' do
- describe 'Invite group', :reliable do
+ describe 'Invite group', :reliable, product_group: :workspace do
shared_examples 'invites group to project' do
it 'verifies group is added and members can access project with correct access level' do
Page::Project::Menu.perform(&:click_members)
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/project_owner_permissions_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/project_owner_permissions_spec.rb
index 29e590976d2..98a08dd0d9a 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/project_owner_permissions_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/project_owner_permissions_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Manage' do
- describe 'Project owner permissions', :reliable do
+ describe 'Project owner permissions', :reliable, product_group: :workspace do
let!(:owner) do
Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1)
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb
index 88f4996ff03..33ca5f6009c 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Manage' do
- describe 'Project activity', :reliable do
+ describe 'Project activity', :reliable, product_group: :workspace do
it 'user creates an event in the activity page upon Git push',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347879' do
Flow::Login.sign_in
diff --git a/qa/qa/specs/features/browser_ui/1_manage/user/follow_user_activity_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/user/follow_user_activity_spec.rb
index a384dc16064..b9b82baa6f1 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/user/follow_user_activity_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/user/follow_user_activity_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Manage' do
- describe 'User', :requires_admin do
+ describe 'User', :requires_admin, product_group: :workspace do
let(:admin_api_client) { Runtime::API::Client.as_admin }
let(:followed_user_api_client) { Runtime::API::Client.new(:gitlab, user: followed_user) }
diff --git a/qa/qa/specs/features/browser_ui/1_manage/user/user_access_termination_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/user/parent_group_access_termination_spec.rb
index 8462f5db30b..54f05f84dca 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/user/user_access_termination_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/user/parent_group_access_termination_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Manage' do
- describe 'User', :requires_admin, :reliable do
+ describe 'User', :requires_admin, :reliable, product_group: :workspace do
let(:admin_api_client) { Runtime::API::Client.as_admin }
let!(:user) do
@@ -27,7 +27,7 @@ module QA
end
end
- context 'after parent group membership termination' do
+ context 'for after parent group membership termination' do
before do
Flow::Login.while_signed_in_as_admin do
group.sandbox.visit!
@@ -39,7 +39,14 @@ module QA
end
end
- it 'is not allowed to edit the project files', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347866' do
+ after do
+ user.remove_via_api!
+ project.remove_via_api!
+ group.remove_via_api!
+ end
+
+ it 'is not allowed to edit the project files',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347866' do
Flow::Login.sign_in(as: user)
project.visit!
@@ -51,12 +58,6 @@ module QA
expect(page).to have_text("You can’t edit files directly in this project.")
end
-
- after do
- user.remove_via_api!
- project.remove_via_api!
- group.remove_via_api!
- end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/user/user_inherited_access_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/user/user_inherited_access_spec.rb
index 8de9d7c2049..b7585f00630 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/user/user_inherited_access_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/user/user_inherited_access_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Manage' do
- describe 'User', :requires_admin do
+ describe 'User', :requires_admin, product_group: :workspace do
let(:admin_api_client) { Runtime::API::Client.as_admin }
let!(:sub_group) do
diff --git a/scripts/packages/automated_cleanup.rb b/scripts/packages/automated_cleanup.rb
index 6f1e4c7f561..2b5a0011079 100755
--- a/scripts/packages/automated_cleanup.rb
+++ b/scripts/packages/automated_cleanup.rb
@@ -13,10 +13,12 @@ module Packages
def initialize(
project_path: ENV['CI_PROJECT_PATH'],
gitlab_token: ENV['GITLAB_PROJECT_PACKAGES_CLEANUP_API_TOKEN'],
+ api_endpoint: ENV['CI_API_V4_URL'],
options: {}
)
@project_path = project_path
@gitlab_token = gitlab_token
+ @api_endpoint = api_endpoint
@dry_run = options[:dry_run]
puts "Dry-run mode." if dry_run
@@ -25,7 +27,7 @@ module Packages
def gitlab
@gitlab ||= begin
Gitlab.configure do |config|
- config.endpoint = 'https://gitlab.com/api/v4'
+ config.endpoint = api_endpoint
config.private_token = gitlab_token
end
@@ -50,7 +52,7 @@ module Packages
private
- attr_reader :project_path, :gitlab_token, :dry_run
+ attr_reader :project_path, :gitlab_token, :api_endpoint, :dry_run
def delete_package(package)
print_package_state(package)
diff --git a/scripts/review_apps/automated_cleanup.rb b/scripts/review_apps/automated_cleanup.rb
index cfe7a679f9e..2440df6958d 100755
--- a/scripts/review_apps/automated_cleanup.rb
+++ b/scripts/review_apps/automated_cleanup.rb
@@ -27,10 +27,12 @@ module ReviewApps
def initialize(
project_path: ENV['CI_PROJECT_PATH'],
gitlab_token: ENV['GITLAB_PROJECT_REVIEW_APP_CLEANUP_API_TOKEN'],
+ api_endpoint: ENV['CI_API_V4_URL'],
options: {}
)
@project_path = project_path
@gitlab_token = gitlab_token
+ @api_endpoint = api_endpoint
@dry_run = options[:dry_run]
puts "Dry-run mode." if dry_run
@@ -39,7 +41,7 @@ module ReviewApps
def gitlab
@gitlab ||= begin
Gitlab.configure do |config|
- config.endpoint = 'https://gitlab.com/api/v4'
+ config.endpoint = api_endpoint
# gitlab-bot's token "GitLab review apps cleanup"
config.private_token = gitlab_token
end
@@ -172,7 +174,7 @@ module ReviewApps
private
- attr_reader :project_path, :gitlab_token, :dry_run
+ attr_reader :project_path, :gitlab_token, :api_endpoint, :dry_run
def fetch_environment(environment)
gitlab.environment(project_path, environment.id)
diff --git a/spec/lib/api/helpers_spec.rb b/spec/lib/api/helpers_spec.rb
index 11bbe825402..652727f371b 100644
--- a/spec/lib/api/helpers_spec.rb
+++ b/spec/lib/api/helpers_spec.rb
@@ -989,42 +989,5 @@ RSpec.describe API::Helpers do
it_behaves_like 'authorized'
end
-
- context 'when gitlab_shell_jwt_token is disabled' do
- let(:valid_secret_token) { +'valid' } # mutable string to use chomp!
- let(:invalid_secret_token) { +'invalid' } # mutable string to use chomp!
-
- before do
- stub_feature_flags(gitlab_shell_jwt_token: false)
- end
-
- context 'when shared secret is not provided' do
- it_behaves_like 'unauthorized'
- end
-
- context 'when shared secret provided via params' do
- let(:params) { { 'secret_token' => valid_secret_token } }
-
- it_behaves_like 'authorized'
-
- context 'but it is invalid' do
- let(:params) { { 'secret_token' => invalid_secret_token } }
-
- it_behaves_like 'unauthorized'
- end
- end
-
- context 'when shared secret provided via headers' do
- let(:headers) { { described_class::GITLAB_SHARED_SECRET_HEADER => Base64.encode64(valid_secret_token) } }
-
- it_behaves_like 'authorized'
-
- context 'but it is invalid' do
- let(:headers) { { described_class::GITLAB_SHARED_SECRET_HEADER => Base64.encode64(invalid_secret_token) } }
-
- it_behaves_like 'unauthorized'
- end
- end
- end
end
end
diff --git a/spec/lib/gitlab/auth/o_auth/user_spec.rb b/spec/lib/gitlab/auth/o_auth/user_spec.rb
index b160f322fb8..95a518afcf1 100644
--- a/spec/lib/gitlab/auth/o_auth/user_spec.rb
+++ b/spec/lib/gitlab/auth/o_auth/user_spec.rb
@@ -4,7 +4,6 @@ require 'spec_helper'
RSpec.describe Gitlab::Auth::OAuth::User do
include LdapHelpers
- include TermsHelper
let(:oauth_user) { described_class.new(auth_hash) }
let(:oauth_user_2) { described_class.new(auth_hash_2) }
@@ -145,49 +144,6 @@ RSpec.describe Gitlab::Auth::OAuth::User do
expect(gl_user).to be_password_automatically_set
end
- context 'terms of service' do
- context 'when terms are enforced' do
- before do
- enforce_terms
- end
-
- context 'when feature flag update_oauth_registration_flow is enabled' do
- before do
- stub_feature_flags(update_oauth_registration_flow: true)
- end
-
- it 'creates the user with accepted terms' do
- oauth_user.save # rubocop:disable Rails/SaveBang
-
- expect(gl_user).to be_persisted
- expect(gl_user.terms_accepted?).to be(true)
- end
- end
-
- context 'when feature flag update_oauth_registration_flow is disabled' do
- before do
- stub_feature_flags(update_oauth_registration_flow: false)
- end
-
- it 'creates the user without accepted terms' do
- oauth_user.save # rubocop:disable Rails/SaveBang
-
- expect(gl_user).to be_persisted
- expect(gl_user.terms_accepted?).to be(false)
- end
- end
- end
-
- context 'when terms are not enforced' do
- it 'creates the user without accepted terms' do
- oauth_user.save # rubocop:disable Rails/SaveBang
-
- expect(gl_user).to be_persisted
- expect(gl_user.terms_accepted?).to be(false)
- end
- end
- end
-
shared_examples 'to verify compliance with allow_single_sign_on' do
context 'provider is marked as external' do
it 'marks user as external' do
diff --git a/spec/lib/gitlab/pagination/keyset/in_operator_optimization/query_builder_spec.rb b/spec/lib/gitlab/pagination/keyset/in_operator_optimization/query_builder_spec.rb
index 1b2054ec17f..cc85c897019 100644
--- a/spec/lib/gitlab/pagination/keyset/in_operator_optimization/query_builder_spec.rb
+++ b/spec/lib/gitlab/pagination/keyset/in_operator_optimization/query_builder_spec.rb
@@ -330,4 +330,148 @@ RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::QueryBuilder
end
end
end
+
+ context 'when ordering by JOIN-ed columns' do
+ let(:scope) { cte_with_issues_and_projects.apply_to(Issue.where({})).reorder(order) }
+
+ let(:cte_with_issues_and_projects) do
+ cte_query = Issue.select('issues.id AS id', 'project_id', 'projects.id AS projects_id', 'projects.name AS projects_name').joins(:project)
+ Gitlab::SQL::CTE.new(:issue_with_project, cte_query, materialized: false)
+ end
+
+ let(:in_operator_optimization_options) do
+ {
+ array_scope: Project.where(namespace_id: top_level_group.self_and_descendants.select(:id)).select(:id),
+ array_mapping_scope: -> (id_expression) { Issue.where(Issue.arel_table[:project_id].eq(id_expression)) }
+ }
+ end
+
+ context 'when directions are project.id DESC, issues.id ASC' do
+ let(:order) do
+ Gitlab::Pagination::Keyset::Order.build([
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'projects_id',
+ order_expression: Issue.arel_table[:projects_id].asc,
+ sql_type: 'integer',
+ nullable: :not_nullable,
+ distinct: false
+ ),
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: :id,
+ order_expression: Issue.arel_table[:id].asc
+ )
+ ])
+ end
+
+ let(:expected_order) { issues.sort_by { |issue| [issue.project_id, issue.id] } }
+
+ context 'when iterating records one by one' do
+ let(:batch_size) { 1 }
+
+ it_behaves_like 'correct ordering examples', skip_finder_query_test: true
+ end
+
+ context 'when iterating records with LIMIT 2' do
+ let(:batch_size) { 2 }
+
+ it_behaves_like 'correct ordering examples', skip_finder_query_test: true
+ end
+ end
+
+ context 'when directions are projects.id DESC, issues.id ASC' do
+ let(:order) do
+ Gitlab::Pagination::Keyset::Order.build([
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'projects_id',
+ order_expression: Issue.arel_table[:projects_id].desc,
+ sql_type: 'integer',
+ nullable: :not_nullable,
+ distinct: false
+ ),
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: :id,
+ order_expression: Issue.arel_table[:id].asc
+ )
+ ])
+ end
+
+ let(:expected_order) { issues.sort_by { |issue| [issue.project_id * -1, issue.id] } }
+
+ context 'when iterating records one by one' do
+ let(:batch_size) { 1 }
+
+ it_behaves_like 'correct ordering examples', skip_finder_query_test: true
+ end
+
+ context 'when iterating records with LIMIT 2' do
+ let(:batch_size) { 2 }
+
+ it_behaves_like 'correct ordering examples', skip_finder_query_test: true
+ end
+ end
+
+ context 'when directions are projects.name ASC, projects.id ASC, issues.id ASC' do
+ let(:order) do
+ Gitlab::Pagination::Keyset::Order.build([
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'projects_name',
+ order_expression: Issue.arel_table[:projects_name].asc,
+ sql_type: 'character varying',
+ nullable: :not_nullable,
+ distinct: false
+ ),
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'projects_id',
+ order_expression: Issue.arel_table[:projects_id].asc,
+ sql_type: 'integer',
+ nullable: :not_nullable,
+ distinct: false
+ ),
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: :id,
+ order_expression: Issue.arel_table[:id].asc
+ )
+ ])
+ end
+
+ let(:expected_order) { issues.sort_by { |issue| [issue.project.name, issue.project.id, issue.id] } }
+
+ context 'when iterating records with LIMIT 2' do
+ let(:batch_size) { 2 }
+
+ it_behaves_like 'correct ordering examples', skip_finder_query_test: true
+ end
+ end
+
+ context 'when directions are projects.name ASC (nullable), issues.id ASC' do
+ let(:cte_with_issues_and_projects) do
+ cte_query = Issue.select('issues.id AS id', 'project_id', 'projects.id AS projects_id', 'NULL AS projects_name').joins(:project)
+ Gitlab::SQL::CTE.new(:issue_with_project, cte_query, materialized: false)
+ end
+
+ let(:order) do
+ Gitlab::Pagination::Keyset::Order.build([
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'projects_name',
+ order_expression: Issue.arel_table[:projects_name].asc,
+ sql_type: 'character varying',
+ nullable: :nulls_last,
+ distinct: false
+ ),
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: :id,
+ order_expression: Issue.arel_table[:id].asc
+ )
+ ])
+ end
+
+ let(:expected_order) { issues.sort_by { |issue| [issue.id] } }
+
+ context 'when iterating records with LIMIT 2' do
+ let(:batch_size) { 2 }
+
+ it_behaves_like 'correct ordering examples', skip_finder_query_test: true
+ end
+ end
+ end
end
diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb
index 6419a9fc9d9..da8c10b67a6 100644
--- a/spec/models/hooks/web_hook_spec.rb
+++ b/spec/models/hooks/web_hook_spec.rb
@@ -139,6 +139,16 @@ RSpec.describe WebHook do
it { is_expected.to contain_exactly(:token, :url, :url_variables) }
end
+ describe '.web_hooks_disable_failed?' do
+ it 'returns true when feature is enabled for parent' do
+ second_hook = build(:project_hook, project: create(:project))
+ stub_feature_flags(web_hooks_disable_failed: [false, second_hook.project])
+
+ expect(described_class.web_hooks_disable_failed?(hook)).to eq(false)
+ expect(described_class.web_hooks_disable_failed?(second_hook)).to eq(true)
+ end
+ end
+
describe 'execute' do
let(:data) { { key: 'value' } }
let(:hook_name) { 'project hook' }
diff --git a/spec/models/user_detail_spec.rb b/spec/models/user_detail_spec.rb
index 9189b9a1469..04964d36dcd 100644
--- a/spec/models/user_detail_spec.rb
+++ b/spec/models/user_detail_spec.rb
@@ -25,5 +25,140 @@ RSpec.describe UserDetail do
describe '#bio' do
it { is_expected.to validate_length_of(:bio).is_at_most(255) }
end
+
+ describe '#linkedin' do
+ it { is_expected.to validate_length_of(:linkedin).is_at_most(500) }
+ end
+
+ describe '#twitter' do
+ it { is_expected.to validate_length_of(:twitter).is_at_most(500) }
+ end
+
+ describe '#skype' do
+ it { is_expected.to validate_length_of(:skype).is_at_most(500) }
+ end
+
+ describe '#location' do
+ it { is_expected.to validate_length_of(:location).is_at_most(500) }
+ end
+
+ describe '#organization' do
+ it { is_expected.to validate_length_of(:organization).is_at_most(500) }
+ end
+
+ describe '#website_url' do
+ it { is_expected.to validate_length_of(:website_url).is_at_most(500) }
+ end
+ end
+
+ describe '.user_fields_changed?' do
+ let(:user) { create(:user) }
+
+ context 'when user detail fields unchanged' do
+ it 'returns false' do
+ expect(described_class.user_fields_changed?(user)).to be false
+ end
+
+ %i[linkedin location organization skype twitter website_url].each do |attr|
+ context "when #{attr} is changed" do
+ before do
+ user[attr] = 'new value'
+ end
+
+ it 'returns true' do
+ expect(described_class.user_fields_changed?(user)).to be true
+ end
+ end
+ end
+ end
+ end
+
+ describe '#sanitize_attrs' do
+ shared_examples 'sanitizes html' do |attr|
+ it 'sanitizes html tags' do
+ details = build_stubbed(:user_detail, attr => '<a href="//evil.com">https://example.com<a>')
+ expect { details.sanitize_attrs }.to change { details[attr] }.to('https://example.com')
+ end
+
+ it 'sanitizes iframe scripts' do
+ details = build_stubbed(:user_detail, attr => '<iframe src=javascript:alert()><iframe>')
+ expect { details.sanitize_attrs }.to change { details[attr] }.to('')
+ end
+
+ it 'sanitizes js scripts' do
+ details = build_stubbed(:user_detail, attr => '<script>alert("Test")</script>')
+ expect { details.sanitize_attrs }.to change { details[attr] }.to('')
+ end
+ end
+
+ %i[linkedin skype twitter website_url].each do |attr|
+ it_behaves_like 'sanitizes html', attr
+
+ it 'encodes HTML entities' do
+ details = build_stubbed(:user_detail, attr => 'test&attr')
+ expect { details.sanitize_attrs }.to change { details[attr] }.to('test&amp;attr')
+ end
+ end
+
+ %i[location organization].each do |attr|
+ it_behaves_like 'sanitizes html', attr
+
+ it 'does not encode HTML entities' do
+ details = build_stubbed(:user_detail, attr => 'test&attr')
+ expect { details.sanitize_attrs }.not_to change { details[attr] }
+ end
+ end
+
+ it 'sanitizes on validation' do
+ details = build(:user_detail)
+
+ expect(details)
+ .to receive(:sanitize_attrs)
+ .at_least(:once)
+ .and_call_original
+
+ details.save!
+ end
+ end
+
+ describe '#assign_changed_fields_from_user' do
+ let(:user_detail) { build(:user_detail) }
+
+ shared_examples 'syncs field with `user_details`' do |field|
+ it 'does not sync the field to `user_details` if unchanged' do
+ expect { user_detail.assign_changed_fields_from_user }
+ .to not_change { user_detail.public_send(field) }
+ end
+
+ it 'syncs the field to `user_details` if changed' do
+ user_detail.user[field] = "new_value"
+ expect { user_detail.assign_changed_fields_from_user }
+ .to change { user_detail.public_send(field) }
+ .to("new_value")
+ end
+
+ it 'truncates the field if too long' do
+ user_detail.user[field] = 'a' * (UserDetail::DEFAULT_FIELD_LENGTH + 1)
+ expect { user_detail.assign_changed_fields_from_user }
+ .to change { user_detail.public_send(field) }
+ .to('a' * UserDetail::DEFAULT_FIELD_LENGTH)
+ end
+
+ it 'properly syncs nil field to `user_details' do
+ user_detail.user[field] = 'Test'
+ user_detail.user.save!(validate: false)
+ user_detail.user[field] = nil
+ expect { user_detail.assign_changed_fields_from_user }
+ .to change { user_detail.public_send(field) }
+ .to('')
+ end
+ end
+
+ it_behaves_like 'syncs field with `user_details`', :linkedin
+ it_behaves_like 'syncs field with `user_details`', :location
+ it_behaves_like 'syncs field with `user_details`', :organization
+ it_behaves_like 'syncs field with `user_details`', :skype
+ it_behaves_like 'syncs field with `user_details`', :twitter
+ it_behaves_like 'syncs field with `user_details`', :website_url
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index e6d4e9cc2f6..73ac4e7d3f2 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -5421,6 +5421,41 @@ RSpec.describe User do
end
end
+ describe '#ensure_user_detail_assigned' do
+ let(:user) { build(:user) }
+
+ context 'when no user detail field has been changed' do
+ before do
+ allow(UserDetail)
+ .to receive(:user_fields_changed?)
+ .and_return(false)
+ end
+
+ it 'does not assign user details before save' do
+ expect(user.user_detail)
+ .not_to receive(:assign_changed_fields_from_user)
+
+ user.save!
+ end
+ end
+
+ context 'when a user detail field has been changed' do
+ before do
+ allow(UserDetail)
+ .to receive(:user_fields_changed?)
+ .and_return(true)
+ end
+
+ it 'assigns user details before save' do
+ expect(user.user_detail)
+ .to receive(:assign_changed_fields_from_user)
+ .and_call_original
+
+ user.save!
+ end
+ end
+ end
+
describe '#username_changed_hook' do
context 'for a new user' do
let(:user) { build(:user) }
diff --git a/spec/policies/issuable_policy_spec.rb b/spec/policies/issuable_policy_spec.rb
index 9a862db992f..2bedcf60539 100644
--- a/spec/policies/issuable_policy_spec.rb
+++ b/spec/policies/issuable_policy_spec.rb
@@ -156,8 +156,8 @@ RSpec.describe IssuablePolicy, models: true do
expect(permissions(guest, issue)).to be_disallowed(:create_timelog)
end
- it 'does not allow reading confidential notes' do
- expect(permissions(guest, issue)).to be_disallowed(:read_confidential_notes)
+ it 'does not allow reading internal notes' do
+ expect(permissions(guest, issue)).to be_disallowed(:read_internal_note)
end
end
diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index 4173a854421..32cacfc713c 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -68,24 +68,6 @@ RSpec.describe API::Internal::Base do
expect(response).to have_gitlab_http_status(:unauthorized)
end
-
- context 'when gitlab_shell_jwt_token is disabled' do
- before do
- stub_feature_flags(gitlab_shell_jwt_token: false)
- end
-
- it 'authenticates using a header' do
- perform_request(headers: { API::Helpers::GITLAB_SHARED_SECRET_HEADER => Base64.encode64(secret_token) })
-
- expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'returns 401 when no credentials provided' do
- get(api("/internal/check"))
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
- end
end
end
diff --git a/spec/services/web_hooks/log_execution_service_spec.rb b/spec/services/web_hooks/log_execution_service_spec.rb
index 9ba2c2b0764..1b8ff9f2a05 100644
--- a/spec/services/web_hooks/log_execution_service_spec.rb
+++ b/spec/services/web_hooks/log_execution_service_spec.rb
@@ -41,6 +41,14 @@ RSpec.describe WebHooks::LogExecutionService do
service.execute
end
+ it 'does not update the last failure when the feature flag is disabled' do
+ stub_feature_flags(web_hooks_disable_failed: false)
+
+ expect(project_hook).not_to receive(:update_last_failure)
+
+ service.execute
+ end
+
context 'obtaining an exclusive lease' do
let(:lease_key) { "web_hooks:update_hook_failure_state:#{project_hook.id}" }
diff --git a/workhorse/go.mod b/workhorse/go.mod
index 0843fe48130..ec78a85e413 100644
--- a/workhorse/go.mod
+++ b/workhorse/go.mod
@@ -7,7 +7,7 @@ require (
github.com/BurntSushi/toml v1.2.0
github.com/FZambia/sentinel v1.1.1
github.com/alecthomas/chroma/v2 v2.3.0
- github.com/aws/aws-sdk-go v1.44.114
+ github.com/aws/aws-sdk-go v1.44.116
github.com/disintegration/imaging v1.6.2
github.com/getsentry/raven-go v0.2.0
github.com/golang-jwt/jwt/v4 v4.4.2
diff --git a/workhorse/go.sum b/workhorse/go.sum
index 9515f7d0384..67392ede877 100644
--- a/workhorse/go.sum
+++ b/workhorse/go.sum
@@ -179,8 +179,8 @@ github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZo
github.com/aws/aws-sdk-go v1.17.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
-github.com/aws/aws-sdk-go v1.44.114 h1:plIkWc/RsHr3DXBj4MEw9sEW4CcL/e2ryokc+CKyq1I=
-github.com/aws/aws-sdk-go v1.44.114/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
+github.com/aws/aws-sdk-go v1.44.116 h1:NpLIhcvLWXJZAEwvPj3TDHeqp7DleK6ZUVYyW01WNHY=
+github.com/aws/aws-sdk-go v1.44.116/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go-v2 v1.16.2 h1:fqlCk6Iy3bnCumtrLz9r3mJ/2gUT0pJ0wLFVIdWh+JA=
github.com/aws/aws-sdk-go-v2 v1.16.2/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.1 h1:SdK4Ppk5IzLs64ZMvr6MrSficMtjY2oS0WOORXTlxwU=