-
-
-
+
diff --git a/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue b/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
index 699d1bebea1..8fe22db6f43 100644
--- a/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
+++ b/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
@@ -5,6 +5,7 @@ import { __, sprintf } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import createFlash from '~/flash';
import eventHub from '~/sidebar/event_hub';
+import toast from '~/vue_shared/plugins/global_toast';
import editForm from './edit_form.vue';
export default {
@@ -84,6 +85,11 @@ export default {
locked: !this.isLocked,
fullPath: this.fullPath,
})
+ .then(() => {
+ if (this.isMergeRequest) {
+ toast(this.isLocked ? __('Merge request locked.') : __('Merge request unlocked.'));
+ }
+ })
.catch(() => {
const flashMessage = __(
'Something went wrong trying to change the locked state of this %{issuableDisplayName}',
diff --git a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
index 1bafa845665..7662d645dd9 100644
--- a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
+++ b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
@@ -6,6 +6,7 @@ import { isLoggedIn } from '~/lib/utils/common_utils';
import { __, sprintf } from '~/locale';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import toast from '~/vue_shared/plugins/global_toast';
import { subscribedQueries, Tracking } from '~/sidebar/constants';
const ICON_ON = 'notifications';
@@ -140,6 +141,10 @@ export default {
message: errors[0],
});
}
+
+ if (this.isMergeRequest) {
+ toast(subscribed ? __('Notifications turned on.') : __('Notifications turned off.'));
+ }
},
)
.catch(() => {
diff --git a/app/assets/stylesheets/components/content_editor.scss b/app/assets/stylesheets/components/content_editor.scss
index 870ed50c6eb..1b6a0208ca7 100644
--- a/app/assets/stylesheets/components/content_editor.scss
+++ b/app/assets/stylesheets/components/content_editor.scss
@@ -106,11 +106,14 @@
}
}
-.table-dropdown .dropdown-menu {
- @include gl-min-w-0;
- @include gl-w-auto;
+.content-editor-dropdown .dropdown-menu {
+ width: auto !important;
- @include gl-white-space-nowrap;
+ @include gl-min-w-0;
+
+ button {
+ @include gl-white-space-nowrap;
+ }
}
diff --git a/app/finders/contributed_projects_finder.rb b/app/finders/contributed_projects_finder.rb
index a351d30229e..eccc7d3f2bb 100644
--- a/app/finders/contributed_projects_finder.rb
+++ b/app/finders/contributed_projects_finder.rb
@@ -11,12 +11,15 @@ class ContributedProjectsFinder < UnionFinder
# current_user - When given the list of the projects is limited to those only
# visible by this user.
#
+ # ignore_visibility - When true the list of projects will include all contributed
+ # projects, regardless of their visibility to the current_user
+ #
# Returns an ActiveRecord::Relation.
- def execute(current_user = nil)
+ def execute(current_user = nil, ignore_visibility: false)
# Do not show contributed projects if the user profile is private.
return Project.none unless can_read_profile?(current_user)
- segments = all_projects(current_user)
+ segments = all_projects(current_user, ignore_visibility)
find_union(segments, Project).with_namespace.order_id_desc
end
@@ -27,7 +30,9 @@ class ContributedProjectsFinder < UnionFinder
Ability.allowed?(current_user, :read_user_profile, @user)
end
- def all_projects(current_user)
+ def all_projects(current_user, ignore_visibility)
+ return [@user.contributed_projects] if ignore_visibility
+
projects = []
projects << @user.contributed_projects.visible_to_user(current_user) if current_user
diff --git a/app/views/admin/application_settings/repository.html.haml b/app/views/admin/application_settings/repository.html.haml
index 785261b4c7b..12063ea700b 100644
--- a/app/views/admin/application_settings/repository.html.haml
+++ b/app/views/admin/application_settings/repository.html.haml
@@ -4,7 +4,7 @@
%section.settings.as-default-branch-name.no-animate#js-default-branch-name{ class: ('expanded' if expanded_by_default?) }
.settings-header
- %h4
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= _('Default branch')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded_by_default? ? _('Collapse') : _('Expand')
@@ -15,7 +15,7 @@
%section.settings.as-mirror.no-animate#js-mirror-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
- %h4
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= _('Repository mirroring')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded_by_default? ? 'Collapse' : 'Expand'
@@ -27,7 +27,7 @@
%section.settings.as-repository-storage.no-animate#js-repository-storage-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'repository_storage_settings_content' } }
.settings-header
- %h4
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= _('Repository storage')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded_by_default? ? _('Collapse') : _('Expand')
@@ -39,7 +39,7 @@
%section.settings.as-repository-check.no-animate#js-repository-check-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
- %h4
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= _('Repository maintenance')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded_by_default? ? _('Collapse') : _('Expand')
@@ -54,7 +54,7 @@
%section.settings.as-repository-static-objects.no-animate#js-repository-static-objects-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
- %h4
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= _('External storage for repository static objects')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded_by_default? ? _('Collapse') : _('Expand')
diff --git a/app/views/projects/merge_requests/_mr_title.html.haml b/app/views/projects/merge_requests/_mr_title.html.haml
index 4f4acb6103f..893f03157db 100644
--- a/app/views/projects/merge_requests/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/_mr_title.html.haml
@@ -3,7 +3,7 @@
- can_reopen_merge_request = can?(current_user, :reopen_merge_request, @merge_request)
- are_close_and_open_buttons_hidden = merge_request_button_hidden?(@merge_request, true) && merge_request_button_hidden?(@merge_request, false)
- hide_gutter_toggle = local_assigns.fetch(:hide_gutter_toggle, false)
-- cache_key = [@project, @merge_request, can_update_merge_request, can_reopen_merge_request, are_close_and_open_buttons_hidden, current_user&.preferred_language, "1.1-updated_header", moved_mr_sidebar_enabled?, hide_gutter_toggle]
+- cache_key = [@project, @merge_request, can_update_merge_request, can_reopen_merge_request, are_close_and_open_buttons_hidden, current_user&.preferred_language, "1.1-updated_header", moved_mr_sidebar_enabled?, hide_gutter_toggle, fluid_layout]
= cache(cache_key, expires_in: 1.day) do
- if @merge_request.closed_or_merged_without_fork?
diff --git a/config/feature_flags/development/rebalance_issues.yml b/config/feature_flags/development/rebalance_issues.yml
index 5b2a4c2437b..5651b02b073 100644
--- a/config/feature_flags/development/rebalance_issues.yml
+++ b/config/feature_flags/development/rebalance_issues.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/239344
milestone: '13.4'
type: development
group: group::project management
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/experiment/showcase_free_security_features.yml b/config/feature_flags/experiment/showcase_free_security_features.yml
new file mode 100644
index 00000000000..e9a55d03adc
--- /dev/null
+++ b/config/feature_flags/experiment/showcase_free_security_features.yml
@@ -0,0 +1,8 @@
+---
+name: showcase_free_security_features
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85359
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/360677
+milestone: '15.2'
+type: experiment
+group: group::acquisition
+default_enabled: false
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index 1535de0c2c2..619080708de 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -489,11 +489,6 @@ To do that:
1. Select the **Disable public access to Pages sites** checkbox.
1. Select **Save changes**.
-WARNING:
-For self-managed installations, all public websites remain private until they are
-redeployed. Resolve this issue by
-[sourcing domain configuration from the GitLab API](https://gitlab.com/gitlab-org/gitlab/-/issues/218357).
-
### Running behind a proxy
Like the rest of GitLab, Pages can be used in those environments where external
diff --git a/doc/api/users.md b/doc/api/users.md
index ddd7de58228..a0aba8681a3 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -284,7 +284,7 @@ Parameters:
| Attribute | Type | Required | Description |
|-----------|---------|----------|------------------|
-| `id` | integer | yes | The ID of a user |
+| `id` | integer | yes | ID of a user |
```json
{
@@ -326,7 +326,7 @@ Parameters:
| Attribute | Type | Required | Description |
|-----------|---------|----------|------------------|
-| `id` | integer | yes | The ID of a user |
+| `id` | integer | yes | ID of a user |
Example Responses:
@@ -477,7 +477,7 @@ Parameters:
| `shared_runners_minutes_limit` **(PREMIUM)** | No | Can be set by administrators only. Maximum number of monthly CI/CD minutes for this user. Can be `nil` (default; inherit system default), `0` (unlimited), or `> 0`. |
| `skip_confirmation` | No | Skip confirmation - true or false (default) |
| `skype` | No | Skype ID |
-| `theme_id` | No | The GitLab theme for the user (see [the user preference docs](../user/profile/preferences.md#navigation-theme) for more information) |
+| `theme_id` | No | GitLab theme for the user (see [the user preference docs](../user/profile/preferences.md#navigation-theme) for more information) |
| `twitter` | No | Twitter account |
| `username` | Yes | Username |
| `view_diffs_file_by_file` | No | Flag indicating the user sees only one file diff per page |
@@ -507,7 +507,7 @@ Parameters:
| `external` | No | Flags the user as external - true or false (default) |
| `extra_shared_runners_minutes_limit` **(PREMIUM)** | No | Can be set by administrators only. Additional CI/CD minutes for this user. |
| `group_id_for_saml` | No | ID of group where SAML has been configured |
-| `id` | Yes | The ID of the user |
+| `id` | Yes | ID of the user |
| `linkedin` | No | LinkedIn |
| `location` | No | User's location |
| `name` | No | Name |
@@ -517,11 +517,11 @@ Parameters:
| `private_profile` | No | User's profile is private - true, false (default), or null (is converted to false) |
| `projects_limit` | No | Limit projects each user can create |
| `provider` | No | External provider name |
-| `public_email` | No | The public email of the user (must be already verified) |
+| `public_email` | No | Public email of the user (must be already verified) |
| `shared_runners_minutes_limit` **(PREMIUM)** | No | Can be set by administrators only. Maximum number of monthly CI/CD minutes for this user. Can be `nil` (default; inherit system default), `0` (unlimited) or `> 0`. |
| `skip_reconfirmation` | No | Skip reconfirmation - true or false (default) |
| `skype` | No | Skype ID |
-| `theme_id` | No | The GitLab theme for the user (see [the user preference docs](../user/profile/preferences.md#navigation-theme) for more information) |
+| `theme_id` | No | GitLab theme for the user (see [the user preference docs](../user/profile/preferences.md#navigation-theme) for more information) |
| `twitter` | No | Twitter account |
| `username` | No | Username |
| `view_diffs_file_by_file` | No | Flag indicating the user sees only one file diff per page |
@@ -544,7 +544,7 @@ Parameters:
| Attribute | Type | Required | Description |
|------------|---------|----------|------------------------|
-| `id` | integer | yes | The ID of a user |
+| `id` | integer | yes | ID of a user |
| `provider` | string | yes | External provider name |
## User deletion
@@ -560,10 +560,14 @@ Parameters:
| Attribute | Type | Required | Description |
|---------------|---------|----------|----------------------------------------------|
-| `id` | integer | yes | The ID of a user |
+| `id` | integer | yes | ID of a user |
| `hard_delete` | boolean | no | If true, contributions that would usually be [moved to Ghost User](../user/profile/account/delete_account.md#associated-records) are deleted instead, as well as groups owned solely by this user. |
-## List current user (for normal users)
+## List current user
+
+Get current user.
+
+### For normal users
Gets currently authenticated user.
@@ -619,7 +623,7 @@ GET /user
Users on [GitLab Premium or higher](https://about.gitlab.com/pricing/) also see the `shared_runners_minutes_limit`, `extra_shared_runners_minutes_limit` parameters.
-## List current user (for administrators)
+### For administrators
> The `namespace_id` field in the response was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82045) in GitLab 14.10.
@@ -631,7 +635,7 @@ Parameters:
| Attribute | Type | Required | Description |
|-----------|---------|----------|--------------------------------------------------|
-| `sudo` | integer | no | the ID of a user to make the call in their place |
+| `sudo` | integer | no | ID of a user to make the call in their place |
```json
{
@@ -721,7 +725,7 @@ GET /users/:id_or_username/status
| Attribute | Type | Required | Description |
| ---------------- | ------ | -------- | ------------------------------------------------- |
-| `id_or_username` | string | yes | The ID or username of the user to get a status of |
+| `id_or_username` | string | yes | ID or username of the user to get a status of |
```shell
curl "https://gitlab.example.com/users/janedoe/status"
@@ -749,8 +753,8 @@ PUT /user/status
| Attribute | Type | Required | Description |
| -------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `emoji` | string | no | The name of the emoji to use as status. If omitted `speech_balloon` is used. Emoji name can be one of the specified names in the [Gemojione index](https://github.com/bonusly/gemojione/blob/master/config/index.json). |
-| `message` | string | no | The message to set as a status. It can also contain emoji codes. |
+| `emoji` | string | no | Name of the emoji to use as status. If omitted `speech_balloon` is used. Emoji name can be one of the specified names in the [Gemojione index](https://github.com/bonusly/gemojione/blob/master/config/index.json). |
+| `message` | string | no | Message to set as a status. It can also contain emoji codes. |
| `clear_status_after` | string | no | Automatically clean up the status after a given time interval, allowed values: `30_minutes`, `3_hours`, `8_hours`, `1_day`, `3_days`, `7_days`, `30_days`
When both parameters `emoji` and `message` are empty, the status is cleared. When the `clear_status_after` parameter is missing from the request, the previously set value for `"clear_status_after` is cleared.
@@ -818,7 +822,7 @@ Parameters:
| `view_diffs_file_by_file` | Yes | Flag indicating the user sees only one file diff per page. |
| `show_whitespace_in_diffs` | Yes | Flag indicating the user sees whitespace changes in diffs. |
-## User Follow
+## User follow
### Follow and unfollow users
@@ -836,7 +840,7 @@ POST /users/:id/unfollow
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | ---------------------------- |
-| `id` | integer | yes | The ID of the user to follow |
+| `id` | integer | yes | ID of the user to follow |
```shell
curl --request POST --header "PRIVATE-TOKEN: " "https://gitlab.example.com/users/3/follow"
@@ -871,7 +875,7 @@ GET /users/:id/following
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | ---------------------------- |
-| `id` | integer | yes | The ID of the user to follow |
+| `id` | integer | yes | ID of the user to follow |
```shell
curl --request GET --header "PRIVATE-TOKEN: " "https://gitlab.example.com/users/3/followers"
@@ -975,7 +979,7 @@ GET /users/:id_or_username/keys
| Attribute | Type | Required | Description |
| ---------------- | ------ | -------- | ------------------------------------------------------- |
-| `id_or_username` | string | yes | The ID or username of the user to get the SSH keys for. |
+| `id_or_username` | string | yes | ID or username of the user to get the SSH keys for. |
## Single SSH key
@@ -989,7 +993,7 @@ Parameters:
| Attribute | Type | Required | Description |
|-----------|--------|----------|----------------------|
-| `key_id` | string | yes | The ID of an SSH key |
+| `key_id` | string | yes | ID of an SSH key |
```json
{
@@ -1038,9 +1042,9 @@ Parameters:
| Attribute | Type | Required | Description |
|--------------|--------|----------|--------------------------------------------------------------------------------|
-| `title` | string | yes | new SSH key's title |
-| `key` | string | yes | new SSH key |
-| `expires_at` | string | no | The expiration date of the SSH key in ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`) |
+| `title` | string | yes | New SSH key's title |
+| `key` | string | yes | New SSH key |
+| `expires_at` | string | no | Expiration date of the SSH key in ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`) |
```json
{
@@ -1079,9 +1083,9 @@ Parameters:
| Attribute | Type | Required | Description |
|--------------|---------|----------|--------------------------------------------------------------------------------|
| `id` | integer | yes | ID of specified user |
-| `title` | string | yes | new SSH key's title |
-| `key` | string | yes | new SSH key |
-| `expires_at` | string | no | The expiration date of the SSH key in ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`) |
+| `title` | string | yes | New SSH key's title |
+| `key` | string | yes | New SSH key |
+| `expires_at` | string | no | Expiration date of the SSH key in ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`) |
NOTE:
This also adds an audit event, as described in [audit instance events](../administration/audit_events.md#instance-events). **(PREMIUM)**
@@ -1152,7 +1156,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
-| `key_id` | integer | yes | The ID of the GPG key |
+| `key_id` | integer | yes | ID of the GPG key |
```shell
curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/user/gpg_keys/1"
@@ -1180,7 +1184,7 @@ Parameters:
| Attribute | Type | Required | Description |
|-----------|--------|----------|-----------------|
-| `key` | string | yes | The new GPG key |
+| `key` | string | yes | New GPG key |
```shell
curl --data "key=-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n\r\nxsBNBFV..." \
@@ -1211,7 +1215,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
-| `key_id` | integer | yes | The ID of the GPG key |
+| `key_id` | integer | yes | ID of the GPG key |
```shell
curl --request DELETE --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/user/gpg_keys/1"
@@ -1231,7 +1235,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | ------------------ |
-| `id` | integer | yes | The ID of the user |
+| `id` | integer | yes | ID of the user |
```shell
curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/users/2/gpg_keys"
@@ -1262,8 +1266,8 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
-| `id` | integer | yes | The ID of the user |
-| `key_id` | integer | yes | The ID of the GPG key |
+| `id` | integer | yes | ID of the user |
+| `key_id` | integer | yes | ID of the GPG key |
```shell
curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/users/2/gpg_keys/1"
@@ -1291,8 +1295,8 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
-| `id` | integer | yes | The ID of the user |
-| `key_id` | integer | yes | The ID of the GPG key |
+| `id` | integer | yes | ID of the user |
+| `key_id` | integer | yes | ID of the GPG key |
```shell
curl --data "key=-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n\r\nxsBNBFV..." \
@@ -1323,8 +1327,8 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
-| `id` | integer | yes | The ID of the user |
-| `key_id` | integer | yes | The ID of the GPG key |
+| `id` | integer | yes | ID of the user |
+| `key_id` | integer | yes | ID of the GPG key |
```shell
curl --request DELETE --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/users/2/gpg_keys/1"
@@ -1627,8 +1631,8 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | ---------------------------------------------------------- |
-| `user_id` | integer | yes | The ID of the user |
-| `state` | string | no | filter tokens based on state (`all`, `active`, `inactive`) |
+| `user_id` | integer | yes | ID of the user |
+| `state` | string | no | Filter tokens based on state (`all`, `active`, `inactive`) |
```shell
curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/users/42/impersonation_tokens"
@@ -1761,8 +1765,8 @@ Parameters:
| Attribute | Type | Required | Description |
| ------------------------ | ------- | -------- | --------------------------------- |
-| `user_id` | integer | yes | The ID of the user |
-| `impersonation_token_id` | integer | yes | The ID of the impersonation token |
+| `user_id` | integer | yes | ID of the user |
+| `impersonation_token_id` | integer | yes | ID of the impersonation token |
```shell
curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/users/42/impersonation_tokens/2"
@@ -1802,10 +1806,10 @@ POST /users/:user_id/impersonation_tokens
| Attribute | Type | Required | Description |
| ------------ | ------- | -------- | --------------------------------------------------------------------------- |
-| `user_id` | integer | yes | The ID of the user |
-| `name` | string | yes | The name of the impersonation token |
-| `expires_at` | date | no | The expiration date of the impersonation token in ISO format (`YYYY-MM-DD`) |
-| `scopes` | array | yes | The array of scopes of the impersonation token (`api`, `read_user`) |
+| `user_id` | integer | yes | ID of the user |
+| `name` | string | yes | Name of the impersonation token |
+| `expires_at` | date | no | Expiration date of the impersonation token in ISO format (`YYYY-MM-DD`) |
+| `scopes` | array | yes | Array of scopes of the impersonation token (`api`, `read_user`) |
```shell
curl --request POST --header "PRIVATE-TOKEN: " --data "name=mytoken" --data "expires_at=2017-04-04" \
@@ -1845,8 +1849,8 @@ Parameters:
| Attribute | Type | Required | Description |
| ------------------------ | ------- | -------- | --------------------------------- |
-| `user_id` | integer | yes | The ID of the user |
-| `impersonation_token_id` | integer | yes | The ID of the impersonation token |
+| `user_id` | integer | yes | ID of the user |
+| `impersonation_token_id` | integer | yes | ID of the impersonation token |
```shell
curl --request DELETE --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/users/42/impersonation_tokens/1"
@@ -1867,10 +1871,10 @@ POST /users/:user_id/personal_access_tokens
| Attribute | Type | Required | Description |
| ------------ | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------ |
-| `user_id` | integer | yes | The ID of the user |
-| `name` | string | yes | The name of the personal access token |
-| `expires_at` | date | no | The expiration date of the personal access token in ISO format (`YYYY-MM-DD`) |
-| `scopes` | array | yes | The array of scopes of the personal access token. See [personal access token scopes](../user/profile/personal_access_tokens.md#personal-access-token-scopes) for possible values. |
+| `user_id` | integer | yes | ID of the user |
+| `name` | string | yes | Name of the personal access token |
+| `expires_at` | date | no | Expiration date of the personal access token in ISO format (`YYYY-MM-DD`) |
+| `scopes` | array | yes | Array of scopes of the personal access token. See [personal access token scopes](../user/profile/personal_access_tokens.md#personal-access-token-scopes) for possible values. |
```shell
curl --request POST --header "PRIVATE-TOKEN: " --data "name=mytoken" --data "expires_at=2017-04-04" \
@@ -1895,10 +1899,11 @@ Example response:
}
```
-## Get user activities (administrator only)
+## Get user activities
-NOTE:
-This API endpoint is only available on 8.15 (EE) and 9.1 (CE) and above.
+Pre-requisite:
+
+- You must be an administrator.
Get the last activity date for all users, sorted from oldest to newest.
@@ -1921,7 +1926,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ------ | -------- | ---------------------------------------------------------------------------------------------- |
-| `from` | string | no | Date string in the format YEAR-MONTH-DAY. For example, `2016-03-11`. Defaults to 6 months ago. |
+| `from` | string | no | Date string in the format `YEAR-MM-DD`. For example, `2016-03-11`. Defaults to 6 months ago. |
```shell
curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/user/activities"
@@ -1951,11 +1956,15 @@ Example response:
`last_activity_at` is deprecated. Use `last_activity_on` instead.
-## User memberships (administrator only)
+## User memberships
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/20532) in GitLab 12.8.
-Lists all projects and groups a user is a member of. This endpoint is available for administrators only.
+Pre-requisite:
+
+- You must be an administrator.
+
+Lists all projects and groups a user is a member of.
It returns the `source_id`, `source_name`, `source_type` and `access_level` of a membership.
Source can be of type `Namespace` (representing a group) or `Project`. The response represents only direct memberships. Inherited memberships, for example in subgroups, are not included.
Access levels are represented by an integer value. For more details, read about the meaning of [access level values](access_requests.md#valid-access-levels).
@@ -1968,7 +1977,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | ------------------------------------------------------------------ |
-| `id` | integer | yes | The ID of a specified user |
+| `id` | integer | yes | ID of a specified user |
| `type` | string | no | Filter memberships by type. Can be either `Project` or `Namespace` |
Returns:
@@ -2001,11 +2010,15 @@ Example response:
]
```
-## Disable two factor authentication (administrator only)
+## Disable two factor authentication
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/295260) in GitLab 15.2.
-Disables two factor authentication (2FA) for the specified user. Only administrators can disable 2FA for users.
+Pre-requisite:
+
+- You must be an administrator.
+
+Disables two factor authentication (2FA) for the specified user.
Administrators cannot disable 2FA for their own user account or other administrators using the API. Instead, they can disable an
administrator's 2FA [using the Rails console](../security/two_factor_authentication.md#for-a-single-user).
@@ -2018,7 +2031,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
-| `id` | integer | yes | The ID of the user |
+| `id` | integer | yes | ID of the user |
```shell
curl --request PATCH --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/users/1/disable_two_factor"
diff --git a/doc/ci/cloud_deployment/heroku.md b/doc/ci/cloud_deployment/heroku.md
new file mode 100644
index 00000000000..4e627675b01
--- /dev/null
+++ b/doc/ci/cloud_deployment/heroku.md
@@ -0,0 +1,32 @@
+---
+stage: Release
+group: Release
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+---
+
+# Use GitLab CI/CD to deploy to Heroku
+
+You can deploy an application to Heroku by using GitLab CI/CD.
+
+## Prerequisites
+
+- A [Heroku](https://id.heroku.com/login) account.
+ Sign in with an existing Heroku account or create a new one.
+
+## Deploy to Heroku
+
+1. In Heroku:
+ 1. Create an application and copy the application name.
+ 1. Browse to **Account Settings** and copy the API key.
+1. In your GitLab project, create two [variables](../../ci/variables/index.md):
+ - `HEROKU_APP_NAME` for the application name.
+ - `HEROKU_PRODUCTION_KEY` for the API key
+1. Edit your `.gitlab-ci.yml` file to add the Heroku deployment command. This example uses the `dpl` gem for Ruby:
+
+ ```yaml
+ heroku_deploy:
+ stage: production
+ script:
+ - gem install dpl
+ - dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_PRODUCTION_KEY
+ ```
diff --git a/doc/ci/examples/index.md b/doc/ci/examples/index.md
index 0fc7b06a584..251256ceb4d 100644
--- a/doc/ci/examples/index.md
+++ b/doc/ci/examples/index.md
@@ -95,6 +95,7 @@ choose one of these templates:
- [Rust (`Rust.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Rust.gitlab-ci.yml)
- [Scala (`Scala.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Scala.gitlab-ci.yml)
- [Swift (`Swift.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Swift.gitlab-ci.yml)
+- [Terraform (`Terraform.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Terraform.gitlab-ci.yml)
- [Terraform (`Terraform.latest.gitlab-ci.yml`)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Terraform.latest.gitlab-ci.yml)
If a programming language or framework template is not in this list, you can contribute
diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md
index e6d61ef342b..f8e83ac7123 100644
--- a/doc/ci/variables/predefined_variables.md
+++ b/doc/ci/variables/predefined_variables.md
@@ -29,7 +29,7 @@ as it can cause the pipeline to behave unexpectedly.
| `CI_API_V4_URL` | 11.7 | all | The GitLab API v4 root URL. |
| `CI_BUILDS_DIR` | all | 11.10 | The top-level directory where builds are executed. |
| `CI_COMMIT_AUTHOR` | 13.11 | all | The author of the commit in `Name ` format. |
-| `CI_COMMIT_BEFORE_SHA` | 11.2 | all | The previous latest commit present on a branch. Is always `0000000000000000000000000000000000000000` in merge request pipelines. |
+| `CI_COMMIT_BEFORE_SHA` | 11.2 | all | The previous latest commit present on a branch or tag. Is always `0000000000000000000000000000000000000000` in merge request pipelines and for the first commit in pipelines for branches or tags. |
| `CI_COMMIT_BRANCH` | 12.6 | 0.5 | The commit branch name. Available in branch pipelines, including pipelines for the default branch. Not available in merge request pipelines or tag pipelines. |
| `CI_COMMIT_DESCRIPTION` | 10.8 | all | The description of the commit. If the title is shorter than 100 characters, the message without the first line. |
| `CI_COMMIT_MESSAGE` | 10.8 | all | The full commit message. |
diff --git a/doc/security/two_factor_authentication.md b/doc/security/two_factor_authentication.md
index bcc50b01b0e..65bdcfd755e 100644
--- a/doc/security/two_factor_authentication.md
+++ b/doc/security/two_factor_authentication.md
@@ -94,7 +94,7 @@ This is a permanent and irreversible action. Users must reactivate 2FA to use it
### For a single user
-To disable 2FA for non-administrator users, we recommend using the [API endpoint](../api/users.md#disable-two-factor-authentication-administrator-only)
+To disable 2FA for non-administrator users, we recommend using the [API endpoint](../api/users.md#disable-two-factor-authentication)
instead of the Rails console.
Using the [Rails console](../administration/operations/rails_console.md), 2FA for a single user can be disabled.
Connect to the Rails console and run:
diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md
index 8f36021084e..295c079c19c 100644
--- a/doc/user/admin_area/index.md
+++ b/doc/user/admin_area/index.md
@@ -184,7 +184,7 @@ The following data is included in the export:
- Type
- Path
- Access level ([Project](../permissions.md#project-members-permissions) and [Group](../permissions.md#group-members-permissions))
-- Date of last activity ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345388) in GitLab 14.6). For a list of activities that populate this column, see the [Users API documentation](../../api/users.md#get-user-activities-administrator-only).
+- Date of last activity ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345388) in GitLab 14.6). For a list of activities that populate this column, see the [Users API documentation](../../api/users.md#get-user-activities).
Only the first 100,000 user accounts are exported.
diff --git a/doc/user/project/import/bitbucket.md b/doc/user/project/import/bitbucket.md
index b2425686024..ff2b8674434 100644
--- a/doc/user/project/import/bitbucket.md
+++ b/doc/user/project/import/bitbucket.md
@@ -102,7 +102,7 @@ Bitbucket username after connecting their Bitbucket account in the
To fix this, the user must verify that their Bitbucket external UID in the GitLab database matches their
current Bitbucket public name, and reconnect if there's a mismatch:
-1. [Use the API to get the currently authenticated user](../../../api/users.md#list-current-user-for-normal-users).
+1. [Use the API to get the currently authenticated user](../../../api/users.md#for-normal-users-1).
1. In the API's response, the `identities` attribute contains the Bitbucket account that exists in
the GitLab database. If the `extern_uid` doesn't match the current Bitbucket public name, the
diff --git a/lib/gitlab/contributions_calendar.rb b/lib/gitlab/contributions_calendar.rb
index f48ba27888c..4d289a59a6a 100644
--- a/lib/gitlab/contributions_calendar.rb
+++ b/lib/gitlab/contributions_calendar.rb
@@ -12,11 +12,8 @@ module Gitlab
@contributor = contributor
@contributor_time_instance = local_timezone_instance(contributor.timezone).now
@current_user = current_user
- @projects = if @contributor.include_private_contributions?
- ContributedProjectsFinder.new(@contributor).execute(@contributor)
- else
- ContributedProjectsFinder.new(contributor).execute(current_user)
- end
+ @projects = ContributedProjectsFinder.new(contributor)
+ .execute(current_user, ignore_visibility: @contributor.include_private_contributions?)
end
# rubocop: disable CodeReuse/ActiveRecord
diff --git a/lib/gitlab/sidekiq_logging/structured_logger.rb b/lib/gitlab/sidekiq_logging/structured_logger.rb
index 6eb39981ef4..7ce3f6b5ccb 100644
--- a/lib/gitlab/sidekiq_logging/structured_logger.rb
+++ b/lib/gitlab/sidekiq_logging/structured_logger.rb
@@ -81,12 +81,6 @@ module Gitlab
payload['job_status'] = 'fail'
Gitlab::ExceptionLogFormatter.format!(job_exception, payload)
-
- # Deprecated fields for compatibility
- # See https://gitlab.com/gitlab-org/gitlab/-/issues/364241
- payload['error_class'] = payload['exception.class']
- payload['error_message'] = payload['exception.message']
- payload['error_backtrace'] = payload['exception.backtrace']
else
payload['message'] = "#{message}: done: #{payload['duration_s']} sec"
payload['job_status'] = 'done'
diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake
index 026511d5943..84de21ea102 100644
--- a/lib/tasks/gitlab/db.rake
+++ b/lib/tasks/gitlab/db.rake
@@ -426,9 +426,10 @@ namespace :gitlab do
end
end
- Rake::Task['db:migrate'].enhance do
- Rake::Task['gitlab:db:dictionary:generate'].invoke if Rails.env.development?
- end
+ # Temporary disable this, see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85760#note_998452069
+ # Rake::Task['db:migrate'].enhance do
+ # Rake::Task['gitlab:db:dictionary:generate'].invoke if Rails.env.development?
+ # end
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 0871c636f5d..d6ae2c1fc13 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -6789,6 +6789,9 @@ msgstr ""
msgid "BulkImport|must be a group"
msgstr ""
+msgid "Bullet list"
+msgstr ""
+
msgid "Burndown chart"
msgstr ""
@@ -8938,6 +8941,9 @@ msgstr ""
msgid "Code Review Analytics displays a table of open merge requests considered to be in code review. There are currently no merge requests in review for this project and/or filters."
msgstr ""
+msgid "Code block"
+msgstr ""
+
msgid "Code coverage statistics for %{ref} %{start_date} - %{end_date}"
msgstr ""
@@ -12977,6 +12983,9 @@ msgstr ""
msgid "Details (default)"
msgstr ""
+msgid "Details block"
+msgstr ""
+
msgid "Detect host keys"
msgstr ""
@@ -20455,9 +20464,6 @@ msgstr ""
msgid "Insert a %{rows}x%{cols} table."
msgstr ""
-msgid "Insert a code block"
-msgstr ""
-
msgid "Insert a quote"
msgstr ""
@@ -24126,12 +24132,18 @@ msgstr ""
msgid "Merge request events"
msgstr ""
+msgid "Merge request locked."
+msgstr ""
+
msgid "Merge request not merged"
msgstr ""
msgid "Merge request reports"
msgstr ""
+msgid "Merge request unlocked."
+msgstr ""
+
msgid "Merge request was scheduled to merge after pipeline succeeds"
msgstr ""
@@ -26154,6 +26166,12 @@ msgstr ""
msgid "Notifications on"
msgstr ""
+msgid "Notifications turned off."
+msgstr ""
+
+msgid "Notifications turned on."
+msgstr ""
+
msgid "Notify users by email when sign-in location is not recognized."
msgstr ""
@@ -26876,6 +26894,9 @@ msgstr ""
msgid "Or you can choose one of the suggested colors below"
msgstr ""
+msgid "Ordered list"
+msgstr ""
+
msgid "Organizations"
msgstr ""
@@ -35482,6 +35503,63 @@ msgstr ""
msgid "Show whitespace changes"
msgstr ""
+msgid "ShowcaseSecurity|Access a dedicated area for vulnerability management. This includes a security dashboard, vulnerability report, and settings."
+msgstr ""
+
+msgid "ShowcaseSecurity|Audit your Docker-based app. Scan for known vulnerabilities in the Docker images where your code is shipped."
+msgstr ""
+
+msgid "ShowcaseSecurity|Container scanning"
+msgstr ""
+
+msgid "ShowcaseSecurity|Dependency scanning"
+msgstr ""
+
+msgid "ShowcaseSecurity|Dynamic Application Security Testing (DAST)"
+msgstr ""
+
+msgid "ShowcaseSecurity|Dynamically examine your application for vulnerabilities in deployed environments."
+msgstr ""
+
+msgid "ShowcaseSecurity|Enable SAST"
+msgstr ""
+
+msgid "ShowcaseSecurity|Enable Secret Detection"
+msgstr ""
+
+msgid "ShowcaseSecurity|Enable Static Application Security Testing (SAST)"
+msgstr ""
+
+msgid "ShowcaseSecurity|Find out if your external libraries are safe. Run dependency scanning jobs that check for known vulnerabilities in your external libraries."
+msgstr ""
+
+msgid "ShowcaseSecurity|Identify vulnerabilities in your code now"
+msgstr ""
+
+msgid "ShowcaseSecurity|Scan your code to detect unintentionally committed secrets, like keys, passwords, and API tokens."
+msgstr ""
+
+msgid "ShowcaseSecurity|Scan your source code using GitLab CI/CD and uncover vulnerabilities before deploying."
+msgstr ""
+
+msgid "ShowcaseSecurity|Start a free 30-day Ultimate trial or upgrade your instance to access organization-wide security and compliance features. See the other features of the Ultimate plan."
+msgstr ""
+
+msgid "ShowcaseSecurity|Start a free trial"
+msgstr ""
+
+msgid "ShowcaseSecurity|Take your security to the next level"
+msgstr ""
+
+msgid "ShowcaseSecurity|Upgrade now"
+msgstr ""
+
+msgid "ShowcaseSecurity|Use GitLab CI/CD to analyze your source code for known vulnerabilities. Compare the found vulnerabilities between your source and target branches."
+msgstr ""
+
+msgid "ShowcaseSecurity|Vulnerability management"
+msgstr ""
+
msgid "Showing %{conflict}"
msgstr ""
@@ -37580,6 +37658,9 @@ msgstr ""
msgid "Task ID: %{elastic_task}"
msgstr ""
+msgid "Task list"
+msgstr ""
+
msgid "TasksToBeDone|Create/import code into a project (repository)"
msgstr ""
diff --git a/spec/finders/contributed_projects_finder_spec.rb b/spec/finders/contributed_projects_finder_spec.rb
index 86d3e7f8f19..e8ce02122a1 100644
--- a/spec/finders/contributed_projects_finder_spec.rb
+++ b/spec/finders/contributed_projects_finder_spec.rb
@@ -23,9 +23,15 @@ RSpec.describe ContributedProjectsFinder do
end
describe 'activity without a current user' do
- subject { finder.execute }
+ it 'does only return public projects' do
+ projects = finder.execute
+ expect(projects).to match_array([public_project])
+ end
- it { is_expected.to match_array([public_project]) }
+ it 'does return all projects when visibility gets ignored' do
+ projects = finder.execute(ignore_visibility: true)
+ expect(projects).to match_array([private_project, internal_project, public_project])
+ end
end
describe 'activity with a current user' do
diff --git a/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js b/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js
index 0334a18c9a1..351fd967719 100644
--- a/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js
+++ b/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js
@@ -2,22 +2,26 @@ import { mountExtended } from 'helpers/vue_test_utils_helper';
import ToolbarMoreDropdown from '~/content_editor/components/toolbar_more_dropdown.vue';
import Diagram from '~/content_editor/extensions/diagram';
import HorizontalRule from '~/content_editor/extensions/horizontal_rule';
-import { createTestEditor, mockChainedCommands } from '../test_utils';
+import eventHubFactory from '~/helpers/event_hub_factory';
+import { createTestEditor, mockChainedCommands, emitEditorEvent } from '../test_utils';
describe('content_editor/components/toolbar_more_dropdown', () => {
let wrapper;
let tiptapEditor;
+ let eventHub;
const buildEditor = () => {
tiptapEditor = createTestEditor({
extensions: [Diagram, HorizontalRule],
});
+ eventHub = eventHubFactory();
};
const buildWrapper = (propsData = {}) => {
wrapper = mountExtended(ToolbarMoreDropdown, {
provide: {
tiptapEditor,
+ eventHub,
},
propsData,
});
@@ -33,19 +37,30 @@ describe('content_editor/components/toolbar_more_dropdown', () => {
});
describe.each`
- label | contentType | data
- ${'Mermaid diagram'} | ${'diagram'} | ${{ language: 'mermaid' }}
- ${'PlantUML diagram'} | ${'diagram'} | ${{ language: 'plantuml' }}
- ${'Horizontal rule'} | ${'horizontalRule'} | ${undefined}
- `('when option $label is clicked', ({ label, contentType, data }) => {
- it(`inserts a ${contentType}`, async () => {
- const commands = mockChainedCommands(tiptapEditor, ['setNode', 'focus', 'run']);
+ name | contentType | command | params
+ ${'Code block'} | ${'codeBlock'} | ${'setNode'} | ${['codeBlock']}
+ ${'Details block'} | ${'details'} | ${'toggleList'} | ${['details', 'detailsContent']}
+ ${'Bullet list'} | ${'bulletList'} | ${'toggleList'} | ${['bulletList', 'listItem']}
+ ${'Ordered list'} | ${'orderedList'} | ${'toggleList'} | ${['orderedList', 'listItem']}
+ ${'Task list'} | ${'taskList'} | ${'toggleList'} | ${['taskList', 'taskItem']}
+ ${'Mermaid diagram'} | ${'diagram'} | ${'setNode'} | ${['diagram', { language: 'mermaid' }]}
+ ${'PlantUML diagram'} | ${'diagram'} | ${'setNode'} | ${['diagram', { language: 'plantuml' }]}
+ ${'Horizontal rule'} | ${'horizontalRule'} | ${'setHorizontalRule'} | ${[]}
+ `('when option $label is clicked', ({ name, command, contentType, params }) => {
+ let commands;
+ let btn;
- const btn = wrapper.findByRole('menuitem', { name: label });
+ beforeEach(async () => {
+ commands = mockChainedCommands(tiptapEditor, [command, 'focus', 'run']);
+ btn = wrapper.findByRole('menuitem', { name });
+ });
+
+ it(`inserts a ${contentType}`, async () => {
await btn.trigger('click');
+ await emitEditorEvent({ event: 'transaction', tiptapEditor });
expect(commands.focus).toHaveBeenCalled();
- expect(commands.setNode).toHaveBeenCalledWith(contentType, data);
+ expect(commands[command]).toHaveBeenCalledWith(...params);
expect(commands.run).toHaveBeenCalled();
expect(wrapper.emitted('execute')).toEqual([[{ contentType }]]);
diff --git a/spec/frontend/content_editor/components/top_toolbar_spec.js b/spec/frontend/content_editor/components/top_toolbar_spec.js
index d98a9a52aff..2acb6e14ce0 100644
--- a/spec/frontend/content_editor/components/top_toolbar_spec.js
+++ b/spec/frontend/content_editor/components/top_toolbar_spec.js
@@ -24,17 +24,15 @@ describe('content_editor/components/top_toolbar', () => {
describe.each`
testId | controlProps
+ ${'text-styles'} | ${{}}
${'bold'} | ${{ contentType: 'bold', iconName: 'bold', label: 'Bold text', editorCommand: 'toggleBold' }}
${'italic'} | ${{ contentType: 'italic', iconName: 'italic', label: 'Italic text', editorCommand: 'toggleItalic' }}
- ${'strike'} | ${{ contentType: 'strike', iconName: 'strikethrough', label: 'Strikethrough', editorCommand: 'toggleStrike' }}
- ${'code'} | ${{ contentType: 'code', iconName: 'code', label: 'Code', editorCommand: 'toggleCode' }}
${'blockquote'} | ${{ contentType: 'blockquote', iconName: 'quote', label: 'Insert a quote', editorCommand: 'toggleBlockquote' }}
+ ${'code'} | ${{ contentType: 'code', iconName: 'code', label: 'Code', editorCommand: 'toggleCode' }}
+ ${'link'} | ${{}}
${'bullet-list'} | ${{ contentType: 'bulletList', iconName: 'list-bulleted', label: 'Add a bullet list', editorCommand: 'toggleBulletList' }}
${'ordered-list'} | ${{ contentType: 'orderedList', iconName: 'list-numbered', label: 'Add a numbered list', editorCommand: 'toggleOrderedList' }}
- ${'details'} | ${{ contentType: 'details', iconName: 'details-block', label: 'Add a collapsible section', editorCommand: 'toggleDetails' }}
- ${'code-block'} | ${{ contentType: 'codeBlock', iconName: 'doc-code', label: 'Insert a code block', editorCommand: 'toggleCodeBlock' }}
- ${'text-styles'} | ${{}}
- ${'link'} | ${{}}
+ ${'task-list'} | ${{ contentType: 'taskList', iconName: 'list-task', label: 'Add a task list', editorCommand: 'toggleTaskList' }}
${'image'} | ${{}}
${'table'} | ${{}}
${'more'} | ${{}}
diff --git a/spec/frontend/sidebar/components/subscriptions/sidebar_subscriptions_widget_spec.js b/spec/frontend/sidebar/components/subscriptions/sidebar_subscriptions_widget_spec.js
index 549ab99c6af..9a68940590d 100644
--- a/spec/frontend/sidebar/components/subscriptions/sidebar_subscriptions_widget_spec.js
+++ b/spec/frontend/sidebar/components/subscriptions/sidebar_subscriptions_widget_spec.js
@@ -8,15 +8,22 @@ import createFlash from '~/flash';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
import SidebarSubscriptionWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue';
import issueSubscribedQuery from '~/sidebar/queries/issue_subscribed.query.graphql';
-import { issueSubscriptionsResponse } from '../../mock_data';
+import updateMergeRequestSubscriptionMutation from '~/sidebar/queries/update_merge_request_subscription.mutation.graphql';
+import toast from '~/vue_shared/plugins/global_toast';
+import {
+ issueSubscriptionsResponse,
+ mergeRequestSubscriptionMutationResponse,
+} from '../../mock_data';
jest.mock('~/flash');
+jest.mock('~/vue_shared/plugins/global_toast');
Vue.use(VueApollo);
describe('Sidebar Subscriptions Widget', () => {
let wrapper;
let fakeApollo;
+ let subscriptionMutationHandler;
const findEditableItem = () => wrapper.findComponent(SidebarEditableItem);
const findToggle = () => wrapper.findComponent(GlToggle);
@@ -24,18 +31,29 @@ describe('Sidebar Subscriptions Widget', () => {
const createComponent = ({
subscriptionsQueryHandler = jest.fn().mockResolvedValue(issueSubscriptionsResponse()),
+ issuableType = 'issue',
+ movedMrSidebar = false,
} = {}) => {
- fakeApollo = createMockApollo([[issueSubscribedQuery, subscriptionsQueryHandler]]);
+ subscriptionMutationHandler = jest
+ .fn()
+ .mockResolvedValue(mergeRequestSubscriptionMutationResponse);
+ fakeApollo = createMockApollo([
+ [issueSubscribedQuery, subscriptionsQueryHandler],
+ [updateMergeRequestSubscriptionMutation, subscriptionMutationHandler],
+ ]);
wrapper = shallowMount(SidebarSubscriptionWidget, {
apolloProvider: fakeApollo,
provide: {
canUpdate: true,
+ glFeatures: {
+ movedMrSidebar,
+ },
},
propsData: {
fullPath: 'group/project',
iid: '1',
- issuableType: 'issue',
+ issuableType,
},
stubs: {
SidebarEditableItem,
@@ -128,4 +146,21 @@ describe('Sidebar Subscriptions Widget', () => {
expect(createFlash).toHaveBeenCalled();
});
+
+ describe('merge request', () => {
+ it('displays toast when mutation is successful', async () => {
+ createComponent({
+ issuableType: 'merge_request',
+ movedMrSidebar: true,
+ subscriptionsQueryHandler: jest.fn().mockResolvedValue(issueSubscriptionsResponse(true)),
+ });
+ await waitForPromises();
+
+ await wrapper.find('.dropdown-item').trigger('click');
+
+ await waitForPromises();
+
+ expect(toast).toHaveBeenCalledWith('Notifications turned on.');
+ });
+ });
});
diff --git a/spec/frontend/sidebar/lock/issuable_lock_form_spec.js b/spec/frontend/sidebar/lock/issuable_lock_form_spec.js
index 8478d3d674d..bb757fdf63b 100644
--- a/spec/frontend/sidebar/lock/issuable_lock_form_spec.js
+++ b/spec/frontend/sidebar/lock/issuable_lock_form_spec.js
@@ -1,17 +1,23 @@
import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
+import Vue, { nextTick } from 'vue';
+import Vuex from 'vuex';
import { mockTracking, triggerEvent } from 'helpers/tracking_helper';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import { createStore as createMrStore } from '~/mr_notes/stores';
import createStore from '~/notes/stores';
import EditForm from '~/sidebar/components/lock/edit_form.vue';
import IssuableLockForm from '~/sidebar/components/lock/issuable_lock_form.vue';
+import toast from '~/vue_shared/plugins/global_toast';
import { ISSUABLE_TYPE_ISSUE, ISSUABLE_TYPE_MR } from './constants';
+jest.mock('~/vue_shared/plugins/global_toast');
+
+Vue.use(Vuex);
+
describe('IssuableLockForm', () => {
let wrapper;
let store;
let issuableType; // Either ISSUABLE_TYPE_ISSUE or ISSUABLE_TYPE_MR
+ let updateLockedAttribute;
const setIssuableType = (pageType) => {
issuableType = pageType;
@@ -29,16 +35,27 @@ describe('IssuableLockForm', () => {
store = createStore();
store.getters.getNoteableData.targetType = 'issue';
} else {
- store = createMrStore();
+ updateLockedAttribute = jest.fn().mockResolvedValue();
+ store = new Vuex.Store({
+ getters: {
+ getNoteableData: () => ({ targetType: issuableType }),
+ },
+ actions: {
+ updateLockedAttribute,
+ },
+ });
}
store.getters.getNoteableData.discussion_locked = isLocked;
};
- const createComponent = ({ props = {} }) => {
+ const createComponent = ({ props = {} }, movedMrSidebar = false) => {
wrapper = shallowMount(IssuableLockForm, {
store,
provide: {
fullPath: '',
+ glFeatures: {
+ movedMrSidebar,
+ },
},
propsData: {
isEditable: true,
@@ -144,4 +161,24 @@ describe('IssuableLockForm', () => {
});
});
});
+
+ describe('merge requests', () => {
+ beforeEach(() => {
+ setIssuableType('merge_request');
+ });
+
+ it.each`
+ locked | message
+ ${true} | ${'Merge request locked.'}
+ ${false} | ${'Merge request unlocked.'}
+ `('displays $message when merge request is $locked', async ({ locked, message }) => {
+ initStore(locked);
+
+ createComponent({}, true);
+
+ await wrapper.find('.dropdown-item').trigger('click');
+
+ expect(toast).toHaveBeenCalledWith(message);
+ });
+ });
});
diff --git a/spec/frontend/sidebar/mock_data.js b/spec/frontend/sidebar/mock_data.js
index 2b421037339..229757ff40c 100644
--- a/spec/frontend/sidebar/mock_data.js
+++ b/spec/frontend/sidebar/mock_data.js
@@ -321,6 +321,19 @@ export const issueSubscriptionsResponse = (subscribed = false, emailsDisabled =
},
});
+export const mergeRequestSubscriptionMutationResponse = {
+ data: {
+ updateIssuableSubscription: {
+ issuable: {
+ __typename: 'MergeRequest',
+ id: 'gid://gitlab/MergeRequest/4',
+ subscribed: true,
+ },
+ errors: [],
+ },
+ },
+};
+
export const issuableQueryResponse = {
data: {
workspace: {
diff --git a/spec/support/shared_contexts/lib/gitlab/sidekiq_logging/structured_logger_shared_context.rb b/spec/support/shared_contexts/lib/gitlab/sidekiq_logging/structured_logger_shared_context.rb
index aa8bc6fa79f..a3c688bb69e 100644
--- a/spec/support/shared_contexts/lib/gitlab/sidekiq_logging/structured_logger_shared_context.rb
+++ b/spec/support/shared_contexts/lib/gitlab/sidekiq_logging/structured_logger_shared_context.rb
@@ -16,9 +16,6 @@ RSpec.shared_context 'structured_logger' do
"created_at" => created_at.to_f,
"enqueued_at" => created_at.to_f,
"correlation_id" => 'cid',
- "error_message" => "wrong number of arguments (2 for 3)",
- "error_class" => "ArgumentError",
- "error_backtrace" => [],
"exception.message" => "wrong number of arguments (2 for 3)",
"exception.class" => "ArgumentError",
"exception.backtrace" => []
@@ -32,7 +29,6 @@ RSpec.shared_context 'structured_logger' do
let(:clock_thread_cputime_end) { 1.333333799 }
let(:start_payload) do
job.except(
- 'error_message', 'error_class', 'error_backtrace',
'exception.backtrace', 'exception.class', 'exception.message'
).merge(
'message' => 'TestWorker JID-da883554ee4fe414012f5f42: start',
@@ -73,9 +69,6 @@ RSpec.shared_context 'structured_logger' do
end_payload.merge(
'message' => 'TestWorker JID-da883554ee4fe414012f5f42: fail: 0.0 sec',
'job_status' => 'fail',
- 'error_class' => 'ArgumentError',
- 'error_message' => 'Something went wrong',
- 'error_backtrace' => be_a(Array).and(be_present),
'exception.class' => 'ArgumentError',
'exception.message' => 'Something went wrong',
'exception.backtrace' => be_a(Array).and(be_present)