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:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-02-21 15:11:26 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-02-21 15:11:26 +0300
commiteac99f198f2834788c38108bcee3a2c567fba9e0 (patch)
tree427b3e304f49b62a207f345d2ebb9ded47ee63d4
parent59ffff96dfdb114c112d14cedc5dfd9553cf568b (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.rubocop_todo/rspec/missing_feature_category.yml1
-rw-r--r--app/assets/javascripts/boards/components/board_list_header.vue128
-rw-r--r--app/assets/javascripts/projects/commit/components/form_modal.vue1
-rw-r--r--app/controllers/repositories/git_http_controller.rb1
-rw-r--r--config/initializers_before_autoloader/001_fast_gettext.rb1
-rw-r--r--db/post_migrate/20230214181633_finalize_ci_build_needs_big_int_conversion.rb18
-rw-r--r--db/post_migrate/20230221010522_prepare_async_foreign_key_validation_for_ci_sources_pipelines.rb15
-rw-r--r--db/post_migrate/20230221011750_prepare_async_foreign_key_validation_for_ci_job_variables.rb15
-rw-r--r--db/schema_migrations/202302141816331
-rw-r--r--db/schema_migrations/202302210105221
-rw-r--r--db/schema_migrations/202302210117501
-rw-r--r--doc/administration/geo/replication/container_registry.md2
-rw-r--r--doc/administration/geo/replication/troubleshooting.md4
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md5
-rw-r--r--doc/administration/object_storage.md6
-rw-r--r--doc/ci/runners/saas/macos_saas_runner.md2
-rw-r--r--doc/development/application_slis/index.md2
-rw-r--r--doc/development/application_slis/rails_request.md (renamed from doc/development/application_slis/rails_request_apdex.md)26
-rw-r--r--doc/development/contributing/merge_request_workflow.md2
-rw-r--r--doc/development/stage_group_observability/index.md6
-rw-r--r--doc/integration/partner_marketplace.md2
-rw-r--r--doc/operations/error_tracking.md2
-rw-r--r--doc/operations/feature_flags.md2
-rw-r--r--doc/update/index.md6
-rw-r--r--doc/user/infrastructure/iac/terraform_state.md2
-rw-r--r--doc/user/packages/npm_registry/index.md2
-rw-r--r--doc/user/permissions.md2
-rw-r--r--lib/api/draft_notes.rb42
-rw-r--r--lib/gitlab/i18n.rb5
-rw-r--r--lib/gitlab/i18n/pluralization.rb86
-rw-r--r--lib/gitlab/instrumentation/redis_base.rb4
-rw-r--r--lib/gitlab/instrumentation/redis_interceptor.rb1
-rw-r--r--lib/gitlab/redis/cache.rb13
-rw-r--r--lib/gitlab/redis/rate_limiting.rb6
-rw-r--r--lib/gitlab/redis/repository_cache.rb3
-rw-r--r--locale/gitlab.pot58
-rw-r--r--spec/controllers/repositories/git_http_controller_spec.rb4
-rw-r--r--spec/features/boards/boards_spec.rb4
-rw-r--r--spec/features/boards/new_issue_spec.rb50
-rw-r--r--spec/features/groups/board_spec.rb6
-rw-r--r--spec/frontend/boards/components/board_list_header_spec.js105
-rw-r--r--spec/lib/gitlab/i18n/pluralization_spec.rb53
-rw-r--r--spec/lib/gitlab/i18n_spec.rb17
-rw-r--r--spec/lib/gitlab/instrumentation/redis_base_spec.rb12
-rw-r--r--spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb2
-rw-r--r--spec/lib/gitlab/redis/cache_spec.rb17
-rw-r--r--spec/lib/gitlab/redis/rate_limiting_spec.rb17
-rw-r--r--spec/lib/gitlab/redis/repository_cache_spec.rb15
-rw-r--r--spec/requests/api/draft_notes_spec.rb96
-rw-r--r--spec/requests/git_http_spec.rb18
-rw-r--r--spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb12
51 files changed, 663 insertions, 239 deletions
diff --git a/.rubocop_todo/rspec/missing_feature_category.yml b/.rubocop_todo/rspec/missing_feature_category.yml
index 9ceeab04049..6c5694293f8 100644
--- a/.rubocop_todo/rspec/missing_feature_category.yml
+++ b/.rubocop_todo/rspec/missing_feature_category.yml
@@ -4904,7 +4904,6 @@ RSpec/MissingFeatureCategory:
- 'spec/lib/gitlab/i18n/metadata_entry_spec.rb'
- 'spec/lib/gitlab/i18n/po_linter_spec.rb'
- 'spec/lib/gitlab/i18n/translation_entry_spec.rb'
- - 'spec/lib/gitlab/i18n_spec.rb'
- 'spec/lib/gitlab/identifier_spec.rb'
- 'spec/lib/gitlab/import/database_helpers_spec.rb'
- 'spec/lib/gitlab/import/import_failure_service_spec.rb'
diff --git a/app/assets/javascripts/boards/components/board_list_header.vue b/app/assets/javascripts/boards/components/board_list_header.vue
index 749fae0c426..09ee6e50881 100644
--- a/app/assets/javascripts/boards/components/board_list_header.vue
+++ b/app/assets/javascripts/boards/components/board_list_header.vue
@@ -1,18 +1,18 @@
<script>
import {
GlButton,
- GlButtonGroup,
GlLabel,
GlTooltip,
GlIcon,
GlSprintf,
GlTooltipDirective,
+ GlDisclosureDropdown,
} from '@gitlab/ui';
import { mapActions, mapGetters, mapState } from 'vuex';
import { isListDraggable } from '~/boards/boards_util';
import { isScopedLabel, parseBoolean } from '~/lib/utils/common_utils';
import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
-import { n__, s__, __ } from '~/locale';
+import { n__, s__ } from '~/locale';
import sidebarEventHub from '~/sidebar/event_hub';
import Tracking from '~/tracking';
import { formatDate } from '~/lib/utils/datetime_utility';
@@ -25,14 +25,15 @@ import ItemCount from './item_count.vue';
export default {
i18n: {
- newIssue: __('New issue'),
- newEpic: s__('Boards|New epic'),
- listSettings: __('List settings'),
+ newIssue: s__('Boards|Create new issue'),
+ listActions: s__('Boards|List actions'),
+ newEpic: s__('Boards|Create new epic'),
+ listSettings: s__('Boards|Edit list settings'),
expand: s__('Boards|Expand'),
collapse: s__('Boards|Collapse'),
},
components: {
- GlButtonGroup,
+ GlDisclosureDropdown,
GlButton,
GlLabel,
GlTooltip,
@@ -111,7 +112,10 @@ export default {
},
showListHeaderActions() {
if (this.isLoggedIn) {
- return this.isNewIssueShown || this.isNewEpicShown || this.isSettingsShown;
+ return (
+ (this.isNewIssueShown || this.isNewEpicShown || this.isSettingsShown) &&
+ !this.list.collapsed
+ );
}
return false;
},
@@ -162,6 +166,50 @@ export default {
canShowTotalWeight() {
return this.weightFeatureAvailable && !this.isLoading;
},
+ actionListItems() {
+ const items = [];
+
+ if (this.isNewIssueShown) {
+ const newIssueText = this.$options.i18n.newIssue;
+ items.push({
+ text: newIssueText,
+ action: this.showNewIssueForm,
+ extraAttrs: {
+ 'data-testid': 'newIssueBtn',
+ title: newIssueText,
+ 'aria-label': newIssueText,
+ },
+ });
+ }
+
+ if (this.isNewEpicShown) {
+ const newEpicText = this.$options.i18n.newEpic;
+ items.push({
+ text: newEpicText,
+ action: this.showNewEpicForm,
+ extraAttrs: {
+ 'data-testid': 'newEpicBtn',
+ title: newEpicText,
+ 'aria-label': newEpicText,
+ },
+ });
+ }
+
+ if (this.isSettingsShown) {
+ const listSettingsText = this.$options.i18n.listSettings;
+ items.push({
+ text: listSettingsText,
+ action: this.openSidebarSettings,
+ extraAttrs: {
+ 'data-testid': 'settingsBtn',
+ title: listSettingsText,
+ 'aria-label': listSettingsText,
+ },
+ });
+ }
+
+ return items;
+ },
},
apollo: {
boardList: {
@@ -188,6 +236,9 @@ export default {
},
methods: {
...mapActions(['updateList', 'setActiveId', 'toggleListCollapsed']),
+ closeListActions() {
+ this.$refs.headerListActions?.close();
+ },
openSidebarSettings() {
if (this.activeId === inactiveId) {
sidebarEventHub.$emit('sidebar.closeAll');
@@ -196,11 +247,12 @@ export default {
this.setActiveId({ id: this.list.id, sidebarType: LIST });
this.track('click_button', { label: 'list_settings' });
+
+ this.closeListActions();
},
showScopedLabels(label) {
return this.scopedLabelsAvailable && isScopedLabel(label);
},
-
showNewIssueForm() {
if (this.isSwimlanesOn) {
eventHub.$emit('open-unassigned-lane');
@@ -210,9 +262,13 @@ export default {
} else {
eventHub.$emit(`${toggleFormEventPrefix.issue}${this.list.id}`);
}
+
+ this.closeListActions();
},
showNewEpicForm() {
eventHub.$emit(`${toggleFormEventPrefix.epic}${this.list.id}`);
+
+ this.closeListActions();
},
toggleExpanded() {
const collapsed = !this.list.collapsed;
@@ -407,44 +463,24 @@ export default {
<!-- EE end -->
</span>
</div>
- <gl-button-group v-if="showListHeaderActions" class="board-list-button-group gl-pl-2">
- <gl-button
- v-if="isNewIssueShown"
- v-show="!list.collapsed"
- ref="newIssueBtn"
- v-gl-tooltip.hover
- :aria-label="$options.i18n.newIssue"
- :title="$options.i18n.newIssue"
- class="no-drag"
- size="small"
- icon="plus"
- @click="showNewIssueForm"
- />
-
- <gl-button
- v-if="isNewEpicShown"
- v-show="!list.collapsed"
- v-gl-tooltip.hover
- :aria-label="$options.i18n.newEpic"
- :title="$options.i18n.newEpic"
- class="no-drag"
- size="small"
- icon="plus"
- @click="showNewEpicForm"
- />
-
- <gl-button
- v-if="isSettingsShown"
- ref="settingsBtn"
- v-gl-tooltip.hover
- :aria-label="$options.i18n.listSettings"
- class="no-drag"
- size="small"
- :title="$options.i18n.listSettings"
- icon="settings"
- @click="openSidebarSettings"
- />
- </gl-button-group>
+ <gl-disclosure-dropdown
+ v-if="showListHeaderActions"
+ ref="headerListActions"
+ v-gl-tooltip.hover.top="{
+ title: $options.i18n.listActions,
+ boundary: 'viewport',
+ }"
+ data-testid="header-list-actions"
+ class="gl-py-2 gl-ml-3"
+ :aria-label="$options.i18n.listActions"
+ :title="$options.i18n.listActions"
+ category="tertiary"
+ icon="ellipsis_v"
+ :text-sr-only="true"
+ :items="actionListItems"
+ no-caret
+ placement="right"
+ />
</h3>
</header>
</template>
diff --git a/app/assets/javascripts/projects/commit/components/form_modal.vue b/app/assets/javascripts/projects/commit/components/form_modal.vue
index f78afef1c17..05e526d51b1 100644
--- a/app/assets/javascripts/projects/commit/components/form_modal.vue
+++ b/app/assets/javascripts/projects/commit/components/form_modal.vue
@@ -85,7 +85,6 @@ export default {
]),
},
mounted() {
- this.setSelectedProject(this.targetProjectId);
eventHub.$on(this.openModal, this.show);
},
methods: {
diff --git a/app/controllers/repositories/git_http_controller.rb b/app/controllers/repositories/git_http_controller.rb
index bd3461d8331..018d98f2786 100644
--- a/app/controllers/repositories/git_http_controller.rb
+++ b/app/controllers/repositories/git_http_controller.rb
@@ -8,6 +8,7 @@ module Repositories
prepend_before_action :deny_head_requests, only: [:info_refs]
rescue_from Gitlab::GitAccess::ForbiddenError, with: :render_403_with_exception
+ rescue_from JWT::DecodeError, with: :render_403_with_exception
rescue_from Gitlab::GitAccess::NotFoundError, with: :render_404_with_exception
rescue_from Gitlab::GitAccessProject::CreationError, with: :render_422_with_exception
rescue_from Gitlab::GitAccess::TimeoutError, with: :render_503_with_exception
diff --git a/config/initializers_before_autoloader/001_fast_gettext.rb b/config/initializers_before_autoloader/001_fast_gettext.rb
index 3d54ed8f32f..01aec95ce53 100644
--- a/config/initializers_before_autoloader/001_fast_gettext.rb
+++ b/config/initializers_before_autoloader/001_fast_gettext.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require_relative '../../lib/gitlab/i18n'
+require_relative '../../lib/gitlab/i18n/pluralization'
Gitlab::I18n.setup(domain: 'gitlab', default_locale: :en)
diff --git a/db/post_migrate/20230214181633_finalize_ci_build_needs_big_int_conversion.rb b/db/post_migrate/20230214181633_finalize_ci_build_needs_big_int_conversion.rb
new file mode 100644
index 00000000000..11870b9c540
--- /dev/null
+++ b/db/post_migrate/20230214181633_finalize_ci_build_needs_big_int_conversion.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class FinalizeCiBuildNeedsBigIntConversion < Gitlab::Database::Migration[2.1]
+ restrict_gitlab_migration gitlab_schema: :gitlab_ci
+
+ TABLE_NAME = 'ci_build_needs'
+
+ def up
+ ensure_batched_background_migration_is_finished(
+ job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
+ table_name: TABLE_NAME,
+ column_name: 'id',
+ job_arguments: [['id'], ['id_convert_to_bigint']]
+ )
+ end
+
+ def down; end
+end
diff --git a/db/post_migrate/20230221010522_prepare_async_foreign_key_validation_for_ci_sources_pipelines.rb b/db/post_migrate/20230221010522_prepare_async_foreign_key_validation_for_ci_sources_pipelines.rb
new file mode 100644
index 00000000000..e25bbca5c80
--- /dev/null
+++ b/db/post_migrate/20230221010522_prepare_async_foreign_key_validation_for_ci_sources_pipelines.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class PrepareAsyncForeignKeyValidationForCiSourcesPipelines < Gitlab::Database::Migration[2.1]
+ TABLE_NAME = :ci_sources_pipelines
+ COLUMN_NAMES = [:source_partition_id, :source_job_id]
+ FOREIGN_KEY_NAME = :fk_be5624bf37_p
+
+ def up
+ prepare_async_foreign_key_validation(TABLE_NAME, COLUMN_NAMES, name: FOREIGN_KEY_NAME)
+ end
+
+ def down
+ unprepare_async_foreign_key_validation(TABLE_NAME, COLUMN_NAMES, name: FOREIGN_KEY_NAME)
+ end
+end
diff --git a/db/post_migrate/20230221011750_prepare_async_foreign_key_validation_for_ci_job_variables.rb b/db/post_migrate/20230221011750_prepare_async_foreign_key_validation_for_ci_job_variables.rb
new file mode 100644
index 00000000000..973c4c7316d
--- /dev/null
+++ b/db/post_migrate/20230221011750_prepare_async_foreign_key_validation_for_ci_job_variables.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class PrepareAsyncForeignKeyValidationForCiJobVariables < Gitlab::Database::Migration[2.1]
+ TABLE_NAME = :ci_job_variables
+ COLUMN_NAMES = [:partition_id, :job_id]
+ FOREIGN_KEY_NAME = :fk_rails_fbf3b34792_p
+
+ def up
+ prepare_async_foreign_key_validation(TABLE_NAME, COLUMN_NAMES, name: FOREIGN_KEY_NAME)
+ end
+
+ def down
+ unprepare_async_foreign_key_validation(TABLE_NAME, COLUMN_NAMES, name: FOREIGN_KEY_NAME)
+ end
+end
diff --git a/db/schema_migrations/20230214181633 b/db/schema_migrations/20230214181633
new file mode 100644
index 00000000000..76d4cf10b5c
--- /dev/null
+++ b/db/schema_migrations/20230214181633
@@ -0,0 +1 @@
+a8c815d1d85a6690755623b53e15e5fb73f7e6be6a24ead3532f21d21c1de20f \ No newline at end of file
diff --git a/db/schema_migrations/20230221010522 b/db/schema_migrations/20230221010522
new file mode 100644
index 00000000000..57727695f5e
--- /dev/null
+++ b/db/schema_migrations/20230221010522
@@ -0,0 +1 @@
+474f2b46179134270dc65c2b32ef9acfb01edb976f5efefe9ab49352d3fc390f \ No newline at end of file
diff --git a/db/schema_migrations/20230221011750 b/db/schema_migrations/20230221011750
new file mode 100644
index 00000000000..9f43d53a1b2
--- /dev/null
+++ b/db/schema_migrations/20230221011750
@@ -0,0 +1 @@
+cca7c1c232fa56e85bb0fc120a5920f3cb75d0fea2657ed2e81a4fe69349134a \ No newline at end of file
diff --git a/doc/administration/geo/replication/container_registry.md b/doc/administration/geo/replication/container_registry.md
index 88ca8781dc3..fea3af3613d 100644
--- a/doc/administration/geo/replication/container_registry.md
+++ b/doc/administration/geo/replication/container_registry.md
@@ -7,7 +7,7 @@ type: howto
# Container Registry for a secondary site **(PREMIUM SELF)**
-You can set up a Container Registry on your **secondary** Geo site that mirrors the one on the **primary** Geo site.
+You can set up a Container Registry on your **secondary** Geo site that mirrors the one on the **primary** Geo site.
NOTE:
The Container Registry replication is used only for disaster recovery purposes. We do not recommend
diff --git a/doc/administration/geo/replication/troubleshooting.md b/doc/administration/geo/replication/troubleshooting.md
index 403f8525d39..5940c1f5580 100644
--- a/doc/administration/geo/replication/troubleshooting.md
+++ b/doc/administration/geo/replication/troubleshooting.md
@@ -1353,7 +1353,7 @@ If you have installed GitLab using the Linux package (Omnibus) and have configur
- `15.6.0`-`15.6.3`
- `15.7.0`-`15.7.1`
-This is due to [a bug introduced in the included version of cURL](https://github.com/curl/curl/issues/10122) shipped with Omnibus GitLab 15.4.6 and later. You are encouraged to upgrade to a later version where this has been [fixed](https://about.gitlab.com/releases/2023/01/09/security-release-gitlab-15-7-2-released/).
+This is due to [a bug introduced in the included version of cURL](https://github.com/curl/curl/issues/10122) shipped with Omnibus GitLab 15.4.6 and later. You are encouraged to upgrade to a later version where this has been [fixed](https://about.gitlab.com/releases/2023/01/09/security-release-gitlab-15-7-2-released/).
The bug causes all wildcard domains (`.example.com`) to be ignored except for the last on in the `no_proxy` environment variable list. Therefore, if for any reason you cannot upgrade to a newer version, you can work around the issue by moving your wildcard domain to the end of the list:
@@ -1368,7 +1368,7 @@ The bug causes all wildcard domains (`.example.com`) to be ignored except for th
```shell
sudo gitlab-ctl reconfigure
- ```
+ ```
You can have only one wildcard domain in the `no_proxy` list.
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index ed55e7d7ff3..9d15d4bbb46 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -163,6 +163,9 @@ The following metrics are available:
| `gitlab_diffs_render_real_duration_seconds` | Histogram | 15.8 | Duration in seconds spent on serializing and rendering diffs on diffs batch request | `controller`, `action` |
| `gitlab_memwd_violations_total` | Counter | 15.9 | Total number of times a Ruby process violated a memory threshold | |
| `gitlab_memwd_violations_handled_total` | Counter | 15.9 | Total number of times Ruby process memory violations were handled | |
+| `gitlab_sli_rails_request_apdex_total` | Counter | 14.4 | Total number of request Apdex measurements. For more information, see [Rails request SLIs](../../../development/application_slis/rails_request.md) | `endpoint_id`, `feature_category`, `request_urgency` |
+| `gitlab_sli_rails_request_apdex_success_total` | Counter | 14.4 | Total number of successful requests that met the target duration for their urgency. Divide by `gitlab_sli_rails_requests_apdex_total` to get a success ratio | `endpoint_id`, `feature_category`, `request_urgency` |
+| `gitlab_sli_rails_request_error_total` | Counter | 15.7 | Total number of request error measurements. For more information, see [Rails request SLIs](../../../development/application_slis/rails_request.md) | `endpoint_id`, `feature_category`, `request_urgency`, `error` |
## Metrics controlled by a feature flag
@@ -320,8 +323,6 @@ configuration option in `gitlab.yml`. These metrics are served from the
| `geo_container_repositories_synced` | Gauge | 15.4 | Number of container repositories synced on secondary | `url` |
| `geo_container_repositories_failed` | Gauge | 15.4 | Number of syncable container repositories failed to sync on secondary | `url` |
| `geo_container_repositories_registry` | Gauge | 15.4 | Number of container repositories in the registry | `url` |
-| `gitlab_sli:rails_request_apdex:total` | Counter | 14.4 | The number of request-apdex measurements, [more information the development documentation](../../../development/application_slis/rails_request_apdex.md) | `endpoint_id`, `feature_category`, `request_urgency` |
-| `gitlab_sli:rails_request_apdex:success_total` | Counter | 14.4 | The number of successful requests that met the target duration for their urgency. Divide by `gitlab_sli:rails_requests_apdex:total` to get a success ratio | `endpoint_id`, `feature_category`, `request_urgency` |
| `geo_ci_secure_files` | Gauge | 15.3 | Number of secure files on primary | `url` |
| `geo_ci_secure_files_checksum_total` | Gauge | 15.3 | Number of secure files tried to checksum on primary | `url` |
| `geo_ci_secure_files_checksummed` | Gauge | 15.3 | Number of secure files successfully checksummed on primary | `url` |
diff --git a/doc/administration/object_storage.md b/doc/administration/object_storage.md
index 4b3e251d407..aaa5f7b90b0 100644
--- a/doc/administration/object_storage.md
+++ b/doc/administration/object_storage.md
@@ -912,9 +912,9 @@ When this is used, GitLab fetches temporary credentials each time an
S3 bucket is accessed, so no hard-coded values are needed in the
configuration.
-To use an Amazon instance profile, GitLab must be able to connect to the
-[instance metadata endpoint](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html).
-If GitLab is [configured to use an Internet proxy](https://docs.gitlab.com/omnibus/settings/environment-variables.html), the endpoint IP
+To use an Amazon instance profile, GitLab must be able to connect to the
+[instance metadata endpoint](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html).
+If GitLab is [configured to use an Internet proxy](https://docs.gitlab.com/omnibus/settings/environment-variables.html), the endpoint IP
address must be added to the `no_proxy` list.
#### Encrypted S3 buckets
diff --git a/doc/ci/runners/saas/macos_saas_runner.md b/doc/ci/runners/saas/macos_saas_runner.md
index 96e294ff828..21120732fbe 100644
--- a/doc/ci/runners/saas/macos_saas_runner.md
+++ b/doc/ci/runners/saas/macos_saas_runner.md
@@ -84,4 +84,4 @@ In SaaS runners on macOS, the objective is to make 90% of CI jobs start executin
- If the VM image does not include the specific software version you need for your job, then the job execution time will increase as the required software needs to be fetched and installed.
- At this time, it is not possible to bring your own OS image.
-- The keychain for user `gitlab` is not publicly available. You must create a keychain instead.
+- The keychain for user `gitlab` is not publicly available. You must create a keychain instead.
diff --git a/doc/development/application_slis/index.md b/doc/development/application_slis/index.md
index bd4587333e0..f48088a6e08 100644
--- a/doc/development/application_slis/index.md
+++ b/doc/development/application_slis/index.md
@@ -24,7 +24,7 @@ to be emitted from the rails application:
## Existing SLIs
-1. [`rails_request_apdex`](rails_request_apdex.md)
+1. [`rails_request`](rails_request.md)
1. `global_search_apdex`
1. `global_search_error_rate`
1. `global_search_indexing_apdex`
diff --git a/doc/development/application_slis/rails_request_apdex.md b/doc/development/application_slis/rails_request.md
index dc9d67b0a2b..fa22b5f6aca 100644
--- a/doc/development/application_slis/rails_request_apdex.md
+++ b/doc/development/application_slis/rails_request.md
@@ -4,7 +4,7 @@ group: Scalability
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# Rails request Apdex SLI
+# Rails request SLIs (service level indicators)
> [Introduced](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/525) in GitLab 14.4
@@ -12,21 +12,33 @@ NOTE:
This SLI is used for service monitoring. But not for [error budgets for stage groups](../stage_group_observability/index.md#error-budget)
by default. You can [opt in](#error-budget-attribution-and-ownership).
-The request Apdex SLI (Service Level Indicator) is [an SLI defined in the application](index.md).
-It measures the duration of successful requests as an indicator for
+The request Apdex SLI and the error rate SLI are [SLIs defined in the application](index.md).
+
+The request Apdex measures the duration of successful requests as an indicator for
application performance. This includes the REST and GraphQL API, and the
-regular controller endpoints. It consists of these counters:
+regular controller endpoints.
+
+The error rate measures unsuccessful requests as an indicator for
+server misbehavior. This includes the REST API, and the
+regular controller endpoints.
-1. `gitlab_sli:rails_request_apdex:total`: This counter gets
+1. `gitlab_sli_rails_request_apdex_total`: This counter gets
incremented for every request that did not result in a response
with a `5xx` status code. It ensures slow failures are not
counted twice, because the request is already counted in the error SLI.
-1. `gitlab_sli:rails_request_apdex:success_total`: This counter gets
+1. `gitlab_sli_rails_request_apdex_success_total`: This counter gets
incremented for every successful request that performed faster than
the [defined target duration depending on the endpoint's urgency](#adjusting-request-urgency).
-Both these counters are labeled with:
+1. `gitlab_sli_rails_request_error_total`: This counter gets
+ incremented for every request that resulted in a response
+ with a `5xx` status code.
+
+1. `gitlab_sli_rails_request_total`: This counter gets
+ incremented for every request.
+
+These counters are labeled with:
1. `endpoint_id`: The identification of the Rails Controller or the
Grape-API endpoint.
diff --git a/doc/development/contributing/merge_request_workflow.md b/doc/development/contributing/merge_request_workflow.md
index 01bfdae5999..87308b68582 100644
--- a/doc/development/contributing/merge_request_workflow.md
+++ b/doc/development/contributing/merge_request_workflow.md
@@ -13,7 +13,7 @@ for community contributions have the [`Seeking community contributions`](issue_w
label, but you are free to contribute to any issue you want.
If an issue is marked for the current milestone at any time, even
-when you are working on it, a GitLab team member may take over the merge request to ensure the work is finished before the release date.
+when you are working on it, a GitLab team member may take over the merge request to ensure the work is finished before the release date.
If a contributor is no longer actively working on a submitted merge request,
we can:
diff --git a/doc/development/stage_group_observability/index.md b/doc/development/stage_group_observability/index.md
index b275b0bfec2..d549123968c 100644
--- a/doc/development/stage_group_observability/index.md
+++ b/doc/development/stage_group_observability/index.md
@@ -68,11 +68,11 @@ component can have two indicators:
and
[Web](https://gitlab.com/gitlab-com/runbooks/-/blob/f22f40b2c2eab37d85e23ccac45e658b2c914445/metrics-catalog/services/web.jsonnet#L154)
services, that threshold is **5 seconds** when not opted in to the
- [`rails_requests` SLI](../application_slis/rails_request_apdex.md).
+ [`rails_request` SLI](../application_slis/rails_request.md).
We've made this target configurable in [this project](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/525).
- To learn how to customize the request Apdex, see
- [Rails request Apdex SLI](../application_slis/rails_request_apdex.md).
+ To customize the request Apdex, see
+ [Rails request SLIs](../application_slis/rails_request.md).
This new Apdex measurement is not part of the error budget until you
[opt in](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1451).
diff --git a/doc/integration/partner_marketplace.md b/doc/integration/partner_marketplace.md
index 57c409824a5..a1e55561a7a 100644
--- a/doc/integration/partner_marketplace.md
+++ b/doc/integration/partner_marketplace.md
@@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Marketplace partner integration
GitLab supports automation for selected distribution marketplaces to process sales of GitLab products to authorized
-channel partners. Marketplace partners can use the GitLab Marketplace APIs to integrate their systems with GitLab to
+channel partners. Marketplace partners can use the GitLab Marketplace APIs to integrate their systems with GitLab to
sell GitLab subscriptions on their site.
This document's target audience is third-party developers for Marketplace partners.
diff --git a/doc/operations/error_tracking.md b/doc/operations/error_tracking.md
index cb46b08c279..b52a2599ce9 100644
--- a/doc/operations/error_tracking.md
+++ b/doc/operations/error_tracking.md
@@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
WARNING:
This feature is in its end-of-life process. It is [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/389991)
-for use in GitLab 15.9, and is planned for removal in GitLab 16.0. We are replacing this feature with functionality in the [GitLab Observability UI](https://gitlab.com/gitlab-org/opstrace/opstrace-ui). Please also reference our direction for [Observability](https://about.gitlab.com/direction/monitor/observability/) and [data visualization](https://about.gitlab.com/direction/monitor/observability/data-visualization/).
+for use in GitLab 15.9, and is planned for removal in GitLab 16.0. We are replacing this feature with functionality in the [GitLab Observability UI](https://gitlab.com/gitlab-org/opstrace/opstrace-ui). Please also reference our direction for [Observability](https://about.gitlab.com/direction/monitor/observability/) and [data visualization](https://about.gitlab.com/direction/monitor/observability/data-visualization/).
Error Tracking allows developers to discover and view errors generated by their application. Because error information is surfaced where the code is being developed, efficiency and awareness are increased.
diff --git a/doc/operations/feature_flags.md b/doc/operations/feature_flags.md
index 68fc0fb9499..4da7a57d327 100644
--- a/doc/operations/feature_flags.md
+++ b/doc/operations/feature_flags.md
@@ -151,7 +151,7 @@ Enables the feature for a list of target users. It is implemented
using the Unleash UserIDs (`userWithId`) activation [strategy](https://docs.getunleash.io/reference/activation-strategies#userids).
Enter user IDs as a comma-separated list of values (for example,
-`user@example.com, user2@example.com`, or `username1,username2,username3`, and so on).
+`user@example.com, user2@example.com`, or `username1,username2,username3`, and so on).
User IDs are identifiers for your application users. They do not need to be GitLab users.
WARNING:
diff --git a/doc/update/index.md b/doc/update/index.md
index e6b7dc69b24..77c19097aed 100644
--- a/doc/update/index.md
+++ b/doc/update/index.md
@@ -520,10 +520,10 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
- GitLab Runner 15.7.0 introduced a breaking change that impacts CI/CD jobs: [Correctly handle expansion of job file variables](https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/3613).
Previously, job-defined variables that referred to
[file type variables](../ci/variables/index.md#use-file-type-cicd-variables)
- were expanded to the value of the file variable (its content). This behavior did not
+ were expanded to the value of the file variable (its content). This behavior did not
respect the typical rules of shell variable expansion. There was also the potential
- that secrets or sensitive information could leak if the file variable and its
- contents printed. For example, if they were printed in an echo output. For more information,
+ that secrets or sensitive information could leak if the file variable and its
+ contents printed. For example, if they were printed in an echo output. For more information,
see [Understanding the file type variable expansion change in GitLab 15.7](https://about.gitlab.com/blog/2023/02/13/impact-of-the-file-type-variable-change-15-7/).
- Geo: [Container registry push events are rejected](https://gitlab.com/gitlab-org/gitlab/-/issues/386389) by the `/api/v4/container_registry_event/events` endpoint resulting in Geo secondary sites not being aware of updates to container registry images and subsequently not replicating the updates. Secondary sites may contain out of date container images after a failover as a consequence. This impacts versions 15.6.0 - 15.6.6 and 15.7.0 - 15.7.2. If you're using Geo with container repositories, you are advised to upgrade to GitLab 15.6.7, 15.7.3, or 15.8.0 which contain a fix for this issue and avoid potential data loss after a failover.
- Due to [a bug introduced in GitLab 15.4](https://gitlab.com/gitlab-org/gitlab/-/issues/390155), if one or more Git repositories in Gitaly Cluster is [unavailable](../administration/gitaly/recovery.md#unavailable-repositories), then [Repository checks](../administration/repository_checks.md#repository-checks) and [Geo replication and verification](../administration/geo/index.md) stop running for all project or project wiki repositories in the affected Gitaly Cluster. The bug was fixed by [reverting the change in GitLab 15.9.0](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110823). Before upgrading to this version, check if you have any "unavailable" repositories. See [the bug issue](https://gitlab.com/gitlab-org/gitlab/-/issues/390155) for more information.
diff --git a/doc/user/infrastructure/iac/terraform_state.md b/doc/user/infrastructure/iac/terraform_state.md
index 93a82023480..ad4863c226f 100644
--- a/doc/user/infrastructure/iac/terraform_state.md
+++ b/doc/user/infrastructure/iac/terraform_state.md
@@ -58,7 +58,7 @@ WARNING:
Like any other job artifact, Terraform plan data is viewable by anyone with the Guest role on the repository.
Neither Terraform nor GitLab encrypts the plan file by default. If your Terraform plan
includes sensitive data, like passwords, access tokens, or certificates, you should
-encrypt plan output or modify the project visibility settings. We also strongly recommend that you **disable**
+encrypt plan output or modify the project visibility settings. We also strongly recommend that you **disable**
[public pipelines](../../../ci/pipelines/settings.md#change-pipeline-visibility-for-non-project-members-in-public-projects)
by setting the artifact's public flag to false (`public: false`). This setting ensures artifacts are
accessible only to GitLab Administrators and project members with the Reporter role and above.
diff --git a/doc/user/packages/npm_registry/index.md b/doc/user/packages/npm_registry/index.md
index 11e3d0e5131..7577a9446c8 100644
--- a/doc/user/packages/npm_registry/index.md
+++ b/doc/user/packages/npm_registry/index.md
@@ -266,7 +266,7 @@ The GitLab npm repository supports the following commands for the npm CLI (`npm`
### `404 Not Found` errors are happening on `npm install` or `yarn`
-Using `CI_JOB_TOKEN` to install npm packages with dependencies in another project gives you 404 Not Found errors. A fix for this problem is proposed in [issue 352962](https://gitlab.com/gitlab-org/gitlab/-/issues/352962).
+Using `CI_JOB_TOKEN` to install npm packages with dependencies in another project gives you 404 Not Found errors. A fix for this problem is proposed in [issue 352962](https://gitlab.com/gitlab-org/gitlab/-/issues/352962).
As a workaround, you can:
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 8e736b6d83e..aa292ca18a3 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -469,7 +469,7 @@ subscriptions.
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/110810) in GitLab 15.9.
Custom roles allow group members who are assigned the Owner role to create roles
-specific to the needs of their organization.
+specific to the needs of their organization.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For a demo of the custom roles feature, see [[Demo] Ultimate Guest can view code on private repositories via custom role](https://www.youtube.com/watch?v=46cp_-Rtxps).
diff --git a/lib/api/draft_notes.rb b/lib/api/draft_notes.rb
index 842180652c4..7305a1702ab 100644
--- a/lib/api/draft_notes.rb
+++ b/lib/api/draft_notes.rb
@@ -30,6 +30,18 @@ module API
.new(merge_request(params: params), current_user)
.execute(get_draft_note(params: params))
end
+
+ def authorize_create_note!(params:)
+ access_denied! unless can?(current_user, :create_note, merge_request(params: params))
+ end
+
+ def draft_note_params
+ {
+ note: params[:note],
+ commit_id: params[:commit_id] == 'undefined' ? nil : params[:commit_id],
+ resolve_discussion: params[:resolve_discussion] || false
+ }
+ end
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
@@ -71,6 +83,36 @@ module API
end
end
+ desc "Create a new draft note" do
+ success Entities::DraftNote
+ failure [
+ { code: 401, message: 'Unauthorized' },
+ { code: 404, message: 'Not found' }
+ ]
+ end
+ params do
+ requires :id, type: String, desc: "The ID of a project."
+ requires :merge_request_iid, type: Integer, desc: "The ID of a merge request."
+ requires :note, type: String, desc: 'The content of a note.'
+ optional :in_reply_to_discussion_id, type: Integer, desc: 'The ID of a discussion the draft note replies to.'
+ optional :commit_id, type: String, desc: 'The sha of a commit to associate the draft note to.'
+ optional :resolve_discussion, type: Boolean, desc: 'The associated discussion should be resolved.'
+ end
+ post ":id/merge_requests/:merge_request_iid/draft_notes", feature_category: :code_review_workflow do
+ authorize_create_note!(params: params)
+
+ create_params = draft_note_params.merge(in_reply_to_discussion_id: params[:in_reply_to_discussion_id])
+ create_service = ::DraftNotes::CreateService.new(merge_request(params: params), current_user, create_params)
+
+ draft_note = create_service.execute
+
+ if draft_note.persisted?
+ present draft_note, with: Entities::DraftNote
+ else
+ render_validation_error!(draft_note)
+ end
+ end
+
desc "Delete a draft note" do
success Entities::DraftNote
failure [
diff --git a/lib/gitlab/i18n.rb b/lib/gitlab/i18n.rb
index 8fe5868ca57..971737e21af 100644
--- a/lib/gitlab/i18n.rb
+++ b/lib/gitlab/i18n.rb
@@ -118,12 +118,17 @@ module Gitlab
end
def setup(domain:, default_locale:)
+ custom_pluralization
setup_repositories(domain)
setup_default_locale(default_locale)
end
private
+ def custom_pluralization
+ Gitlab::I18n::Pluralization.install_on(FastGettext)
+ end
+
def setup_repositories(domain)
translation_repositories = [
(po_repository(domain, 'jh/locale') if Gitlab.jh?),
diff --git a/lib/gitlab/i18n/pluralization.rb b/lib/gitlab/i18n/pluralization.rb
new file mode 100644
index 00000000000..5d4a05f1fd1
--- /dev/null
+++ b/lib/gitlab/i18n/pluralization.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module I18n
+ # Pluralization formulas per locale used by FastGettext via:
+ # `FastGettext.pluralisation_rule.call(count)`.
+ module Pluralization
+ # rubocop:disable all
+ MAP = {
+ "bg" => ->(n) { (n != 1) },
+ "cs_CZ" => ->(n) { (n==1) ? 0 : (n>=2 && n<=4) ? 1 : 3 },
+ "da_DK" => ->(n) { (n != 1) },
+ "de" => ->(n) { (n != 1) },
+ "en" => ->(n) { (n != 1) },
+ "eo" => ->(n) { (n != 1) },
+ "es" => ->(n) { (n != 1) },
+ "fil_PH" => ->(n) { (n > 1) },
+ "fr" => ->(n) { (n > 1) },
+ "gl_ES" => ->(n) { (n != 1) },
+ "id_ID" => ->(n) { 0 },
+ "it" => ->(n) { (n != 1) },
+ "ja" => ->(n) { 0 },
+ "ko" => ->(n) { 0 },
+ "nb_NO" => ->(n) { (n != 1) },
+ "nl_NL" => ->(n) { (n != 1) },
+ "pl_PL" => ->(n) { (n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3) },
+ "pt_BR" => ->(n) { (n != 1) },
+ "ro_RO" => ->(n) { (n==1 ? 0 : (n==0 || (n%100>0 && n%100<20)) ? 1 : 2) },
+ "ru" => ->(n) { ((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3)) },
+ "si_LK" => ->(n) { (n != 1) },
+ "tr_TR" => ->(n) { (n != 1) },
+ "uk" => ->(n) { ((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3)) },
+ "zh_CN" => ->(n) { 0 },
+ "zh_HK" => ->(n) { 0 },
+ "zh_TW" => ->(n) { 0 }
+ }.freeze
+ # rubocop:enable
+
+ NOT_FOUND_ERROR = lambda do |locale|
+ po = File.expand_path("../../../locale/#{locale}/gitlab.po", __dir__)
+
+ forms = File.read(po)[/Plural-Forms:.*; plural=(.*?);\\n/, 1] if File.exist?(po)
+ suggestion = <<~TEXT if forms
+ Add the following line to #{__FILE__}:
+
+ MAP = {
+ ...
+ "#{locale}" => ->(n) { #{forms} },
+ ...
+ }.freeze
+
+ This rule was extracted from #{po}.
+ TEXT
+
+ raise ArgumentError, <<~MESSAGE
+ Missing pluralization rule for locale #{locale.inspect}.
+
+ #{suggestion}
+ MESSAGE
+ end
+
+ def self.call(count)
+ locale = FastGettext.locale
+
+ MAP.fetch(locale, &NOT_FOUND_ERROR).call(count)
+ end
+
+ def self.install_on(klass)
+ klass.extend(FastGettextClassMethods)
+ end
+
+ module FastGettextClassMethods
+ # FastGettext allows to set the rule via
+ # `FastGettext.pluralisation_rule=` which is on thread-level.
+ #
+ # Because we are patching FastGettext at boot time per thread values
+ # won't work so we have to override the method implementation.
+ #
+ # `FastGettext.pluralisation_rule=` has now no effect.
+ def pluralisation_rule
+ Gitlab::I18n::Pluralization
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/instrumentation/redis_base.rb b/lib/gitlab/instrumentation/redis_base.rb
index de24132a28e..3ee8768d509 100644
--- a/lib/gitlab/instrumentation/redis_base.rb
+++ b/lib/gitlab/instrumentation/redis_base.rb
@@ -129,6 +129,10 @@ module Gitlab
@request_latency_histogram.observe({ storage: storage_key }, duration)
end
+ def log_exception(ex)
+ ::Gitlab::ErrorTracking.log_exception(ex, storage: storage_key)
+ end
+
private
def request_count_key
diff --git a/lib/gitlab/instrumentation/redis_interceptor.rb b/lib/gitlab/instrumentation/redis_interceptor.rb
index 35dd7cbfeb8..82531883810 100644
--- a/lib/gitlab/instrumentation/redis_interceptor.rb
+++ b/lib/gitlab/instrumentation/redis_interceptor.rb
@@ -41,6 +41,7 @@ module Gitlab
yield
rescue ::Redis::BaseError => ex
instrumentation_class.instance_count_exception(ex)
+ instrumentation_class.log_exception(ex)
raise ex
ensure
duration = Gitlab::Metrics::System.monotonic_time - start
diff --git a/lib/gitlab/redis/cache.rb b/lib/gitlab/redis/cache.rb
index 647573e59fe..ba3af3e7a6f 100644
--- a/lib/gitlab/redis/cache.rb
+++ b/lib/gitlab/redis/cache.rb
@@ -2,16 +2,6 @@
module Gitlab
module Redis
- # Match signature in
- # https://github.com/rails/rails/blob/v6.1.7.2/activesupport/lib/active_support/cache/redis_cache_store.rb#L59
- ERROR_HANDLER = ->(method:, returning:, exception:) do
- Gitlab::ErrorTracking.log_exception(
- exception,
- method: method,
- returning: returning.inspect
- )
- end
-
class Cache < ::Gitlab::Redis::Wrapper
CACHE_NAMESPACE = 'cache:gitlab'
@@ -22,8 +12,7 @@ module Gitlab
redis: pool,
compress: Gitlab::Utils.to_boolean(ENV.fetch('ENABLE_REDIS_CACHE_COMPRESSION', '1')),
namespace: CACHE_NAMESPACE,
- expires_in: default_ttl_seconds,
- error_handler: ::Gitlab::Redis::ERROR_HANDLER
+ expires_in: default_ttl_seconds
}
end
diff --git a/lib/gitlab/redis/rate_limiting.rb b/lib/gitlab/redis/rate_limiting.rb
index 12710bafbea..3a9fb63a495 100644
--- a/lib/gitlab/redis/rate_limiting.rb
+++ b/lib/gitlab/redis/rate_limiting.rb
@@ -10,11 +10,7 @@ module Gitlab
end
def cache_store
- @cache_store ||= ActiveSupport::Cache::RedisCacheStore.new(
- redis: pool,
- namespace: Cache::CACHE_NAMESPACE,
- error_handler: ::Gitlab::Redis::ERROR_HANDLER
- )
+ @cache_store ||= ActiveSupport::Cache::RedisCacheStore.new(redis: pool, namespace: Cache::CACHE_NAMESPACE)
end
private
diff --git a/lib/gitlab/redis/repository_cache.rb b/lib/gitlab/redis/repository_cache.rb
index 6c7bc8c41d5..d8d434ae062 100644
--- a/lib/gitlab/redis/repository_cache.rb
+++ b/lib/gitlab/redis/repository_cache.rb
@@ -14,8 +14,7 @@ module Gitlab
redis: pool,
compress: Gitlab::Utils.to_boolean(ENV.fetch('ENABLE_REDIS_CACHE_COMPRESSION', '1')),
namespace: Cache::CACHE_NAMESPACE,
- expires_in: Cache.default_ttl_seconds,
- error_handler: ::Gitlab::Redis::ERROR_HANDLER
+ expires_in: Cache.default_ttl_seconds
)
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 1b34e6e24d5..24f81b76e96 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -6958,15 +6958,27 @@ msgstr[1] ""
msgid "Boards|Collapse"
msgstr ""
+msgid "Boards|Create new epic"
+msgstr ""
+
+msgid "Boards|Create new issue"
+msgstr ""
+
msgid "Boards|Edit board"
msgstr ""
+msgid "Boards|Edit list settings"
+msgstr ""
+
msgid "Boards|Expand"
msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|List actions"
+msgstr ""
+
msgid "Boards|Move card"
msgstr ""
@@ -6979,9 +6991,6 @@ msgstr ""
msgid "Boards|New board"
msgstr ""
-msgid "Boards|New epic"
-msgstr ""
-
msgid "Boards|Retrieving blocking %{issuableType}s"
msgstr ""
@@ -32458,18 +32467,33 @@ msgstr ""
msgid "ProductAnalytics|All pages"
msgstr ""
+msgid "ProductAnalytics|All sessions compared"
+msgstr ""
+
msgid "ProductAnalytics|An error occured while loading the %{panelTitle} panel."
msgstr ""
msgid "ProductAnalytics|An error occurred while fetching data. Refresh the page to try again."
msgstr ""
+msgid "ProductAnalytics|Analytics dashboards"
+msgstr ""
+
msgid "ProductAnalytics|Any Click on elements"
msgstr ""
msgid "ProductAnalytics|Audience"
msgstr ""
+msgid "ProductAnalytics|Average Session Duration"
+msgstr ""
+
+msgid "ProductAnalytics|Average duration in minutes"
+msgstr ""
+
+msgid "ProductAnalytics|Average per User"
+msgstr ""
+
msgid "ProductAnalytics|Browser"
msgstr ""
@@ -32494,6 +32518,9 @@ msgstr ""
msgid "ProductAnalytics|Compares all events against each other"
msgstr ""
+msgid "ProductAnalytics|Compares all user sessions against each other"
+msgstr ""
+
msgid "ProductAnalytics|Compares click events against each other"
msgstr ""
@@ -32542,6 +32569,12 @@ msgstr ""
msgid "ProductAnalytics|Host"
msgstr ""
+msgid "ProductAnalytics|How many sessions a user has"
+msgstr ""
+
+msgid "ProductAnalytics|How often sesions are repeated"
+msgstr ""
+
msgid "ProductAnalytics|Language"
msgstr ""
@@ -32554,6 +32587,12 @@ msgstr ""
msgid "ProductAnalytics|Measure all or specific Page Views"
msgstr ""
+msgid "ProductAnalytics|Measure all sessions"
+msgstr ""
+
+msgid "ProductAnalytics|Measure by unique users"
+msgstr ""
+
msgid "ProductAnalytics|Measuring"
msgstr ""
@@ -32587,15 +32626,18 @@ msgstr ""
msgid "ProductAnalytics|Panel"
msgstr ""
-msgid "ProductAnalytics|Product analytics dashboards"
+msgid "ProductAnalytics|Referer"
msgstr ""
-msgid "ProductAnalytics|Referer"
+msgid "ProductAnalytics|Repeat Visit percentage"
msgstr ""
msgid "ProductAnalytics|Resulting Data"
msgstr ""
+msgid "ProductAnalytics|Sessions"
+msgstr ""
+
msgid "ProductAnalytics|Single Statistic"
msgstr ""
@@ -32608,6 +32650,12 @@ msgstr ""
msgid "ProductAnalytics|URL"
msgstr ""
+msgid "ProductAnalytics|Unique Users"
+msgstr ""
+
+msgid "ProductAnalytics|User Sessions"
+msgstr ""
+
msgid "ProductAnalytics|User activity"
msgstr ""
diff --git a/spec/controllers/repositories/git_http_controller_spec.rb b/spec/controllers/repositories/git_http_controller_spec.rb
index da62acb1fda..93857435c9d 100644
--- a/spec/controllers/repositories/git_http_controller_spec.rb
+++ b/spec/controllers/repositories/git_http_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Repositories::GitHttpController do
+RSpec.describe Repositories::GitHttpController, feature_category: :source_code_management do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:personal_snippet) { create(:personal_snippet, :public, :repository) }
let_it_be(:project_snippet) { create(:project_snippet, :public, :repository, project: project) }
@@ -14,7 +14,7 @@ RSpec.describe Repositories::GitHttpController do
request.headers.merge! auth_env(user.username, user.password, nil)
end
- context 'when Gitaly is unavailable' do
+ context 'when Gitaly is unavailable', :use_clean_rails_redis_caching do
it 'responds with a 503 message' do
expect(Gitlab::GitalyClient).to receive(:call).and_raise(GRPC::Unavailable)
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index 87896b60f87..1ea6e079104 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -591,7 +591,9 @@ RSpec.describe 'Project issue boards', :js, feature_category: :team_planning do
def remove_list
page.within(find('.board:nth-child(2)')) do
- find('button[title="List settings"]').click
+ dropdown = first("[data-testid='header-list-actions']")
+ dropdown.click
+ click_button('Edit list settings')
end
page.within(find('.js-board-settings-sidebar')) do
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index d597c57ac1c..6753f0ea009 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -32,18 +32,23 @@ RSpec.describe 'Issue Boards new issue', :js, feature_category: :team_planning d
end
it 'displays new issue button' do
- expect(first('.board')).to have_button('New issue', count: 1)
+ dropdown = first("[data-testid='header-list-actions']")
+ dropdown.click
+ expect(first('.board')).to have_button('Create new issue', count: 1)
end
it 'does not display new issue button in closed list' do
page.within('.board:nth-child(3)') do
- expect(page).not_to have_button('New issue')
+ expect(page).not_to have_selector("[data-testid='header-list-actions']")
+ expect(page).not_to have_button('Create new issue')
end
end
it 'shows form when clicking button' do
page.within(first('.board')) do
- click_button 'New issue'
+ dropdown = first("[data-testid='header-list-actions']")
+ dropdown.click
+ click_button 'Create new issue'
expect(page).to have_selector('.board-new-issue-form')
end
@@ -51,7 +56,9 @@ RSpec.describe 'Issue Boards new issue', :js, feature_category: :team_planning d
it 'hides form when clicking cancel' do
page.within(first('.board')) do
- click_button 'New issue'
+ dropdown = first("[data-testid='header-list-actions']")
+ dropdown.click
+ click_button 'Create new issue'
expect(page).to have_selector('.board-new-issue-form')
@@ -63,7 +70,9 @@ RSpec.describe 'Issue Boards new issue', :js, feature_category: :team_planning d
it 'creates new issue, places it on top of the list, and opens sidebar' do
page.within(first('.board')) do
- click_button 'New issue'
+ dropdown = first("[data-testid='header-list-actions']")
+ dropdown.click
+ click_button 'Create new issue'
end
page.within(first('.board-new-issue-form')) do
@@ -91,7 +100,9 @@ RSpec.describe 'Issue Boards new issue', :js, feature_category: :team_planning d
it 'successfuly loads labels to be added to newly created issue' do
page.within(first('.board')) do
- click_button 'New issue'
+ dropdown = first("[data-testid='header-list-actions']")
+ dropdown.click
+ click_button 'Create new issue'
end
page.within(first('.board-new-issue-form')) do
@@ -121,7 +132,9 @@ RSpec.describe 'Issue Boards new issue', :js, feature_category: :team_planning d
wait_for_all_requests
page.within('.board:nth-child(2)') do
- click_button('New issue')
+ dropdown = first("[data-testid='header-list-actions']")
+ dropdown.click
+ click_button('Create new issue')
page.within(first('.board-new-issue-form')) do
find('.form-control').set('new issue')
@@ -144,12 +157,14 @@ RSpec.describe 'Issue Boards new issue', :js, feature_category: :team_planning d
end
it 'does not display new issue button in open list' do
- expect(first('.board')).not_to have_button('New issue')
+ expect(page).not_to have_selector("[data-testid='header-list-actions']")
+ expect(first('.board')).not_to have_button('Create new issue')
end
it 'does not display new issue button in label list' do
page.within('.board:nth-child(2)') do
- expect(page).not_to have_button('New issue')
+ expect(page).not_to have_selector("[data-testid='header-list-actions']")
+ expect(page).not_to have_button('Create new issue')
end
end
end
@@ -173,7 +188,8 @@ RSpec.describe 'Issue Boards new issue', :js, feature_category: :team_planning d
context 'when backlog does not exist' do
it 'does not display new issue button in label list' do
page.within('.board.is-draggable') do
- expect(page).not_to have_button('New issue')
+ expect(page).not_to have_selector("[data-testid='header-list-actions']")
+ expect(page).not_to have_button('Create new issue')
end
end
end
@@ -182,12 +198,14 @@ RSpec.describe 'Issue Boards new issue', :js, feature_category: :team_planning d
let_it_be(:backlog_list) { create(:backlog_list, board: group_board) }
it 'does not display new issue button in open list' do
- expect(first('.board')).not_to have_button('New issue')
+ expect(page).not_to have_selector("[data-testid='header-list-actions']")
+ expect(first('.board')).not_to have_button('Create new issue')
end
it 'does not display new issue button in label list' do
page.within('.board.is-draggable') do
- expect(page).not_to have_button('New issue')
+ expect(page).not_to have_selector("[data-testid='header-list-actions']")
+ expect(page).not_to have_button('Create new issue')
end
end
end
@@ -205,7 +223,9 @@ RSpec.describe 'Issue Boards new issue', :js, feature_category: :team_planning d
context 'when backlog does not exist' do
it 'display new issue button in label list' do
- expect(board_list_header).to have_button('New issue')
+ dropdown = first("[data-testid='header-list-actions']")
+ dropdown.click
+ expect(board_list_header).to have_button('Create new issue')
end
end
@@ -214,7 +234,9 @@ RSpec.describe 'Issue Boards new issue', :js, feature_category: :team_planning d
before do
page.within(board_list_header) do
- click_button 'New issue'
+ dropdown = first("[data-testid='header-list-actions']")
+ dropdown.click
+ click_button 'Create new issue'
end
project_select_dropdown.click
diff --git a/spec/features/groups/board_spec.rb b/spec/features/groups/board_spec.rb
index c451a97bed5..8acf3ffe441 100644
--- a/spec/features/groups/board_spec.rb
+++ b/spec/features/groups/board_spec.rb
@@ -25,8 +25,10 @@ RSpec.describe 'Group Boards', feature_category: :team_planning do
it 'adds an issue to the backlog' do
page.within(find('.board', match: :first)) do
- issue_title = 'New Issue'
- click_button 'New issue'
+ dropdown = first("[data-testid='header-list-actions']")
+ dropdown.click
+ issue_title = 'Create new issue'
+ click_button issue_title
wait_for_requests
diff --git a/spec/frontend/boards/components/board_list_header_spec.js b/spec/frontend/boards/components/board_list_header_spec.js
index 9e65e900440..f8865bb6feb 100644
--- a/spec/frontend/boards/components/board_list_header_spec.js
+++ b/spec/frontend/boards/components/board_list_header_spec.js
@@ -1,10 +1,9 @@
-import { shallowMount } from '@vue/test-utils';
+import { GlDisclosureDropdown, GlDisclosureDropdownItem } from '@gitlab/ui';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import Vuex from 'vuex';
import createMockApollo from 'helpers/mock_apollo_helper';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { boardListQueryResponse, mockLabelList } from 'jest/boards/mock_data';
import BoardListHeader from '~/boards/components/board_list_header.vue';
import { ListType } from '~/boards/constants';
@@ -64,31 +63,33 @@ describe('Board List Header Component', () => {
fakeApollo = createMockApollo([[listQuery, listQueryHandler]]);
- wrapper = extendedWrapper(
- shallowMount(BoardListHeader, {
- apolloProvider: fakeApollo,
- store,
- propsData: {
- list: listMock,
- },
- provide: {
- boardId,
- weightFeatureAvailable: false,
- currentUserId,
- isEpicBoard: false,
- disabled: false,
- ...injectedProps,
- },
- }),
- );
+ wrapper = shallowMountExtended(BoardListHeader, {
+ apolloProvider: fakeApollo,
+ store,
+ propsData: {
+ list: listMock,
+ },
+ provide: {
+ boardId,
+ weightFeatureAvailable: false,
+ currentUserId,
+ isEpicBoard: false,
+ disabled: false,
+ ...injectedProps,
+ },
+ stubs: {
+ GlDisclosureDropdown,
+ GlDisclosureDropdownItem,
+ },
+ });
};
+ const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
const isCollapsed = () => wrapper.vm.list.collapsed;
-
- const findAddIssueButton = () => wrapper.findComponent({ ref: 'newIssueBtn' });
const findTitle = () => wrapper.find('.board-title');
const findCaret = () => wrapper.findByTestId('board-title-caret');
- const findSettingsButton = () => wrapper.findComponent({ ref: 'settingsBtn' });
+ const findNewIssueButton = () => wrapper.findByTestId('newIssueBtn');
+ const findSettingsButton = () => wrapper.findByTestId('settingsBtn');
describe('Add issue button', () => {
const hasNoAddButton = [ListType.closed];
@@ -100,59 +101,49 @@ describe('Board List Header Component', () => {
ListType.assignee,
];
- it.each(hasNoAddButton)('does not render when List Type is `%s`', (listType) => {
+ it.each(hasNoAddButton)('does not render dropdown when List Type is `%s`', (listType) => {
createComponent({ listType });
- expect(findAddIssueButton().exists()).toBe(false);
+ expect(findDropdown().exists()).toBe(false);
});
it.each(hasAddButton)('does render when List Type is `%s`', (listType) => {
createComponent({ listType });
- expect(findAddIssueButton().exists()).toBe(true);
+ expect(findDropdown().exists()).toBe(true);
+ expect(findNewIssueButton().exists()).toBe(true);
});
- it('has a test for each list type', () => {
- createComponent();
-
- Object.values(ListType).forEach((value) => {
- expect([...hasAddButton, ...hasNoAddButton]).toContain(value);
- });
- });
-
- it('does not render when logged out', () => {
+ it('does not render dropdown when logged out', () => {
createComponent({
currentUserId: null,
});
- expect(findAddIssueButton().exists()).toBe(false);
+ expect(findDropdown().exists()).toBe(false);
});
});
describe('Settings Button', () => {
- describe('with disabled=true', () => {
- const hasSettings = [
- ListType.assignee,
- ListType.milestone,
- ListType.iteration,
- ListType.label,
- ];
- const hasNoSettings = [ListType.backlog, ListType.closed];
-
- it.each(hasSettings)('does render for List Type `%s` when disabled=true', (listType) => {
- createComponent({ listType, injectedProps: { disabled: true } });
-
- expect(findSettingsButton().exists()).toBe(true);
- });
+ const hasSettings = [ListType.assignee, ListType.milestone, ListType.iteration, ListType.label];
- it.each(hasNoSettings)(
- 'does not render for List Type `%s` when disabled=true',
- (listType) => {
- createComponent({ listType });
+ it.each(hasSettings)('does render for List Type `%s`', (listType) => {
+ createComponent({ listType });
- expect(findSettingsButton().exists()).toBe(false);
- },
- );
+ expect(findDropdown().exists()).toBe(true);
+ expect(findSettingsButton().exists()).toBe(true);
+ });
+
+ it('does not render dropdown when ListType `closed`', () => {
+ createComponent({ listType: ListType.closed });
+
+ expect(findDropdown().exists()).toBe(false);
+ });
+
+ it('renders dropdown but not the Settings button when ListType `backlog`', () => {
+ createComponent({ listType: ListType.backlog });
+
+ expect(findDropdown().exists()).toBe(true);
+ expect(findSettingsButton().exists()).toBe(false);
});
});
diff --git a/spec/lib/gitlab/i18n/pluralization_spec.rb b/spec/lib/gitlab/i18n/pluralization_spec.rb
new file mode 100644
index 00000000000..857562d549c
--- /dev/null
+++ b/spec/lib/gitlab/i18n/pluralization_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'rspec-parameterized'
+require 'gettext_i18n_rails'
+
+RSpec.describe Gitlab::I18n::Pluralization, feature_category: :internationalization do
+ describe '.call' do
+ subject(:rule) { described_class.call(1) }
+
+ context 'with available locales' do
+ around do |example|
+ Gitlab::I18n.with_locale(locale, &example)
+ end
+
+ where(:locale) do
+ Gitlab::I18n.available_locales
+ end
+
+ with_them do
+ it 'supports pluralization' do
+ expect(rule).not_to be_nil
+ end
+ end
+
+ context 'with missing rules' do
+ let(:locale) { "pl_PL" }
+
+ before do
+ stub_const("#{described_class}::MAP", described_class::MAP.except(locale))
+ end
+
+ it 'raises an ArgumentError' do
+ expect { rule }.to raise_error(ArgumentError,
+ /Missing pluralization rule for locale "#{locale}"/
+ )
+ end
+ end
+ end
+ end
+
+ describe '.install_on' do
+ let(:mod) { Module.new }
+
+ before do
+ described_class.install_on(mod)
+ end
+
+ it 'adds pluralisation_rule method' do
+ expect(mod.pluralisation_rule).to eq(described_class)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/i18n_spec.rb b/spec/lib/gitlab/i18n_spec.rb
index b752d89bf0d..ee92831922d 100644
--- a/spec/lib/gitlab/i18n_spec.rb
+++ b/spec/lib/gitlab/i18n_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::I18n do
+RSpec.describe Gitlab::I18n, feature_category: :internationalization do
let(:user) { create(:user, preferred_language: :es) }
describe '.selectable_locales' do
@@ -47,4 +47,19 @@ RSpec.describe Gitlab::I18n do
expect(::I18n.locale).to eq(:en)
end
end
+
+ describe '.pluralisation_rule' do
+ context 'when overridden' do
+ before do
+ # Internally, FastGettext sets
+ # Thread.current[:fast_gettext_pluralisation_rule].
+ # Our patch patches `FastGettext.pluralisation_rule` instead.
+ FastGettext.pluralisation_rule = :something
+ end
+
+ it 'returns custom definition regardless' do
+ expect(FastGettext.pluralisation_rule).to eq(Gitlab::I18n::Pluralization)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/instrumentation/redis_base_spec.rb b/spec/lib/gitlab/instrumentation/redis_base_spec.rb
index 656e6ffba05..426997f6e86 100644
--- a/spec/lib/gitlab/instrumentation/redis_base_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_base_spec.rb
@@ -210,4 +210,16 @@ RSpec.describe Gitlab::Instrumentation::RedisBase, :request_store do
end
end
end
+
+ describe '.log_exception' do
+ it 'logs exception with storage details' do
+ expect(::Gitlab::ErrorTracking).to receive(:log_exception)
+ .with(
+ an_instance_of(StandardError),
+ storage: instrumentation_class_a.storage_key
+ )
+
+ instrumentation_class_a.log_exception(StandardError.new)
+ end
+ end
end
diff --git a/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb b/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
index 187a6ff1739..63fdad0cab2 100644
--- a/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
@@ -67,6 +67,8 @@ RSpec.describe Gitlab::Instrumentation::RedisInterceptor, :clean_gitlab_redis_sh
it 'counts exceptions' do
expect(instrumentation_class).to receive(:instance_count_exception)
.with(instance_of(Redis::CommandError)).and_call_original
+ expect(instrumentation_class).to receive(:log_exception)
+ .with(instance_of(Redis::CommandError)).and_call_original
expect(instrumentation_class).to receive(:instance_count_request).and_call_original
expect do
diff --git a/spec/lib/gitlab/redis/cache_spec.rb b/spec/lib/gitlab/redis/cache_spec.rb
index 64615c4d9ad..82ff8a26199 100644
--- a/spec/lib/gitlab/redis/cache_spec.rb
+++ b/spec/lib/gitlab/redis/cache_spec.rb
@@ -26,22 +26,5 @@ RSpec.describe Gitlab::Redis::Cache do
expect(described_class.active_support_config[:expires_in]).to eq(1.day)
end
-
- context 'when encountering an error' do
- let(:cache) { ActiveSupport::Cache::RedisCacheStore.new(**described_class.active_support_config) }
-
- subject { cache.read('x') }
-
- before do
- described_class.with do |redis|
- allow(redis).to receive(:get).and_raise(::Redis::CommandError)
- end
- end
-
- it 'logs error' do
- expect(::Gitlab::ErrorTracking).to receive(:log_exception)
- subject
- end
- end
end
end
diff --git a/spec/lib/gitlab/redis/rate_limiting_spec.rb b/spec/lib/gitlab/redis/rate_limiting_spec.rb
index d82228426f0..e79c070df93 100644
--- a/spec/lib/gitlab/redis/rate_limiting_spec.rb
+++ b/spec/lib/gitlab/redis/rate_limiting_spec.rb
@@ -4,21 +4,4 @@ require 'spec_helper'
RSpec.describe Gitlab::Redis::RateLimiting do
include_examples "redis_new_instance_shared_examples", 'rate_limiting', Gitlab::Redis::Cache
-
- describe '.cache_store' do
- context 'when encountering an error' do
- subject { described_class.cache_store.read('x') }
-
- before do
- described_class.with do |redis|
- allow(redis).to receive(:get).and_raise(::Redis::CommandError)
- end
- end
-
- it 'logs error' do
- expect(::Gitlab::ErrorTracking).to receive(:log_exception)
- subject
- end
- end
- end
end
diff --git a/spec/lib/gitlab/redis/repository_cache_spec.rb b/spec/lib/gitlab/redis/repository_cache_spec.rb
index 2c167a6eb62..8cdc4580f9e 100644
--- a/spec/lib/gitlab/redis/repository_cache_spec.rb
+++ b/spec/lib/gitlab/redis/repository_cache_spec.rb
@@ -17,20 +17,5 @@ RSpec.describe Gitlab::Redis::RepositoryCache, feature_category: :scalability do
it 'has a default ttl of 8 hours' do
expect(described_class.cache_store.options[:expires_in]).to eq(8.hours)
end
-
- context 'when encountering an error' do
- subject { described_class.cache_store.read('x') }
-
- before do
- described_class.with do |redis|
- allow(redis).to receive(:get).and_raise(::Redis::CommandError)
- end
- end
-
- it 'logs error' do
- expect(::Gitlab::ErrorTracking).to receive(:log_exception)
- subject
- end
- end
end
end
diff --git a/spec/requests/api/draft_notes_spec.rb b/spec/requests/api/draft_notes_spec.rb
index e8f519e004d..56b0c834010 100644
--- a/spec/requests/api/draft_notes_spec.rb
+++ b/spec/requests/api/draft_notes_spec.rb
@@ -8,6 +8,11 @@ RSpec.describe API::DraftNotes, feature_category: :code_review_workflow do
let_it_be(:project) { create(:project, :public) }
let_it_be(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) }
+ let_it_be(:private_project) { create(:project, :private) }
+ let_it_be(:private_merge_request) do
+ create(:merge_request, source_project: private_project, target_project: private_project)
+ end
+
let_it_be(:merge_request_note) { create(:note, noteable: merge_request, project: project, author: user) }
let!(:draft_note_by_current_user) { create(:draft_note, merge_request: merge_request, author: user) }
let!(:draft_note_by_random_user) { create(:draft_note, merge_request: merge_request) }
@@ -120,6 +125,97 @@ RSpec.describe API::DraftNotes, feature_category: :code_review_workflow do
end
end
+ def create_draft_note(params = {}, url = api_stub)
+ post api("#{url}/draft_notes", user), params: params
+ end
+
+ describe "Create a new draft note" do
+ let(:basic_create_params) do
+ {
+ note: "Example body string"
+ }
+ end
+
+ context "when creating a new draft note" do
+ context "with required params" do
+ it "returns 201 Created status" do
+ create_draft_note(basic_create_params)
+
+ expect(response).to have_gitlab_http_status(:created)
+ end
+
+ it "creates a new draft note with the submitted params" do
+ expect { create_draft_note(basic_create_params) }.to change { DraftNote.count }.by(1)
+
+ expect(json_response["note"]).to eq(basic_create_params[:note])
+ expect(json_response["merge_request_id"]).to eq(merge_request.id)
+ expect(json_response["author_id"]).to eq(user.id)
+ end
+ end
+
+ context "without required params" do
+ it "returns 400 Bad Request status" do
+ create_draft_note({})
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+
+ context "when providing a non-existing commit_id" do
+ it "returns a 400 Bad Request" do
+ create_draft_note(
+ basic_create_params.merge(
+ commit_id: 'bad SHA'
+ )
+ )
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+
+ context "when targeting a merge request the user doesn't have access to" do
+ it "returns a 404 Not Found" do
+ create_draft_note(
+ basic_create_params,
+ "/projects/#{private_project.id}/merge_requests/#{private_merge_request.iid}"
+ )
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context "when attempting to resolve a disscussion" do
+ context "when providing a non-existant ID" do
+ it "returns a 400 Bad Request" do
+ create_draft_note(
+ basic_create_params.merge(
+ resolve_discussion: true,
+ in_reply_to_discussion_id: non_existing_record_id
+ )
+ )
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+
+ context "when not providing an ID" do
+ it "returns a 400 Bad Request" do
+ create_draft_note(basic_create_params.merge(resolve_discussion: true))
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+
+ it "returns a validation error message" do
+ create_draft_note(basic_create_params.merge(resolve_discussion: true))
+
+ expect(response.body)
+ .to eq("{\"message\":{\"base\":[\"User is not allowed to resolve thread\"]}}")
+ end
+ end
+ end
+ end
+ end
+
describe "Publishing a draft note" do
let(:publish_draft_note) do
put api(
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 02b99eba8ce..eaac2a77353 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -472,10 +472,11 @@ RSpec.describe 'Git HTTP requests', feature_category: :source_code_management do
end
context 'when the request is not from gitlab-workhorse' do
- it 'raises an exception' do
- expect do
- get("/#{project.full_path}.git/info/refs?service=git-upload-pack")
- end.to raise_error(JWT::DecodeError)
+ it 'responds with 403 Forbidden' do
+ get("/#{project.full_path}.git/info/refs?service=git-upload-pack")
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ expect(response.body).to eq('Nil JSON web token')
end
end
@@ -1112,10 +1113,11 @@ RSpec.describe 'Git HTTP requests', feature_category: :source_code_management do
end
context 'when the request is not from gitlab-workhorse' do
- it 'raises an exception' do
- expect do
- get("/#{project.full_path}.git/info/refs?service=git-upload-pack")
- end.to raise_error(JWT::DecodeError)
+ it 'responds with 403 Forbidden' do
+ get("/#{project.full_path}.git/info/refs?service=git-upload-pack")
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ expect(response.body).to eq('Nil JSON web token')
end
end
diff --git a/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb b/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb
index cc28a79b4ca..39c300d547d 100644
--- a/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb
@@ -87,14 +87,20 @@ RSpec.shared_examples Repositories::GitHttpController do
end
describe 'POST #git_upload_pack' do
- before do
+ it 'returns 200' do
allow(controller).to receive(:verify_workhorse_api!).and_return(true)
- end
- it 'returns 200' do
post :git_upload_pack, params: params
expect(response).to have_gitlab_http_status(:ok)
end
+
+ context 'when JWT token is not provided' do
+ it 'returns 403' do
+ post :git_upload_pack, params: params
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
end
end