summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-02-10 18:10:50 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-02-10 18:10:50 +0000
commitf27a1b0faf16a83ba9c3f71f660262e368f4509a (patch)
treef87277526b25da417b30a425c51bbd81eff6639b
parentae1b3d982482280f22a907faba2c9ba02f4d1db1 (diff)
downloadgitlab-ce-f27a1b0faf16a83ba9c3f71f660262e368f4509a.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab-ci.yml5
-rw-r--r--.rubocop_todo/layout/argument_alignment.yml3
-rw-r--r--.rubocop_todo/rspec/missing_feature_category.yml7
-rw-r--r--app/assets/javascripts/confidential_merge_request/components/dropdown.vue48
-rw-r--r--app/assets/javascripts/confidential_merge_request/components/project_form_group.vue2
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue5
-rw-r--r--app/finders/packages/tags_finder.rb2
-rw-r--r--app/models/packages/package.rb3
-rw-r--r--app/models/packages/tag.rb4
-rw-r--r--app/models/system_note_metadata.rb3
-rw-r--r--app/presenters/packages/npm/package_presenter.rb2
-rw-r--r--db/migrate/20230201014223_initialize_conversion_of_system_note_metadata_note_id_to_bigint.rb16
-rw-r--r--db/post_migrate/20230201014238_backfill_system_note_metadata_note_id_for_bigint_conversion.rb16
-rw-r--r--db/schema_migrations/202302010142231
-rw-r--r--db/schema_migrations/202302010142381
-rw-r--r--db/structure.sql14
-rw-r--r--doc/development/logging.md26
-rw-r--r--doc/user/group/epics/manage_epics.md13
-rw-r--r--doc/user/group/saml_sso/index.md12
-rw-r--r--doc/user/group/saml_sso/scim_setup.md2
-rw-r--r--doc/user/search/index.md19
-rw-r--r--locale/gitlab.pot9
-rw-r--r--qa/qa/page/merge_request/new.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb8
-rw-r--r--spec/frontend/confidential_merge_request/components/dropdown_spec.js100
-rw-r--r--spec/frontend/issues/show/components/incidents/timeline_events_item_spec.js20
-rw-r--r--spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js4
-rw-r--r--spec/lib/feature_groups/gitlab_team_members_spec.rb2
-rw-r--r--spec/lib/feature_spec.rb2
-rw-r--r--spec/models/packages/tag_spec.rb12
-rw-r--r--spec/requests/api/appearance_spec.rb35
-rw-r--r--spec/requests/api/applications_spec.rb77
-rw-r--r--spec/spec_helper.rb2
-rw-r--r--spec/support/shared_examples/requests/admin_mode_shared_examples.rb98
34 files changed, 401 insertions, 175 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9972d0f166b..9fa296be455 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -124,7 +124,7 @@ workflow:
variables:
PG_VERSION: "12"
- DEFAULT_CI_IMAGE: "${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}.patched-golang-${GO_VERSION}-node-16.14-postgresql-${PG_VERSION}:rubygems-3.2-git-2.36-lfs-2.9-chrome-${CHROME_VERSION}-yarn-1.22-graphicsmagick-1.3.36"
+ DEFAULT_CI_IMAGE: "${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}.patched-golang-${GO_VERSION}-rust-${RUST_VERSION}-node-16.14-postgresql-${PG_VERSION}:rubygems-3.2-git-2.36-lfs-2.9-chrome-${CHROME_VERSION}-yarn-1.22-graphicsmagick-1.3.36"
# We set $GITLAB_DEPENDENCY_PROXY to another variable (since it's set at the group level and has higher precedence than .gitlab-ci.yml)
# so that we can override $GITLAB_DEPENDENCY_PROXY_ADDRESS in workflow rules.
GITLAB_DEPENDENCY_PROXY_ADDRESS: "${GITLAB_DEPENDENCY_PROXY}"
@@ -141,10 +141,11 @@ variables:
GIT_SUBMODULE_STRATEGY: "none"
GET_SOURCES_ATTEMPTS: "3"
DEBIAN_VERSION: "bullseye"
- CHROME_VERSION: "106"
+ CHROME_VERSION: "109"
DOCKER_VERSION: "20.10.14"
RUBY_VERSION: "2.7"
GO_VERSION: "1.18"
+ RUST_VERSION: "1.65"
FLAKY_RSPEC_SUITE_REPORT_PATH: rspec/flaky/report-suite.json
FRONTEND_FIXTURES_MAPPING_PATH: crystalball/frontend_fixtures_mapping.json
diff --git a/.rubocop_todo/layout/argument_alignment.yml b/.rubocop_todo/layout/argument_alignment.yml
index 4e22c8a85a4..e658ac751e7 100644
--- a/.rubocop_todo/layout/argument_alignment.yml
+++ b/.rubocop_todo/layout/argument_alignment.yml
@@ -1318,7 +1318,6 @@ Layout/ArgumentAlignment:
- 'ee/app/services/group_saml/saml_group_links/create_service.rb'
- 'ee/app/services/iterations/create_service.rb'
- 'ee/app/services/merge_trains/create_pipeline_service.rb'
- - 'ee/app/services/projects/register_suggested_reviewers_project_service.rb'
- 'ee/app/services/registrations/standard_namespace_create_service.rb'
- 'ee/app/services/resource_events/change_weight_service.rb'
- 'ee/app/services/security/findings/dismiss_service.rb'
@@ -1585,7 +1584,6 @@ Layout/ArgumentAlignment:
- 'ee/spec/lib/gitlab/analytics/cycle_analytics/data_collector_spec.rb'
- 'ee/spec/lib/gitlab/analytics/cycle_analytics/distinct_stage_loader_spec.rb'
- 'ee/spec/lib/gitlab/analytics/cycle_analytics/summary/stage_time_summary_spec.rb'
- - 'ee/spec/lib/gitlab/applied_ml/suggested_reviewers/client_spec.rb'
- 'ee/spec/lib/gitlab/audit/auditor_spec.rb'
- 'ee/spec/lib/gitlab/auth/group_saml/identity_linker_spec.rb'
- 'ee/spec/lib/gitlab/auth/ldap/access_spec.rb'
@@ -2237,7 +2235,6 @@ Layout/ArgumentAlignment:
- 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/wiki_ssh_push_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/12_systems/geo/wiki_ssh_push_to_secondary_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/13_secure/merge_request_license_widget_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/14_model_ops/suggested_reviewer_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/15_growth/free_trial_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_saml_enforced_sso_git_access_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_saml_non_enforced_sso_spec.rb'
diff --git a/.rubocop_todo/rspec/missing_feature_category.yml b/.rubocop_todo/rspec/missing_feature_category.yml
index 91b2fb06070..77004d41167 100644
--- a/.rubocop_todo/rspec/missing_feature_category.yml
+++ b/.rubocop_todo/rspec/missing_feature_category.yml
@@ -1489,7 +1489,6 @@ RSpec/MissingFeatureCategory:
- 'ee/spec/models/license_spec.rb'
- 'ee/spec/models/member_spec.rb'
- 'ee/spec/models/merge_request/blocking_spec.rb'
- - 'ee/spec/models/merge_request/suggested_reviewers_merge_request_spec.rb'
- 'ee/spec/models/merge_request_block_spec.rb'
- 'ee/spec/models/merge_request_spec.rb'
- 'ee/spec/models/merge_requests/compliance_violation_spec.rb'
@@ -2153,8 +2152,6 @@ RSpec/MissingFeatureCategory:
- 'ee/spec/services/merge_request_approval_settings/update_service_spec.rb'
- 'ee/spec/services/merge_requests/approval_service_spec.rb'
- 'ee/spec/services/merge_requests/build_service_spec.rb'
- - 'ee/spec/services/merge_requests/capture_suggested_reviewers_accepted_service_spec.rb'
- - 'ee/spec/services/merge_requests/fetch_suggested_reviewers_service_spec.rb'
- 'ee/spec/services/merge_requests/merge_service_spec.rb'
- 'ee/spec/services/merge_requests/merge_to_ref_service_spec.rb'
- 'ee/spec/services/merge_requests/mergeability/check_approved_service_spec.rb'
@@ -2214,7 +2211,6 @@ RSpec/MissingFeatureCategory:
- 'ee/spec/services/projects/operations/update_service_spec.rb'
- 'ee/spec/services/projects/prometheus/alerts/notify_service_spec.rb'
- 'ee/spec/services/projects/protect_default_branch_service_spec.rb'
- - 'ee/spec/services/projects/register_suggested_reviewers_project_service_spec.rb'
- 'ee/spec/services/projects/restore_service_spec.rb'
- 'ee/spec/services/projects/setup_ci_cd_spec.rb'
- 'ee/spec/services/projects/transfer_service_spec.rb'
@@ -2416,7 +2412,6 @@ RSpec/MissingFeatureCategory:
- 'ee/spec/views/projects/security/policies/index.html.haml_spec.rb'
- 'ee/spec/views/projects/security/sast_configuration/show.html.haml_spec.rb'
- 'ee/spec/views/projects/settings/merge_requests/_merge_request_approvals.html.haml_spec.rb'
- - 'ee/spec/views/projects/settings/merge_requests/_suggested_reviewers_settings.html.haml_spec.rb'
- 'ee/spec/views/projects/settings/subscriptions/_index.html.haml_spec.rb'
- 'ee/spec/views/registrations/company/new.html.haml_spec.rb'
- 'ee/spec/views/registrations/groups_projects/new.html.haml_spec.rb'
@@ -2571,7 +2566,6 @@ RSpec/MissingFeatureCategory:
- 'ee/spec/workers/ldap_sync_worker_spec.rb'
- 'ee/spec/workers/licenses/reset_submit_license_usage_data_banner_worker_spec.rb'
- 'ee/spec/workers/merge_request_reset_approvals_worker_spec.rb'
- - 'ee/spec/workers/merge_requests/capture_suggested_reviewers_accepted_worker_spec.rb'
- 'ee/spec/workers/merge_requests/stream_approval_audit_event_worker_spec.rb'
- 'ee/spec/workers/merge_requests/sync_code_owner_approval_rules_worker_spec.rb'
- 'ee/spec/workers/merge_trains/refresh_worker_spec.rb'
@@ -2585,7 +2579,6 @@ RSpec/MissingFeatureCategory:
- 'ee/spec/workers/project_import_schedule_worker_spec.rb'
- 'ee/spec/workers/project_template_export_worker_spec.rb'
- 'ee/spec/workers/projects/disable_legacy_open_source_license_for_inactive_projects_worker_spec.rb'
- - 'ee/spec/workers/projects/register_suggested_reviewers_project_worker_spec.rb'
- 'ee/spec/workers/refresh_license_compliance_checks_worker_spec.rb'
- 'ee/spec/workers/repository_update_mirror_worker_spec.rb'
- 'ee/spec/workers/requirements_management/import_requirements_csv_worker_spec.rb'
diff --git a/app/assets/javascripts/confidential_merge_request/components/dropdown.vue b/app/assets/javascripts/confidential_merge_request/components/dropdown.vue
index 9cb7cd9607f..c937e65abe3 100644
--- a/app/assets/javascripts/confidential_merge_request/components/dropdown.vue
+++ b/app/assets/javascripts/confidential_merge_request/components/dropdown.vue
@@ -1,11 +1,10 @@
<script>
-import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { GlCollapsibleListbox } from '@gitlab/ui';
import { __ } from '~/locale';
export default {
components: {
- GlDropdown,
- GlDropdownItem,
+ GlCollapsibleListbox,
},
props: {
projects: {
@@ -19,32 +18,37 @@ export default {
},
},
computed: {
- dropdownText() {
- if (Object.keys(this.selectedProject).length) {
- return this.selectedProject.name;
- }
-
- return __('Select private project');
+ selectedProjectValue() {
+ return this.selectedProject?.id && String(this.selectedProject.id);
+ },
+ toggleText() {
+ return this.selectedProject?.name || __('Select private project');
+ },
+ listboxItems() {
+ return this.projects.map(({ id, name }) => {
+ return {
+ value: String(id),
+ text: name,
+ };
+ });
},
},
methods: {
- selectProject(project) {
- this.$emit('click', project);
+ selectProject(projectId) {
+ const project = this.projects.find(({ id }) => String(id) === projectId);
+ this.$emit('select', project);
},
},
};
</script>
<template>
- <gl-dropdown block icon="lock" :text="dropdownText">
- <gl-dropdown-item
- v-for="project in projects"
- :key="project.id"
- is-check-item
- :is-checked="project.id === selectedProject.id"
- @click="selectProject(project)"
- >
- {{ project.name }}
- </gl-dropdown-item>
- </gl-dropdown>
+ <gl-collapsible-listbox
+ icon="lock"
+ :items="listboxItems"
+ :selected="selectedProjectValue"
+ :toggle-text="toggleText"
+ block
+ @select="selectProject"
+ />
</template>
diff --git a/app/assets/javascripts/confidential_merge_request/components/project_form_group.vue b/app/assets/javascripts/confidential_merge_request/components/project_form_group.vue
index e95424eef4d..196f5537a90 100644
--- a/app/assets/javascripts/confidential_merge_request/components/project_form_group.vue
+++ b/app/assets/javascripts/confidential_merge_request/components/project_form_group.vue
@@ -114,7 +114,7 @@ export default {
v-if="projects.length"
:projects="projects"
:selected-project="selectedProject"
- @click="selectProject"
+ @select="selectProject"
/>
<p class="gl-text-gray-600 gl-mt-1 gl-mb-0">
<template v-if="projects.length">
diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue
index 9819fb459b3..d33f3146d64 100644
--- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue
+++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue
@@ -42,6 +42,9 @@ export default {
time() {
return formatDate(this.occurredAt, 'HH:MM', true);
},
+ canEditEvent() {
+ return this.action === 'comment';
+ },
},
methods: {
getEventIcon,
@@ -83,7 +86,7 @@ export default {
category="tertiary"
no-caret
>
- <gl-dropdown-item @click="$emit('edit')">
+ <gl-dropdown-item v-if="canEditEvent" @click="$emit('edit')">
{{ $options.i18n.edit }}
</gl-dropdown-item>
<gl-dropdown-item @click="$emit('delete')">
diff --git a/app/finders/packages/tags_finder.rb b/app/finders/packages/tags_finder.rb
index 020b3d8072a..dd104ea6f91 100644
--- a/app/finders/packages/tags_finder.rb
+++ b/app/finders/packages/tags_finder.rb
@@ -15,7 +15,7 @@ class Packages::TagsFinder
.with_name(package_name)
packages = packages.with_package_type(package_type) if package_type.present?
- Packages::Tag.for_packages(packages)
+ Packages::Tag.for_package_ids(packages.select(:id))
end
private
diff --git a/app/models/packages/package.rb b/app/models/packages/package.rb
index c5da647eea5..970538b45e7 100644
--- a/app/models/packages/package.rb
+++ b/app/models/packages/package.rb
@@ -162,7 +162,8 @@ class Packages::Package < ApplicationRecord
scope :preload_files, -> { preload(:installable_package_files) }
scope :preload_nuget_files, -> { preload(:installable_nuget_package_files) }
scope :preload_pipelines, -> { preload(pipelines: :user) }
- scope :last_of_each_version, -> { where(id: all.select('MAX(id) AS id').group(:version)) }
+ scope :last_of_each_version, -> { where(id: all.last_of_each_version_ids) }
+ scope :last_of_each_version_ids, -> { select('MAX(id) AS id').unscope(where: :id).group(:version) }
scope :limit_recent, ->(limit) { order_created_desc.limit(limit) }
scope :select_distinct_name, -> { select(:name).distinct }
diff --git a/app/models/packages/tag.rb b/app/models/packages/tag.rb
index 14a1ae98ed4..9c17a147bf4 100644
--- a/app/models/packages/tag.rb
+++ b/app/models/packages/tag.rb
@@ -10,8 +10,8 @@ class Packages::Tag < ApplicationRecord
scope :preload_package, -> { preload(:package) }
scope :with_name, -> (name) { where(name: name) }
- def self.for_packages(packages)
- where(package_id: packages.select(:id))
+ def self.for_package_ids(package_ids)
+ where(package_id: package_ids)
.order(updated_at: :desc)
.limit(FOR_PACKAGES_TAGS_LIMIT)
end
diff --git a/app/models/system_note_metadata.rb b/app/models/system_note_metadata.rb
index 36166bdbc9a..bb8527d8c01 100644
--- a/app/models/system_note_metadata.rb
+++ b/app/models/system_note_metadata.rb
@@ -2,6 +2,9 @@
class SystemNoteMetadata < ApplicationRecord
include Importable
+ include IgnorableColumns
+
+ ignore_column :note_id_convert_to_bigint, remove_with: '16.0', remove_after: '2023-05-22'
# These notes's action text might contain a reference that is external.
# We should always force a deep validation upon references that are found
diff --git a/app/presenters/packages/npm/package_presenter.rb b/app/presenters/packages/npm/package_presenter.rb
index fabb0a36746..57bdd373309 100644
--- a/app/presenters/packages/npm/package_presenter.rb
+++ b/app/presenters/packages/npm/package_presenter.rb
@@ -83,7 +83,7 @@ module Packages
end
def package_tags
- Packages::Tag.for_packages(packages)
+ Packages::Tag.for_package_ids(packages.last_of_each_version_ids)
.preload_package
end
diff --git a/db/migrate/20230201014223_initialize_conversion_of_system_note_metadata_note_id_to_bigint.rb b/db/migrate/20230201014223_initialize_conversion_of_system_note_metadata_note_id_to_bigint.rb
new file mode 100644
index 00000000000..ec84f1cb946
--- /dev/null
+++ b/db/migrate/20230201014223_initialize_conversion_of_system_note_metadata_note_id_to_bigint.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class InitializeConversionOfSystemNoteMetadataNoteIdToBigint < Gitlab::Database::Migration[2.1]
+ TABLE = :system_note_metadata
+ COLUMNS = %i[note_id]
+
+ enable_lock_retries!
+
+ def up
+ initialize_conversion_of_integer_to_bigint(TABLE, COLUMNS)
+ end
+
+ def down
+ revert_initialize_conversion_of_integer_to_bigint(TABLE, COLUMNS)
+ end
+end
diff --git a/db/post_migrate/20230201014238_backfill_system_note_metadata_note_id_for_bigint_conversion.rb b/db/post_migrate/20230201014238_backfill_system_note_metadata_note_id_for_bigint_conversion.rb
new file mode 100644
index 00000000000..df24b755b7f
--- /dev/null
+++ b/db/post_migrate/20230201014238_backfill_system_note_metadata_note_id_for_bigint_conversion.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class BackfillSystemNoteMetadataNoteIdForBigintConversion < Gitlab::Database::Migration[2.1]
+ TABLE = :system_note_metadata
+ COLUMNS = %i[note_id]
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ backfill_conversion_of_integer_to_bigint(TABLE, COLUMNS)
+ end
+
+ def down
+ revert_backfill_conversion_of_integer_to_bigint(TABLE, COLUMNS)
+ end
+end
diff --git a/db/schema_migrations/20230201014223 b/db/schema_migrations/20230201014223
new file mode 100644
index 00000000000..323fe555df5
--- /dev/null
+++ b/db/schema_migrations/20230201014223
@@ -0,0 +1 @@
+42100a86045f084c3b74e404a2f95d4d76751ad92102edb271dc628279060ce5 \ No newline at end of file
diff --git a/db/schema_migrations/20230201014238 b/db/schema_migrations/20230201014238
new file mode 100644
index 00000000000..8a2d6effa5e
--- /dev/null
+++ b/db/schema_migrations/20230201014238
@@ -0,0 +1 @@
+00bbfdf3e45248b72aac115e44a95c23b71344dcc9e35ad3be6bf1f5eda33561 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 5a937446086..de5030325f9 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -297,6 +297,15 @@ BEGIN
END;
$$;
+CREATE FUNCTION trigger_482bac5ec48a() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ NEW."note_id_convert_to_bigint" := NEW."note_id";
+ RETURN NEW;
+END;
+$$;
+
CREATE FUNCTION trigger_c5a5f48f12b0() RETURNS trigger
LANGUAGE plpgsql
AS $$
@@ -22359,7 +22368,8 @@ CREATE TABLE system_note_metadata (
action character varying,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL,
- description_version_id bigint
+ description_version_id bigint,
+ note_id_convert_to_bigint bigint DEFAULT 0 NOT NULL
);
CREATE SEQUENCE system_note_metadata_id_seq
@@ -33581,6 +33591,8 @@ CREATE TRIGGER trigger_bfc6e47be8cc BEFORE INSERT OR UPDATE ON snippet_user_ment
CREATE TRIGGER trigger_c2051020aa8b BEFORE INSERT OR UPDATE ON issue_user_mentions FOR EACH ROW EXECUTE FUNCTION trigger_c2051020aa8b();
+CREATE TRIGGER trigger_482bac5ec48a BEFORE INSERT OR UPDATE ON system_note_metadata FOR EACH ROW EXECUTE FUNCTION trigger_482bac5ec48a();
+
CREATE TRIGGER trigger_c5a5f48f12b0 BEFORE INSERT OR UPDATE ON epic_user_mentions FOR EACH ROW EXECUTE FUNCTION trigger_c5a5f48f12b0();
CREATE TRIGGER trigger_c7107f30d69d BEFORE INSERT OR UPDATE ON merge_request_metrics FOR EACH ROW EXECUTE FUNCTION trigger_c7107f30d69d();
diff --git a/doc/development/logging.md b/doc/development/logging.md
index 6282f0f6677..538fc7ccfe1 100644
--- a/doc/development/logging.md
+++ b/doc/development/logging.md
@@ -390,14 +390,32 @@ end
On GitLab.com, that setting is only 6 compressed files. These settings should suffice
for most users, but you may need to tweak them in [Omnibus GitLab](https://gitlab.com/gitlab-org/omnibus-gitlab).
-1. If you add a new file, submit an issue to the
- [production tracker](https://gitlab.com/gitlab-com/gl-infra/production/-/issues) or
- a merge request to the [`gitlab_fluentd`](https://gitlab.com/gitlab-cookbooks/gitlab_fluentd)
- project. See [this example](https://gitlab.com/gitlab-cookbooks/gitlab_fluentd/-/merge_requests/51/diffs).
+1. On GitLab.com all new JSON log files generated by GitLab Rails are
+ automatically shipped to Elasticsearch (and available in Kibana) on GitLab
+ Rails Kubernetes pods. If you need the file forwarded from Gitaly nodes then
+ submit an issue to the
+ [production tracker](https://gitlab.com/gitlab-com/gl-infra/production/-/issues)
+ or a merge request to the
+ [`gitlab_fluentd`](https://gitlab.com/gitlab-cookbooks/gitlab_fluentd)
+ project. See
+ [this example](https://gitlab.com/gitlab-cookbooks/gitlab_fluentd/-/merge_requests/51/diffs).
1. Be sure to update the [GitLab CE/EE documentation](../administration/logs/index.md) and the
[GitLab.com runbooks](https://gitlab.com/gitlab-com/runbooks/blob/master/docs/logging/README.md).
+## Finding new log files in Kibana (GitLab.com only)
+
+On GitLab.com all new JSON log files generated by GitLab Rails are
+automatically shipped to Elasticsearch (and available in Kibana) on GitLab
+Rails Kubernetes pods. The `json.subcomponent` field in Kibana will allow you
+to filter by the different kinds of log files. For example the
+`json.subcomponent` will be `production_json` for entries forwarded from
+`production_json.log`.
+
+It's also worth noting that log files from Web/API pods go to a different
+index than log files from Sidekiq pods. Depending on where you log from you
+will find the logs in a different index pattern.
+
## Control logging visibility
An increase in the logs can cause a growing backlog of unacknowledged messages. When adding new log messages, make sure they don't increase the overall volume of logging by more than 10%.
diff --git a/doc/user/group/epics/manage_epics.md b/doc/user/group/epics/manage_epics.md
index 25f875ccdfd..74cfa2bd6ed 100644
--- a/doc/user/group/epics/manage_epics.md
+++ b/doc/user/group/epics/manage_epics.md
@@ -264,7 +264,7 @@ To filter:
### Filter with the OR operator
-> OR filtering for labels was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/382969) in GitLab 15.9 [with a flag](../../../administration/feature_flags.md) named `or_issuable_queries`. Disabled by default.
+> OR filtering for labels and authors was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/382969) in GitLab 15.9 [with a flag](../../../administration/feature_flags.md) named `or_issuable_queries`. Disabled by default.
FLAG:
On self-managed GitLab, by default this feature is not available.
@@ -273,7 +273,10 @@ On GitLab.com, this feature is not available.
The feature is not ready for production use.
When this feature is enabled, you can use the OR operator (**is one of: `||`**)
-to [filter the list of epics](#filter-the-list-of-epics) by labels.
+when you [filter the list of epics](#filter-the-list-of-epics) by:
+
+- Authors
+- Labels
`is one of` represents an inclusive OR. For example, if you filter by `Label is one of Deliverable` and
`Label is one of UX`, GitLab shows epics with either `Deliverable`, `UX`, or both labels.
@@ -518,10 +521,12 @@ The maximum number of direct child epics is 100.
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/8502) in GitLab 15.6 [with a flag](../../../administration/feature_flags.md) named `child_epics_from_different_hierarchies`. Disabled by default.
> - Minimum required role for the group [changed](https://gitlab.com/gitlab-org/gitlab/-/issues/382503) from Reporter to Guest in GitLab 15.7.
+> - Cross-group child epics [enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/375622) in GitLab 15.9. Enabled by default.
FLAG:
-On self-managed GitLab, by default this feature is not available. To make it available per group, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `child_epics_from_different_hierarchies`.
-On GitLab.com, this feature is not available. The feature is not ready for production use.
+On self-managed GitLab, by default this feature is available. To disable it,
+ask an administrator to [disable the feature flag](../../../administration/feature_flags.md) named `child_epics_from_different_hierarchies`.
+On GitLab.com, this feature is available.
You can add a child epic that belongs to a group that is different from the parent epic's group.
diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md
index 3d1ef3bad3e..810d680ec59 100644
--- a/doc/user/group/saml_sso/index.md
+++ b/doc/user/group/saml_sso/index.md
@@ -214,9 +214,9 @@ for additional guidance on information your identity provider may require.
GitLab provides the following information for guidance only.
If you have any questions on configuring the SAML app, contact your provider's support.
-### Azure setup notes
+### Set up Azure
-Follow the Azure documentation on [configuring single sign-on to applications](https://learn.microsoft.com/en-us/azure/active-directory/manage-apps/view-applications-portal) with the notes below for consideration.
+Follow the Azure documentation on [configuring single sign-on to applications](https://learn.microsoft.com/en-us/azure/active-directory/manage-apps/add-application-portal-setup-sso), and use the following notes when needed.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For a demo of the Azure SAML setup including SCIM, see [SCIM Provisioning on Azure Using SAML SSO for Groups Demo](https://youtu.be/24-ZxmTeEBU).
@@ -230,11 +230,11 @@ The video is outdated in regard to objectID mapping and you should follow the [S
| Identity provider single sign-on URL | Login URL |
| Certificate fingerprint | Thumbprint |
-The recommended attributes are:
+You should set the following attributes:
-- **Unique User Identifier (Name identifier)** set to `user.objectID`.
-- **nameid-format** set to persistent.
-- Additional claims set to [supported attributes](#user-attributes).
+- **Unique User Identifier (Name identifier)** to `user.objectID`.
+- **nameid-format** to persistent.
+- Additional claims to [supported attributes](#user-attributes).
If using [Group Sync](#group-sync), customize the name of the group claim to match the required attribute.
diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md
index f02a232ea99..74dc7e067dc 100644
--- a/doc/user/group/saml_sso/scim_setup.md
+++ b/doc/user/group/saml_sso/scim_setup.md
@@ -116,7 +116,7 @@ For each attribute:
1. Select the required settings.
1. Select **Ok**.
-If your SAML configuration differs from [the recommended SAML settings](index.md#azure-setup-notes), select the mapping
+If your SAML configuration differs from [the recommended SAML settings](index.md#set-up-azure), select the mapping
attributes and modify them accordingly. In particular, the `objectId` source attribute must map to the `externalId`
target attribute.
diff --git a/doc/user/search/index.md b/doc/user/search/index.md
index c86fa136044..dc07b4c9215 100644
--- a/doc/user/search/index.md
+++ b/doc/user/search/index.md
@@ -84,6 +84,23 @@ where the results were found.
![code search results](img/code_search_git_blame_v15_1.png)
+## Search for projects by full path
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108906) in GitLab 15.9 [with a flag](../../administration/feature_flags.md) named `full_path_project_search`. Disabled by default.
+
+FLAG:
+On self-managed GitLab, by default this feature is not available.
+To make it available, ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `full_path_project_search`.
+On GitLab.com, this feature is not available.
+
+You can search for a project by entering its full path (including the namespace it belongs to) in the search box.
+As you type the project path, [autocomplete suggestions](#autocomplete-suggestions) are displayed.
+
+For example, the search query:
+
+- `gitlab-org/gitlab` searches for the `gitlab` project in the `gitlab-org` namespace.
+- `gitlab-org/` displays autocomplete suggestions for projects that belong to the `gitlab-org` namespace.
+
## Search for a SHA
You can search for a commit SHA.
@@ -149,7 +166,7 @@ To delete filter tokens one at a time, the <kbd>⌥</kbd> (Mac) / <kbd>Control</
In the search bar, you can view autocomplete suggestions for:
-- Projects and groups
+- [Projects](#search-for-projects-by-full-path) and groups
- Users
- Various help pages (try and type **API help**)
- Project feature pages (try and type **milestones**)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index a6c4917abf7..7cbfcaea1fb 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -34568,6 +34568,9 @@ msgstr ""
msgid "ProtectedBranch|default"
msgstr ""
+msgid "ProtectedEnvironments|Add deployment rules"
+msgstr ""
+
msgid "ProtectedEnvironments|Allowed to deploy"
msgstr ""
@@ -34577,6 +34580,9 @@ msgstr ""
msgid "ProtectedEnvironments|Approval rules"
msgstr ""
+msgid "ProtectedEnvironments|Create deployment rule"
+msgstr ""
+
msgid "ProtectedEnvironments|Delete deployment rule"
msgstr ""
@@ -39293,6 +39299,9 @@ msgstr ""
msgid "Set a default description template to be used for new issues. %{link_start}What are description templates?%{link_end}"
msgstr ""
+msgid "Set a group, access level or users who are required to deploy."
+msgstr ""
+
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr ""
diff --git a/qa/qa/page/merge_request/new.rb b/qa/qa/page/merge_request/new.rb
index ffd40fabf05..a8ea937112b 100644
--- a/qa/qa/page/merge_request/new.rb
+++ b/qa/qa/page/merge_request/new.rb
@@ -49,8 +49,7 @@ module QA
def select_source_branch(branch)
click_element(:source_branch_dropdown)
- fill_element(:dropdown_input_field, branch)
- click_via_capybara(:click_on, branch)
+ search_and_select(branch)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb
index cda45efd828..3fb5c921187 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb
@@ -41,7 +41,11 @@ module QA
'using a ci job token' => {
authentication_token_type: :ci_job_token,
maven_header_name: 'Job-Token',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347579'
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347579',
+ quarantine: {
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/373189',
+ type: :stale
+ }
}
}
end
@@ -60,7 +64,7 @@ module QA
end
end
- it 'pushes and pulls a maven package', testcase: params[:testcase] do
+ it 'pushes and pulls a maven package', testcase: params[:testcase], quarantine: params[:quarantine] do
Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
Resource::Repository::Commit.fabricate_via_api! do |commit|
gitlab_ci_yaml = ERB.new(read_fixture('package_managers/maven/group/producer', 'gitlab_ci.yaml.erb')).result(binding)
diff --git a/spec/frontend/confidential_merge_request/components/dropdown_spec.js b/spec/frontend/confidential_merge_request/components/dropdown_spec.js
index 770f2636648..4d577fe1132 100644
--- a/spec/frontend/confidential_merge_request/components/dropdown_spec.js
+++ b/spec/frontend/confidential_merge_request/components/dropdown_spec.js
@@ -1,47 +1,79 @@
-import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
+import { GlCollapsibleListbox } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
import Dropdown from '~/confidential_merge_request/components/dropdown.vue';
-let vm;
+const TEST_PROJECTS = [
+ {
+ id: 7,
+ name: 'test',
+ },
+ {
+ id: 9,
+ name: 'lorem ipsum',
+ },
+ {
+ id: 11,
+ name: 'dolar sit',
+ },
+];
-function factory(projects = []) {
- vm = mount(Dropdown, {
- propsData: {
- projects,
- selectedProject: projects[0],
- },
- });
-}
-
-describe('Confidential merge request project dropdown component', () => {
- afterEach(() => {
- vm.destroy();
- });
+describe('~/confidential_merge_request/components/dropdown.vue', () => {
+ let wrapper;
- it('renders dropdown items', () => {
- factory([
- {
- id: 1,
- name: 'test',
- },
- {
- id: 2,
- name: 'test',
+ function factory(props = {}) {
+ wrapper = shallowMount(Dropdown, {
+ propsData: {
+ projects: TEST_PROJECTS,
+ ...props,
},
- ]);
+ });
+ }
- expect(vm.findAllComponents(GlDropdownItem).length).toBe(2);
- });
+ const findListbox = () => wrapper.findComponent(GlCollapsibleListbox);
+
+ describe('default', () => {
+ beforeEach(() => {
+ factory();
+ });
+
+ it('renders collapsible listbox', () => {
+ expect(findListbox().props()).toMatchObject({
+ icon: 'lock',
+ selected: [],
+ toggleText: 'Select private project',
+ block: true,
+ items: TEST_PROJECTS.map(({ id, name }) => ({
+ value: String(id),
+ text: name,
+ })),
+ });
+ });
+
+ it('does not emit anything', () => {
+ expect(wrapper.emitted()).toEqual({});
+ });
- it('shows lock icon', () => {
- factory();
+ describe('when listbox emits selected', () => {
+ beforeEach(() => {
+ findListbox().vm.$emit('select', String(TEST_PROJECTS[1].id));
+ });
- expect(vm.findComponent(GlDropdown).props('icon')).toBe('lock');
+ it('emits selected project', () => {
+ expect(wrapper.emitted('select')).toEqual([[TEST_PROJECTS[1]]]);
+ });
+ });
});
- it('has dropdown text', () => {
- factory();
+ describe('with selected', () => {
+ beforeEach(() => {
+ factory({ selectedProject: TEST_PROJECTS[1] });
+ });
- expect(vm.findComponent(GlDropdown).props('text')).toBe('Select private project');
+ it('shows selected project', () => {
+ expect(findListbox().props()).toMatchObject({
+ selected: String(TEST_PROJECTS[1].id),
+ toggleText: TEST_PROJECTS[1].name,
+ });
+ });
});
});
diff --git a/spec/frontend/issues/show/components/incidents/timeline_events_item_spec.js b/spec/frontend/issues/show/components/incidents/timeline_events_item_spec.js
index acff5d9ed43..24653a23036 100644
--- a/spec/frontend/issues/show/components/incidents/timeline_events_item_spec.js
+++ b/spec/frontend/issues/show/components/incidents/timeline_events_item_spec.js
@@ -9,8 +9,8 @@ import { mockEvents } from './mock_data';
describe('IncidentTimelineEventList', () => {
let wrapper;
- const mountComponent = ({ propsData, provide } = {}) => {
- const { action, noteHtml, occurredAt } = mockEvents[0];
+ const mountComponent = ({ propsData, provide, mockEvent = mockEvents[0] } = {}) => {
+ const { action, noteHtml, occurredAt } = mockEvent;
wrapper = mountExtended(IncidentTimelineEventItem, {
propsData: {
action,
@@ -30,6 +30,7 @@ describe('IncidentTimelineEventList', () => {
const findEventTags = () => wrapper.findAllComponents(GlBadge);
const findDropdown = () => wrapper.findComponent(GlDropdown);
const findDeleteButton = () => wrapper.findByText(timelineItemI18n.delete);
+ const findEditButton = () => wrapper.findByText(timelineItemI18n.edit);
describe('template', () => {
beforeEach(() => {
@@ -88,6 +89,21 @@ describe('IncidentTimelineEventList', () => {
expect(findDeleteButton().exists()).toBe(false);
});
+ it('does not show edit item when event was system generated', () => {
+ const systemGeneratedMockEvent = {
+ ...mockEvents[0],
+ action: 'status',
+ };
+
+ mountComponent({
+ provide: { canUpdateTimelineEvent: true },
+ mockEvent: systemGeneratedMockEvent,
+ });
+
+ expect(findDropdown().exists()).toBe(true);
+ expect(findEditButton().exists()).toBe(false);
+ });
+
it('shows dropdown and delete item when user has update permission', () => {
mountComponent({ provide: { canUpdateTimelineEvent: true } });
diff --git a/spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js b/spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js
index 5bac1d6e7ad..63474070701 100644
--- a/spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js
+++ b/spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js
@@ -112,7 +112,9 @@ describe('TimelineEventsTab', () => {
await waitForPromises();
expect(findEmptyState().exists()).toBe(false);
- expect(findTimelineEventsList().props('timelineEvents')).toHaveLength(3);
+ expect(findTimelineEventsList().props('timelineEvents')).toHaveLength(
+ timelineEventsQueryListResponse.data.project.incidentManagementTimelineEvents.nodes.length,
+ );
});
});
diff --git a/spec/lib/feature_groups/gitlab_team_members_spec.rb b/spec/lib/feature_groups/gitlab_team_members_spec.rb
index 103cb62af1c..f4db02e6c58 100644
--- a/spec/lib/feature_groups/gitlab_team_members_spec.rb
+++ b/spec/lib/feature_groups/gitlab_team_members_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe FeatureGroups::GitlabTeamMembers do # rubocop:disable RSpec/MissingFeatureCategory
+RSpec.describe FeatureGroups::GitlabTeamMembers, feature_category: :shared do
let_it_be(:gitlab_com) { create(:group) }
let_it_be_with_reload(:member) { create(:user).tap { |user| gitlab_com.add_developer(user) } }
let_it_be_with_reload(:non_member) { create(:user) }
diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb
index 82501cbd1aa..c86bc36057a 100644
--- a/spec/lib/feature_spec.rb
+++ b/spec/lib/feature_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Feature, stub_feature_flags: false do # rubocop:disable RSpec/MissingFeatureCategory
+RSpec.describe Feature, stub_feature_flags: false, feature_category: :shared do
include StubVersion
before do
diff --git a/spec/models/packages/tag_spec.rb b/spec/models/packages/tag_spec.rb
index 842ba7ad518..bc03c34f56b 100644
--- a/spec/models/packages/tag_spec.rb
+++ b/spec/models/packages/tag_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Packages::Tag, type: :model do
+RSpec.describe Packages::Tag, type: :model, feature_category: :package_registry do
let!(:project) { create(:project) }
let!(:package) { create(:npm_package, version: '1.0.2', project: project, updated_at: 3.days.ago) }
@@ -16,14 +16,14 @@ RSpec.describe Packages::Tag, type: :model do
it { is_expected.to validate_presence_of(:name) }
end
- describe '.for_packages' do
+ describe '.for_package_ids' do
let(:package2) { create(:package, project: project, updated_at: 2.days.ago) }
let(:package3) { create(:package, project: project, updated_at: 1.day.ago) }
let!(:tag1) { create(:packages_tag, package: package) }
let!(:tag2) { create(:packages_tag, package: package2) }
let!(:tag3) { create(:packages_tag, package: package3) }
- subject { described_class.for_packages(project.packages) }
+ subject { described_class.for_package_ids(project.packages) }
it { is_expected.to match_array([tag1, tag2, tag3]) }
@@ -34,6 +34,12 @@ RSpec.describe Packages::Tag, type: :model do
it { is_expected.to match_array([tag2, tag3]) }
end
+
+ context 'with package ids' do
+ subject { described_class.for_package_ids(project.packages.select(:id)) }
+
+ it { is_expected.to match_array([tag1, tag2, tag3]) }
+ end
end
describe '.with_name' do
diff --git a/spec/requests/api/appearance_spec.rb b/spec/requests/api/appearance_spec.rb
index 1b54fcc94f4..c08ecae28e8 100644
--- a/spec/requests/api/appearance_spec.rb
+++ b/spec/requests/api/appearance_spec.rb
@@ -5,21 +5,15 @@ require 'spec_helper'
RSpec.describe API::Appearance, 'Appearance', feature_category: :navigation do
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
+ let_it_be(:path) { "/application/appearance" }
describe "GET /application/appearance" do
- context 'as a non-admin user' do
- it "returns 403" do
- get api("/application/appearance", user)
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
+ it_behaves_like 'GET request permissions for admin mode'
context 'as an admin user' do
it "returns appearance" do
- get api("/application/appearance", admin)
+ get api("/application/appearance", admin, admin_mode: true)
- expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Hash
expect(json_response['description']).to eq('')
expect(json_response['email_header_and_footer_enabled']).to be(false)
@@ -42,18 +36,12 @@ RSpec.describe API::Appearance, 'Appearance', feature_category: :navigation do
end
describe "PUT /application/appearance" do
- context 'as a non-admin user' do
- it "returns 403" do
- put api("/application/appearance", user), params: { title: "Test" }
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
+ it_behaves_like 'PUT request permissions for admin mode', { title: "Test" }
context 'as an admin user' do
context "instance basics" do
it "allows updating the settings" do
- put api("/application/appearance", admin), params: {
+ put api("/application/appearance", admin, admin_mode: true), params: {
title: "GitLab Test Instance",
description: "gitlab-test.example.com",
pwa_name: "GitLab PWA Test",
@@ -63,7 +51,6 @@ RSpec.describe API::Appearance, 'Appearance', feature_category: :navigation do
profile_image_guidelines: "Custom profile image guidelines"
}
- expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Hash
expect(json_response['description']).to eq('gitlab-test.example.com')
expect(json_response['email_header_and_footer_enabled']).to be(false)
@@ -94,7 +81,7 @@ RSpec.describe API::Appearance, 'Appearance', feature_category: :navigation do
email_header_and_footer_enabled: true
}
- put api("/application/appearance", admin), params: settings
+ put api("/application/appearance", admin, admin_mode: true), params: settings
expect(response).to have_gitlab_http_status(:ok)
settings.each do |attribute, value|
@@ -104,14 +91,14 @@ RSpec.describe API::Appearance, 'Appearance', feature_category: :navigation do
context "fails on invalid color values" do
it "with message_font_color" do
- put api("/application/appearance", admin), params: { message_font_color: "No Color" }
+ put api("/application/appearance", admin, admin_mode: true), params: { message_font_color: "No Color" }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']['message_font_color']).to contain_exactly('must be a valid color code')
end
it "with message_background_color" do
- put api("/application/appearance", admin), params: { message_background_color: "#1" }
+ put api("/application/appearance", admin, admin_mode: true), params: { message_background_color: "#1" }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']['message_background_color']).to contain_exactly('must be a valid color code')
@@ -123,7 +110,7 @@ RSpec.describe API::Appearance, 'Appearance', feature_category: :navigation do
let_it_be(:appearance) { create(:appearance) }
it "allows updating the image files" do
- put api("/application/appearance", admin), params: {
+ put api("/application/appearance", admin, admin_mode: true), params: {
logo: fixture_file_upload("spec/fixtures/dk.png", "image/png"),
header_logo: fixture_file_upload("spec/fixtures/dk.png", "image/png"),
pwa_icon: fixture_file_upload("spec/fixtures/dk.png", "image/png"),
@@ -139,14 +126,14 @@ RSpec.describe API::Appearance, 'Appearance', feature_category: :navigation do
context "fails on invalid color images" do
it "with string instead of file" do
- put api("/application/appearance", admin), params: { logo: 'not-a-file.png' }
+ put api("/application/appearance", admin, admin_mode: true), params: { logo: 'not-a-file.png' }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq("logo is invalid")
end
it "with .svg file instead of .png" do
- put api("/application/appearance", admin), params: { favicon: fixture_file_upload("spec/fixtures/logo_sample.svg", "image/svg") }
+ put api("/application/appearance", admin, admin_mode: true), params: { favicon: fixture_file_upload("spec/fixtures/logo_sample.svg", "image/svg") }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']['favicon']).to contain_exactly("You are not allowed to upload \"svg\" files, allowed types: png, ico")
diff --git a/spec/requests/api/applications_spec.rb b/spec/requests/api/applications_spec.rb
index e238a1fb554..b81cdcfea8e 100644
--- a/spec/requests/api/applications_spec.rb
+++ b/spec/requests/api/applications_spec.rb
@@ -3,21 +3,23 @@
require 'spec_helper'
RSpec.describe API::Applications, :api, feature_category: :authentication_and_authorization do
- let(:admin_user) { create(:user, admin: true) }
- let(:user) { create(:user, admin: false) }
- let(:scopes) { 'api' }
+ let_it_be(:admin) { create(:admin) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:scopes) { 'api' }
+ let_it_be(:path) { "/applications" }
let!(:application) { create(:application, name: 'another_application', owner: nil, redirect_uri: 'http://other_application.url', scopes: scopes) }
describe 'POST /applications' do
+ it_behaves_like 'POST request permissions for admin mode', { name: 'application_name', redirect_uri: 'http://application.url', scopes: 'api' }
+
context 'authenticated and authorized user' do
it 'creates and returns an OAuth application' do
expect do
- post api('/applications', admin_user), params: { name: 'application_name', redirect_uri: 'http://application.url', scopes: scopes }
+ post api(path, admin, admin_mode: true), params: { name: 'application_name', redirect_uri: 'http://application.url', scopes: scopes }
end.to change { Doorkeeper::Application.count }.by 1
application = Doorkeeper::Application.find_by(name: 'application_name', redirect_uri: 'http://application.url')
- expect(response).to have_gitlab_http_status(:created)
expect(json_response).to be_a Hash
expect(json_response['application_id']).to eq application.uid
expect(json_response['secret']).to eq application.secret
@@ -28,7 +30,7 @@ RSpec.describe API::Applications, :api, feature_category: :authentication_and_au
it 'does not allow creating an application with the wrong redirect_uri format' do
expect do
- post api('/applications', admin_user), params: { name: 'application_name', redirect_uri: 'http://', scopes: scopes }
+ post api(path, admin, admin_mode: true), params: { name: 'application_name', redirect_uri: 'http://', scopes: scopes }
end.not_to change { Doorkeeper::Application.count }
expect(response).to have_gitlab_http_status(:bad_request)
@@ -38,7 +40,7 @@ RSpec.describe API::Applications, :api, feature_category: :authentication_and_au
it 'does not allow creating an application with a forbidden URI format' do
expect do
- post api('/applications', admin_user), params: { name: 'application_name', redirect_uri: 'javascript://alert()', scopes: scopes }
+ post api(path, admin, admin_mode: true), params: { name: 'application_name', redirect_uri: 'javascript://alert()', scopes: scopes }
end.not_to change { Doorkeeper::Application.count }
expect(response).to have_gitlab_http_status(:bad_request)
@@ -48,7 +50,7 @@ RSpec.describe API::Applications, :api, feature_category: :authentication_and_au
it 'does not allow creating an application without a name' do
expect do
- post api('/applications', admin_user), params: { redirect_uri: 'http://application.url', scopes: scopes }
+ post api(path, admin, admin_mode: true), params: { redirect_uri: 'http://application.url', scopes: scopes }
end.not_to change { Doorkeeper::Application.count }
expect(response).to have_gitlab_http_status(:bad_request)
@@ -58,7 +60,7 @@ RSpec.describe API::Applications, :api, feature_category: :authentication_and_au
it 'does not allow creating an application without a redirect_uri' do
expect do
- post api('/applications', admin_user), params: { name: 'application_name', scopes: scopes }
+ post api(path, admin, admin_mode: true), params: { name: 'application_name', scopes: scopes }
end.not_to change { Doorkeeper::Application.count }
expect(response).to have_gitlab_http_status(:bad_request)
@@ -68,7 +70,7 @@ RSpec.describe API::Applications, :api, feature_category: :authentication_and_au
it 'does not allow creating an application without specifying `scopes`' do
expect do
- post api('/applications', admin_user), params: { name: 'application_name', redirect_uri: 'http://application.url' }
+ post api(path, admin, admin_mode: true), params: { name: 'application_name', redirect_uri: 'http://application.url' }
end.not_to change { Doorkeeper::Application.count }
expect(response).to have_gitlab_http_status(:bad_request)
@@ -78,7 +80,7 @@ RSpec.describe API::Applications, :api, feature_category: :authentication_and_au
it 'does not allow creating an application with blank `scopes`' do
expect do
- post api('/applications', admin_user), params: { name: 'application_name', redirect_uri: 'http://application.url', scopes: '' }
+ post api(path, admin, admin_mode: true), params: { name: 'application_name', redirect_uri: 'http://application.url', scopes: '' }
end.not_to change { Doorkeeper::Application.count }
expect(response).to have_gitlab_http_status(:bad_request)
@@ -87,7 +89,7 @@ RSpec.describe API::Applications, :api, feature_category: :authentication_and_au
it 'does not allow creating an application with invalid `scopes`' do
expect do
- post api('/applications', admin_user), params: { name: 'application_name', redirect_uri: 'http://application.url', scopes: 'non_existent_scope' }
+ post api(path, admin, admin_mode: true), params: { name: 'application_name', redirect_uri: 'http://application.url', scopes: 'non_existent_scope' }
end.not_to change { Doorkeeper::Application.count }
expect(response).to have_gitlab_http_status(:bad_request)
@@ -97,7 +99,7 @@ RSpec.describe API::Applications, :api, feature_category: :authentication_and_au
context 'multiple scopes' do
it 'creates an application with multiple `scopes` when each scope specified is seperated by a space' do
expect do
- post api('/applications', admin_user), params: { name: 'application_name', redirect_uri: 'http://application.url', scopes: 'api read_user' }
+ post api(path, admin, admin_mode: true), params: { name: 'application_name', redirect_uri: 'http://application.url', scopes: 'api read_user' }
end.to change { Doorkeeper::Application.count }.by 1
application = Doorkeeper::Application.last
@@ -108,7 +110,7 @@ RSpec.describe API::Applications, :api, feature_category: :authentication_and_au
it 'does not allow creating an application with multiple `scopes` when one of the scopes is invalid' do
expect do
- post api('/applications', admin_user), params: { name: 'application_name', redirect_uri: 'http://application.url', scopes: 'api non_existent_scope' }
+ post api(path, admin, admin_mode: true), params: { name: 'application_name', redirect_uri: 'http://application.url', scopes: 'api non_existent_scope' }
end.not_to change { Doorkeeper::Application.count }
expect(response).to have_gitlab_http_status(:bad_request)
@@ -118,7 +120,7 @@ RSpec.describe API::Applications, :api, feature_category: :authentication_and_au
it 'defaults to creating an application with confidential' do
expect do
- post api('/applications', admin_user), params: { name: 'application_name', redirect_uri: 'http://application.url', scopes: scopes, confidential: nil }
+ post api(path, admin, admin_mode: true), params: { name: 'application_name', redirect_uri: 'http://application.url', scopes: scopes, confidential: nil }
end.to change { Doorkeeper::Application.count }.by(1)
expect(response).to have_gitlab_http_status(:created)
@@ -133,15 +135,13 @@ RSpec.describe API::Applications, :api, feature_category: :authentication_and_au
expect do
post api('/applications', user), params: { name: 'application_name', redirect_uri: 'http://application.url', scopes: scopes }
end.not_to change { Doorkeeper::Application.count }
-
- expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'non-authenticated user' do
it 'does not create application' do
expect do
- post api('/applications'), params: { name: 'application_name', redirect_uri: 'http://application.url' }
+ post api(path), params: { name: 'application_name', redirect_uri: 'http://application.url' }
end.not_to change { Doorkeeper::Application.count }
expect(response).to have_gitlab_http_status(:unauthorized)
@@ -150,26 +150,17 @@ RSpec.describe API::Applications, :api, feature_category: :authentication_and_au
end
describe 'GET /applications' do
- context 'authenticated and authorized user' do
- it 'can list application' do
- get api('/applications', admin_user)
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response).to be_a(Array)
- end
- end
+ it_behaves_like 'GET request permissions for admin mode'
- context 'authorized user without authorization' do
- it 'cannot list application' do
- get api('/applications', user)
+ it 'can list application' do
+ get api(path, admin, admin_mode: true)
- expect(response).to have_gitlab_http_status(:forbidden)
- end
+ expect(json_response).to be_a(Array)
end
context 'non-authenticated user' do
it 'cannot list application' do
- get api('/applications')
+ get api(path)
expect(response).to have_gitlab_http_status(:unauthorized)
end
@@ -177,33 +168,29 @@ RSpec.describe API::Applications, :api, feature_category: :authentication_and_au
end
describe 'DELETE /applications/:id' do
+ context 'user authorization' do
+ let!(:path) { "/applications/#{application.id}" }
+
+ it_behaves_like 'DELETE request permissions for admin mode'
+ end
+
context 'authenticated and authorized user' do
it 'can delete an application' do
expect do
- delete api("/applications/#{application.id}", admin_user)
+ delete api("#{path}/#{application.id}", admin, admin_mode: true)
end.to change { Doorkeeper::Application.count }.by(-1)
-
- expect(response).to have_gitlab_http_status(:no_content)
end
it 'cannot delete non-existing application' do
- delete api("/applications/#{non_existing_record_id}", admin_user)
+ delete api("#{path}/#{non_existing_record_id}", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:not_found)
end
end
- context 'authorized user without authorization' do
- it 'cannot delete an application' do
- delete api("/applications/#{application.id}", user)
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
-
context 'non-authenticated user' do
it 'cannot delete an application' do
- delete api("/applications/#{application.id}")
+ delete api("#{path}/#{application.id}")
expect(response).to have_gitlab_http_status(:unauthorized)
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 2d3afb489df..4e8f990fc10 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -362,8 +362,6 @@ RSpec.configure do |config|
./spec/requests/api/admin/instance_clusters_spec.rb
./spec/requests/api/admin/plan_limits_spec.rb
./spec/requests/api/admin/sidekiq_spec.rb
- ./spec/requests/api/appearance_spec.rb
- ./spec/requests/api/applications_spec.rb
./spec/requests/api/broadcast_messages_spec.rb
./spec/requests/api/ci/pipelines_spec.rb
./spec/requests/api/ci/runners_reset_registration_token_spec.rb
diff --git a/spec/support/shared_examples/requests/admin_mode_shared_examples.rb b/spec/support/shared_examples/requests/admin_mode_shared_examples.rb
new file mode 100644
index 00000000000..07fde7d3f35
--- /dev/null
+++ b/spec/support/shared_examples/requests/admin_mode_shared_examples.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+RSpec.shared_examples 'GET request permissions for admin mode' do
+ it_behaves_like 'GET request permissions for admin mode when user'
+ it_behaves_like 'GET request permissions for admin mode when admin'
+end
+
+RSpec.shared_examples 'PUT request permissions for admin mode' do |params|
+ it_behaves_like 'PUT request permissions for admin mode when user', params
+ it_behaves_like 'PUT request permissions for admin mode when admin', params
+end
+
+RSpec.shared_examples 'POST request permissions for admin mode' do |params|
+ it_behaves_like 'POST request permissions for admin mode when user', params
+ it_behaves_like 'POST request permissions for admin mode when admin', params
+end
+
+RSpec.shared_examples 'DELETE request permissions for admin mode' do
+ it_behaves_like 'DELETE request permissions for admin mode when user'
+ it_behaves_like 'DELETE request permissions for admin mode when admin'
+end
+
+RSpec.shared_examples 'GET request permissions for admin mode when user' do
+ subject { get api(path, current_user, admin_mode: admin_mode) }
+
+ let_it_be(:current_user) { create(:user) }
+
+ it_behaves_like 'admin mode on', true, :forbidden
+ it_behaves_like 'admin mode on', false, :forbidden
+end
+
+RSpec.shared_examples 'GET request permissions for admin mode when admin' do
+ subject { get api(path, current_user, admin_mode: admin_mode) }
+
+ let_it_be(:current_user) { create(:admin) }
+
+ it_behaves_like 'admin mode on', true, :ok
+ it_behaves_like 'admin mode on', false, :forbidden
+end
+
+RSpec.shared_examples 'PUT request permissions for admin mode when user' do |params|
+ subject { put api(path, current_user, admin_mode: admin_mode), params: params }
+
+ let_it_be(:current_user) { create(:user) }
+
+ it_behaves_like 'admin mode on', true, :forbidden
+ it_behaves_like 'admin mode on', false, :forbidden
+end
+
+RSpec.shared_examples 'PUT request permissions for admin mode when admin' do |params|
+ subject { put api(path, current_user, admin_mode: admin_mode), params: params }
+
+ let_it_be(:current_user) { create(:admin) }
+
+ it_behaves_like 'admin mode on', true, :ok
+ it_behaves_like 'admin mode on', false, :forbidden
+end
+
+RSpec.shared_examples 'POST request permissions for admin mode when user' do |params|
+ subject { post api(path, current_user, admin_mode: admin_mode), params: params }
+
+ let_it_be(:current_user) { create(:user) }
+
+ it_behaves_like 'admin mode on', true, :forbidden
+ it_behaves_like 'admin mode on', false, :forbidden
+end
+
+RSpec.shared_examples 'POST request permissions for admin mode when admin' do |params|
+ subject { post api(path, current_user, admin_mode: admin_mode), params: params }
+
+ let_it_be(:current_user) { create(:admin) }
+
+ it_behaves_like 'admin mode on', true, :created
+ it_behaves_like 'admin mode on', false, :forbidden
+end
+
+RSpec.shared_examples 'DELETE request permissions for admin mode when user' do
+ subject { delete api(path, current_user, admin_mode: admin_mode) }
+
+ let_it_be(:current_user) { create(:user) }
+
+ it_behaves_like 'admin mode on', true, :forbidden
+ it_behaves_like 'admin mode on', false, :forbidden
+end
+
+RSpec.shared_examples 'DELETE request permissions for admin mode when admin' do
+ subject { delete api(path, current_user, admin_mode: admin_mode) }
+
+ let_it_be(:current_user) { create(:admin) }
+
+ it_behaves_like 'admin mode on', true, :no_content
+ it_behaves_like 'admin mode on', false, :forbidden
+end
+
+RSpec.shared_examples "admin mode on" do |admin_mode, status|
+ let_it_be(:admin_mode) { admin_mode }
+
+ it_behaves_like 'returning response status', status
+end