Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
74c7e01089
commit
e3624c5be1
39 changed files with 496 additions and 111 deletions
|
@ -137,7 +137,7 @@ export default {
|
|||
<gl-form-input
|
||||
data-qa-selector="githubish_import_filter_field"
|
||||
name="filter"
|
||||
:placeholder="__('Filter your repositories by name')"
|
||||
:placeholder="__('Filter by name')"
|
||||
autofocus
|
||||
size="lg"
|
||||
@keyup.enter="setFilter($event.target.value)"
|
||||
|
|
|
@ -352,6 +352,8 @@ export default {
|
|||
return Promise.resolve();
|
||||
},
|
||||
initPolling() {
|
||||
if (this.startingPollInterval <= 0) return;
|
||||
|
||||
this.pollingInterval = new SmartInterval({
|
||||
callback: this.checkStatus,
|
||||
startingInterval: this.startingPollInterval,
|
||||
|
@ -435,10 +437,10 @@ export default {
|
|||
notify.notifyMe(title, message, this.mr.gitlabLogo);
|
||||
},
|
||||
resumePolling() {
|
||||
this.pollingInterval.resume();
|
||||
this.pollingInterval?.resume();
|
||||
},
|
||||
stopPolling() {
|
||||
this.pollingInterval.stopTimer();
|
||||
this.pollingInterval?.stopTimer();
|
||||
},
|
||||
bindEventHubListeners() {
|
||||
eventHub.$on('MRWidgetUpdateRequested', (cb) => {
|
||||
|
|
31
app/graphql/mutations/saved_replies/base.rb
Normal file
31
app/graphql/mutations/saved_replies/base.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Mutations
|
||||
module SavedReplies
|
||||
class Base < BaseMutation
|
||||
field :saved_reply, Types::SavedReplyType,
|
||||
null: true,
|
||||
description: 'Updated saved reply.'
|
||||
|
||||
private
|
||||
|
||||
def present_result(result)
|
||||
if result.success?
|
||||
{
|
||||
saved_reply: result[:saved_reply],
|
||||
errors: []
|
||||
}
|
||||
else
|
||||
{
|
||||
saved_reply: nil,
|
||||
errors: result.message
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def feature_enabled?
|
||||
Feature.enabled?(:saved_replies, current_user, default_enabled: :yaml)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
26
app/graphql/mutations/saved_replies/create.rb
Normal file
26
app/graphql/mutations/saved_replies/create.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Mutations
|
||||
module SavedReplies
|
||||
class Create < Base
|
||||
graphql_name 'SavedReplyCreate'
|
||||
|
||||
authorize :create_saved_replies
|
||||
|
||||
argument :name, GraphQL::Types::String,
|
||||
required: true,
|
||||
description: copy_field_description(Types::SavedReplyType, :name)
|
||||
|
||||
argument :content, GraphQL::Types::String,
|
||||
required: true,
|
||||
description: copy_field_description(Types::SavedReplyType, :content)
|
||||
|
||||
def resolve(name:, content:)
|
||||
raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature disabled' unless feature_enabled?
|
||||
|
||||
result = ::Users::SavedReplies::CreateService.new(current_user: current_user, name: name, content: content).execute
|
||||
present_result(result)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -129,6 +129,7 @@ module Types
|
|||
mount_mutation Mutations::WorkItems::CreateFromTask
|
||||
mount_mutation Mutations::WorkItems::Delete
|
||||
mount_mutation Mutations::WorkItems::Update
|
||||
mount_mutation Mutations::SavedReplies::Create
|
||||
end
|
||||
end
|
||||
|
||||
|
|
21
app/graphql/types/saved_reply_type.rb
Normal file
21
app/graphql/types/saved_reply_type.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Types
|
||||
class SavedReplyType < BaseObject
|
||||
graphql_name 'SavedReply'
|
||||
|
||||
authorize :read_saved_replies
|
||||
|
||||
field :id, Types::GlobalIDType[::Users::SavedReply],
|
||||
null: false,
|
||||
description: 'Global ID of the saved reply.'
|
||||
|
||||
field :content, GraphQL::Types::String,
|
||||
null: false,
|
||||
description: 'Content of the saved reply.'
|
||||
|
||||
field :name, GraphQL::Types::String,
|
||||
null: false,
|
||||
description: 'Name of the saved reply.'
|
||||
end
|
||||
end
|
|
@ -115,6 +115,10 @@ module Types
|
|||
extras: [:lookahead],
|
||||
complexity: 5,
|
||||
resolver: ::Resolvers::TimelogResolver
|
||||
field :saved_replies,
|
||||
Types::SavedReplyType.connection_type,
|
||||
null: true,
|
||||
description: 'Saved replies authored by the user.'
|
||||
|
||||
field :gitpod_enabled, GraphQL::Types::Boolean, null: true,
|
||||
description: 'Whether Gitpod is enabled at the user level.'
|
||||
|
|
|
@ -23,9 +23,11 @@ class UserPolicy < BasePolicy
|
|||
enable :destroy_user
|
||||
enable :update_user
|
||||
enable :update_user_status
|
||||
enable :create_saved_replies
|
||||
enable :read_user_personal_access_tokens
|
||||
enable :read_group_count
|
||||
enable :read_user_groups
|
||||
enable :read_saved_replies
|
||||
end
|
||||
|
||||
rule { default }.enable :read_user_profile
|
||||
|
|
7
app/policies/users/saved_reply_policy.rb
Normal file
7
app/policies/users/saved_reply_policy.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Users
|
||||
class SavedReplyPolicy < BasePolicy
|
||||
delegate { @subject.user }
|
||||
end
|
||||
end
|
|
@ -19,6 +19,14 @@ class UserPresenter < Gitlab::View::Presenter::Delegated
|
|||
profile_path(user: { gitpod_enabled: true }) if application_gitpod_enabled?
|
||||
end
|
||||
|
||||
delegator_override :saved_replies
|
||||
def saved_replies
|
||||
return ::Users::SavedReply.none unless Feature.enabled?(:saved_replies, current_user, default_enabled: :yaml)
|
||||
return ::Users::SavedReply.none unless current_user.can?(:read_saved_replies, user)
|
||||
|
||||
user.saved_replies
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def can?(*args)
|
||||
|
|
29
app/services/users/saved_replies/create_service.rb
Normal file
29
app/services/users/saved_replies/create_service.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Users
|
||||
module SavedReplies
|
||||
class CreateService
|
||||
def initialize(current_user:, name:, content:)
|
||||
@current_user = current_user
|
||||
@name = name
|
||||
@content = content
|
||||
end
|
||||
|
||||
def execute
|
||||
saved_reply = saved_replies.build(name: name, content: content)
|
||||
|
||||
if saved_reply.save
|
||||
ServiceResponse.success(payload: { saved_reply: saved_reply })
|
||||
else
|
||||
ServiceResponse.error(message: saved_reply.errors.full_messages)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :current_user, :name, :content
|
||||
|
||||
delegate :saved_replies, to: :current_user
|
||||
end
|
||||
end
|
||||
end
|
8
config/feature_flags/development/saved_replies.yml
Normal file
8
config/feature_flags/development/saved_replies.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: saved_replies
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80811
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/352956
|
||||
milestone: '14.9'
|
||||
type: development
|
||||
group: group::project management
|
||||
default_enabled: false
|
|
@ -1043,6 +1043,25 @@ To resolve this issue:
|
|||
the **primary** node using IPv4 in the `/etc/hosts` file. Alternatively, you should
|
||||
[enable IPv6 on the **primary** node](https://docs.gitlab.com/omnibus/settings/nginx.html#setting-the-nginx-listen-address-or-addresses).
|
||||
|
||||
### Secondary site returns 502 errors with Geo proxying
|
||||
|
||||
When [Geo proxying for secondary sites](../secondary_proxy/index.md) is enabled, and the secondary site user interface returns
|
||||
502 errors, it is possible that the response header proxied from the primary site is too large.
|
||||
|
||||
Check the NGINX logs for errors similar to this example:
|
||||
|
||||
```plaintext
|
||||
2022/01/26 00:02:13 [error] 26641#0: *829148 upstream sent too big header while reading response header from upstream, client: 1.2.3.4, server: geo.staging.gitlab.com, request: "POST /users/sign_in HTTP/2.0", upstream: "http://unix:/var/opt/gitlab/gitlab-workhorse/sockets/socket:/users/sign_in", host: "geo.staging.gitlab.com", referrer: "https://geo.staging.gitlab.com/users/sign_in"
|
||||
```
|
||||
|
||||
To resolve this issue:
|
||||
|
||||
1. Set `nginx['proxy_custom_buffer_size'] = '8k'` in `/etc/gitlab.rb` on all web nodes on the secondary site.
|
||||
1. Reconfigure the **secondary** using `sudo gitlab-ctl reconfigure`.
|
||||
|
||||
If you still get this error, you can further increase the buffer size by repeating the steps above
|
||||
and changing the `8k` size, for example by doubling it to `16k`.
|
||||
|
||||
### Geo Admin Area shows 'Unknown' for health status and 'Request failed with status code 401'
|
||||
|
||||
If using a load balancer, ensure that the load balancer's URL is set as the `external_url` in the
|
||||
|
|
|
@ -6,6 +6,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Broadcast Messages API **(FREE SELF)**
|
||||
|
||||
> 'target_access_levels' [introduced](https://gitlab.com/gitlab-org/growth/team-tasks/-/issues/461) in GitLab 14.8 [with a flag](../administration/feature_flags.md) named `role_targeted_broadcast_messages`. Disabled by default.
|
||||
|
||||
Broadcast messages API operates on [broadcast messages](../user/admin_area/broadcast_messages.md).
|
||||
|
||||
As of GitLab 12.8, GET requests do not require authentication. All other broadcast message API endpoints are accessible only to administrators. Non-GET requests by:
|
||||
|
@ -39,6 +41,7 @@ Example response:
|
|||
"font":"#FFFFFF",
|
||||
"id":1,
|
||||
"active": false,
|
||||
"target_access_levels": [10,30],
|
||||
"target_path": "*/welcome",
|
||||
"broadcast_type": "banner",
|
||||
"dismissable": false
|
||||
|
@ -77,6 +80,7 @@ Example response:
|
|||
"font":"#FFFFFF",
|
||||
"id":1,
|
||||
"active":false,
|
||||
"target_access_levels": [10,30],
|
||||
"target_path": "*/welcome",
|
||||
"broadcast_type": "banner",
|
||||
"dismissable": false
|
||||
|
@ -93,21 +97,31 @@ POST /broadcast_messages
|
|||
|
||||
Parameters:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|:----------------|:---------|:---------|:------------------------------------------------------|
|
||||
| `message` | string | yes | Message to display. |
|
||||
| `starts_at` | datetime | no | Starting time (defaults to current time). Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
|
||||
| `ends_at` | datetime | no | Ending time (defaults to one hour from current time). Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
|
||||
| `color` | string | no | Background color hex code. |
|
||||
| `font` | string | no | Foreground color hex code. |
|
||||
| `target_path` | string | no | Target path of the broadcast message. |
|
||||
| `broadcast_type`| string | no | Appearance type (defaults to banner) |
|
||||
| `dismissable` | boolean | no | Can the user dismiss the message? |
|
||||
| Attribute | Type | Required | Description |
|
||||
|:-----------------------|:------------------|:---------|:------------------------------------------------------|
|
||||
| `message` | string | yes | Message to display. |
|
||||
| `starts_at` | datetime | no | Starting time (defaults to current time). Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
|
||||
| `ends_at` | datetime | no | Ending time (defaults to one hour from current time). Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
|
||||
| `color` | string | no | Background color hex code. |
|
||||
| `font` | string | no | Foreground color hex code. |
|
||||
| `target_access_levels` | array of integers | no | Target access levels (roles) of the broadcast message.|
|
||||
| `target_path` | string | no | Target path of the broadcast message. |
|
||||
| `broadcast_type` | string | no | Appearance type (defaults to banner) |
|
||||
| `dismissable` | boolean | no | Can the user dismiss the message? |
|
||||
|
||||
The `target_access_levels` are defined in the `Gitlab::Access` module. The
|
||||
following levels are valid:
|
||||
|
||||
- Guest (`10`)
|
||||
- Reporter (`20`)
|
||||
- Developer (`30`)
|
||||
- Maintainer (`40`)
|
||||
- Owner (`50`)
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --data "message=Deploy in progress&color=#cecece" \
|
||||
curl --data "message=Deploy in progress&color=#cecece&target_access_levels[]=10&target_access_levels[]=30" \
|
||||
--header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
"https://gitlab.example.com/api/v4/broadcast_messages"
|
||||
```
|
||||
|
@ -123,6 +137,7 @@ Example response:
|
|||
"font":"#FFFFFF",
|
||||
"id":1,
|
||||
"active": true,
|
||||
"target_access_levels": [10,30],
|
||||
"target_path": "*/welcome",
|
||||
"broadcast_type": "notification",
|
||||
"dismissable": false
|
||||
|
@ -139,17 +154,27 @@ PUT /broadcast_messages/:id
|
|||
|
||||
Parameters:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|:----------------|:---------|:---------|:--------------------------------------|
|
||||
| `id` | integer | yes | ID of broadcast message to update. |
|
||||
| `message` | string | no | Message to display. |
|
||||
| `starts_at` | datetime | no | Starting time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
|
||||
| `ends_at` | datetime | no | Ending time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
|
||||
| `color` | string | no | Background color hex code. |
|
||||
| `font` | string | no | Foreground color hex code. |
|
||||
| `target_path` | string | no | Target path of the broadcast message. |
|
||||
| `broadcast_type`| string | no | Appearance type (defaults to banner) |
|
||||
| `dismissable` | boolean | no | Can the user dismiss the message? |
|
||||
| Attribute | Type | Required | Description |
|
||||
|:-----------------------|:------------------|:---------|:------------------------------------------------------|
|
||||
| `id` | integer | yes | ID of broadcast message to update. |
|
||||
| `message` | string | no | Message to display. |
|
||||
| `starts_at` | datetime | no | Starting time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
|
||||
| `ends_at` | datetime | no | Ending time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
|
||||
| `color` | string | no | Background color hex code. |
|
||||
| `font` | string | no | Foreground color hex code. |
|
||||
| `target_access_levels` | array of integers | no | Target access levels (roles) of the broadcast message.|
|
||||
| `target_path` | string | no | Target path of the broadcast message. |
|
||||
| `broadcast_type` | string | no | Appearance type (defaults to banner) |
|
||||
| `dismissable` | boolean | no | Can the user dismiss the message? |
|
||||
|
||||
The `target_access_levels` are defined in the `Gitlab::Access` module. The
|
||||
following levels are valid:
|
||||
|
||||
- Guest (`10`)
|
||||
- Reporter (`20`)
|
||||
- Developer (`30`)
|
||||
- Maintainer (`40`)
|
||||
- Owner (`50`)
|
||||
|
||||
Example request:
|
||||
|
||||
|
@ -169,6 +194,7 @@ Example response:
|
|||
"font":"#FFFFFF",
|
||||
"id":1,
|
||||
"active": true,
|
||||
"target_access_levels": [10,30],
|
||||
"target_path": "*/welcome",
|
||||
"broadcast_type": "notification",
|
||||
"dismissable": false
|
||||
|
|
|
@ -4239,6 +4239,26 @@ Input type: `RunnersRegistrationTokenResetInput`
|
|||
| <a id="mutationrunnersregistrationtokenreseterrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
|
||||
| <a id="mutationrunnersregistrationtokenresettoken"></a>`token` | [`String`](#string) | Runner token after mutation. |
|
||||
|
||||
### `Mutation.savedReplyCreate`
|
||||
|
||||
Input type: `SavedReplyCreateInput`
|
||||
|
||||
#### Arguments
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationsavedreplycreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationsavedreplycreatecontent"></a>`content` | [`String!`](#string) | Content of the saved reply. |
|
||||
| <a id="mutationsavedreplycreatename"></a>`name` | [`String!`](#string) | Name of the saved reply. |
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationsavedreplycreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationsavedreplycreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
|
||||
| <a id="mutationsavedreplycreatesavedreply"></a>`savedReply` | [`SavedReply`](#savedreply) | Updated saved reply. |
|
||||
|
||||
### `Mutation.scanExecutionPolicyCommit`
|
||||
|
||||
Commits the `policy_yaml` content to the assigned security policy project for the given project(`project_path`).
|
||||
|
@ -7782,6 +7802,29 @@ The edge type for [`SastCiConfigurationOptionsEntity`](#sastciconfigurationoptio
|
|||
| <a id="sastciconfigurationoptionsentityedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
|
||||
| <a id="sastciconfigurationoptionsentityedgenode"></a>`node` | [`SastCiConfigurationOptionsEntity`](#sastciconfigurationoptionsentity) | The item at the end of the edge. |
|
||||
|
||||
#### `SavedReplyConnection`
|
||||
|
||||
The connection type for [`SavedReply`](#savedreply).
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="savedreplyconnectionedges"></a>`edges` | [`[SavedReplyEdge]`](#savedreplyedge) | A list of edges. |
|
||||
| <a id="savedreplyconnectionnodes"></a>`nodes` | [`[SavedReply]`](#savedreply) | A list of nodes. |
|
||||
| <a id="savedreplyconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
|
||||
|
||||
#### `SavedReplyEdge`
|
||||
|
||||
The edge type for [`SavedReply`](#savedreply).
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="savedreplyedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
|
||||
| <a id="savedreplyedgenode"></a>`node` | [`SavedReply`](#savedreply) | The item at the end of the edge. |
|
||||
|
||||
#### `ScanConnection`
|
||||
|
||||
The connection type for [`Scan`](#scan).
|
||||
|
@ -12496,6 +12539,7 @@ A user assigned to a merge request.
|
|||
| <a id="mergerequestassigneeprofileenablegitpodpath"></a>`profileEnableGitpodPath` | [`String`](#string) | Web path to enable Gitpod for the user. |
|
||||
| <a id="mergerequestassigneeprojectmemberships"></a>`projectMemberships` | [`ProjectMemberConnection`](#projectmemberconnection) | Project memberships of the user. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestassigneepublicemail"></a>`publicEmail` | [`String`](#string) | User's public email. |
|
||||
| <a id="mergerequestassigneesavedreplies"></a>`savedReplies` | [`SavedReplyConnection`](#savedreplyconnection) | Saved replies authored by the user. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestassigneestate"></a>`state` | [`UserState!`](#userstate) | State of the user. |
|
||||
| <a id="mergerequestassigneestatus"></a>`status` | [`UserStatus`](#userstatus) | User status. |
|
||||
| <a id="mergerequestassigneeuserpermissions"></a>`userPermissions` | [`UserPermissions!`](#userpermissions) | Permissions for the current user on the resource. |
|
||||
|
@ -12760,6 +12804,7 @@ A user assigned to a merge request as a reviewer.
|
|||
| <a id="mergerequestreviewerprofileenablegitpodpath"></a>`profileEnableGitpodPath` | [`String`](#string) | Web path to enable Gitpod for the user. |
|
||||
| <a id="mergerequestreviewerprojectmemberships"></a>`projectMemberships` | [`ProjectMemberConnection`](#projectmemberconnection) | Project memberships of the user. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestreviewerpublicemail"></a>`publicEmail` | [`String`](#string) | User's public email. |
|
||||
| <a id="mergerequestreviewersavedreplies"></a>`savedReplies` | [`SavedReplyConnection`](#savedreplyconnection) | Saved replies authored by the user. (see [Connections](#connections)) |
|
||||
| <a id="mergerequestreviewerstate"></a>`state` | [`UserState!`](#userstate) | State of the user. |
|
||||
| <a id="mergerequestreviewerstatus"></a>`status` | [`UserStatus`](#userstatus) | User status. |
|
||||
| <a id="mergerequestrevieweruserpermissions"></a>`userPermissions` | [`UserPermissions!`](#userpermissions) | Permissions for the current user on the resource. |
|
||||
|
@ -15338,6 +15383,16 @@ Represents an entity for options in SAST CI configuration.
|
|||
| <a id="sastciconfigurationoptionsentitylabel"></a>`label` | [`String`](#string) | Label of option entity. |
|
||||
| <a id="sastciconfigurationoptionsentityvalue"></a>`value` | [`String`](#string) | Value of option entity. |
|
||||
|
||||
### `SavedReply`
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="savedreplycontent"></a>`content` | [`String!`](#string) | Content of the saved reply. |
|
||||
| <a id="savedreplyid"></a>`id` | [`UsersSavedReplyID!`](#userssavedreplyid) | Global ID of the saved reply. |
|
||||
| <a id="savedreplyname"></a>`name` | [`String!`](#string) | Name of the saved reply. |
|
||||
|
||||
### `Scan`
|
||||
|
||||
Represents the security scan information.
|
||||
|
@ -16116,6 +16171,7 @@ Core represention of a GitLab user.
|
|||
| <a id="usercoreprofileenablegitpodpath"></a>`profileEnableGitpodPath` | [`String`](#string) | Web path to enable Gitpod for the user. |
|
||||
| <a id="usercoreprojectmemberships"></a>`projectMemberships` | [`ProjectMemberConnection`](#projectmemberconnection) | Project memberships of the user. (see [Connections](#connections)) |
|
||||
| <a id="usercorepublicemail"></a>`publicEmail` | [`String`](#string) | User's public email. |
|
||||
| <a id="usercoresavedreplies"></a>`savedReplies` | [`SavedReplyConnection`](#savedreplyconnection) | Saved replies authored by the user. (see [Connections](#connections)) |
|
||||
| <a id="usercorestate"></a>`state` | [`UserState!`](#userstate) | State of the user. |
|
||||
| <a id="usercorestatus"></a>`status` | [`UserStatus`](#userstatus) | User status. |
|
||||
| <a id="usercoreuserpermissions"></a>`userPermissions` | [`UserPermissions!`](#userpermissions) | Permissions for the current user on the resource. |
|
||||
|
@ -19189,6 +19245,12 @@ A `UserID` is a global ID. It is encoded as a string.
|
|||
|
||||
An example `UserID` is: `"gid://gitlab/User/1"`.
|
||||
|
||||
### `UsersSavedReplyID`
|
||||
|
||||
A `UsersSavedReplyID` is a global ID. It is encoded as a string.
|
||||
|
||||
An example `UsersSavedReplyID` is: `"gid://gitlab/Users::SavedReply/1"`.
|
||||
|
||||
### `VulnerabilitiesExternalIssueLinkID`
|
||||
|
||||
A `VulnerabilitiesExternalIssueLinkID` is a global ID. It is encoded as a string.
|
||||
|
@ -19619,6 +19681,7 @@ Implementations:
|
|||
| <a id="userprofileenablegitpodpath"></a>`profileEnableGitpodPath` | [`String`](#string) | Web path to enable Gitpod for the user. |
|
||||
| <a id="userprojectmemberships"></a>`projectMemberships` | [`ProjectMemberConnection`](#projectmemberconnection) | Project memberships of the user. (see [Connections](#connections)) |
|
||||
| <a id="userpublicemail"></a>`publicEmail` | [`String`](#string) | User's public email. |
|
||||
| <a id="usersavedreplies"></a>`savedReplies` | [`SavedReplyConnection`](#savedreplyconnection) | Saved replies authored by the user. (see [Connections](#connections)) |
|
||||
| <a id="userstate"></a>`state` | [`UserState!`](#userstate) | State of the user. |
|
||||
| <a id="userstatus"></a>`status` | [`UserStatus`](#userstatus) | User status. |
|
||||
| <a id="useruserpermissions"></a>`userPermissions` | [`UserPermissions!`](#userpermissions) | Permissions for the current user on the resource. |
|
||||
|
|
|
@ -7,7 +7,9 @@ type: reference, howto
|
|||
|
||||
# Broadcast messages **(FREE SELF)**
|
||||
|
||||
GitLab can display broadcast messages to all users of a GitLab instance. There are two types of broadcast messages:
|
||||
> Target roles [introduced](https://gitlab.com/gitlab-org/growth/team-tasks/-/issues/461) in GitLab 14.8 [with a flag](../../administration/feature_flags.md) named `role_targeted_broadcast_messages`. Disabled by default.
|
||||
|
||||
GitLab can display broadcast messages to users of a GitLab instance. There are two types of broadcast messages:
|
||||
|
||||
- Banners
|
||||
- Notifications
|
||||
|
@ -66,6 +68,7 @@ To add a broadcast message:
|
|||
- `text-decoration`
|
||||
1. Select one of the suggested background colors, or add the hex code of a different color. The default color is orange.
|
||||
1. Select the **Dismissable** checkbox to enable users to dismiss the broadcast message.
|
||||
1. Optional. Select **Target roles** to only show the broadcast message to users with the selected roles. The message displays on group, subgroup, and project pages, and does not display in Git remote responses.
|
||||
1. If required, add a **Target Path** to only show the broadcast message on URLs matching that path. You can use the wildcard character `*` to match multiple URLs, for example `mygroup/myproject*`.
|
||||
1. Select a date for the message to start and end.
|
||||
1. Select **Add broadcast message**.
|
||||
|
|
|
@ -36,6 +36,8 @@ module API
|
|||
optional :ends_at, type: DateTime, desc: 'Ending time', default: -> { 1.hour.from_now }
|
||||
optional :color, type: String, desc: 'Background color'
|
||||
optional :font, type: String, desc: 'Foreground color'
|
||||
optional :target_access_levels, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce,
|
||||
values: BroadcastMessage::ALLOWED_TARGET_ACCESS_LEVELS, desc: 'Target user roles'
|
||||
optional :target_path, type: String, desc: 'Target path'
|
||||
optional :broadcast_type, type: String, values: BroadcastMessage.broadcast_types.keys, desc: 'Broadcast type. Defaults to banner', default: -> { 'banner' }
|
||||
optional :dismissable, type: Boolean, desc: 'Is dismissable'
|
||||
|
@ -76,6 +78,8 @@ module API
|
|||
optional :ends_at, type: DateTime, desc: 'Ending time'
|
||||
optional :color, type: String, desc: 'Background color'
|
||||
optional :font, type: String, desc: 'Foreground color'
|
||||
optional :target_access_levels, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce,
|
||||
values: BroadcastMessage::ALLOWED_TARGET_ACCESS_LEVELS, desc: 'Target user roles'
|
||||
optional :target_path, type: String, desc: 'Target path'
|
||||
optional :broadcast_type, type: String, values: BroadcastMessage.broadcast_types.keys, desc: 'Broadcast Type'
|
||||
optional :dismissable, type: Boolean, desc: 'Is dismissable'
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module API
|
||||
module Entities
|
||||
class BroadcastMessage < Grape::Entity
|
||||
expose :id, :message, :starts_at, :ends_at, :color, :font, :target_path, :broadcast_type, :dismissable
|
||||
expose :id, :message, :starts_at, :ends_at, :color, :font, :target_access_levels, :target_path, :broadcast_type, :dismissable
|
||||
expose :active?, as: :active
|
||||
end
|
||||
end
|
||||
|
|
|
@ -92,7 +92,8 @@ module Gitlab
|
|||
batched_migration.table_name,
|
||||
batched_migration.column_name,
|
||||
batch_min_value: min_value,
|
||||
batch_size: new_batch_size
|
||||
batch_size: new_batch_size,
|
||||
job_arguments: batched_migration.job_arguments
|
||||
)
|
||||
midpoint = next_batch_bounds.last
|
||||
|
||||
|
|
|
@ -7,18 +7,26 @@ module Gitlab
|
|||
|
||||
# SIDEKIQ_LATENCY_BUCKETS are latency histogram buckets better suited to Sidekiq
|
||||
# timeframes than the DEFAULT_BUCKET definition. Defined in seconds.
|
||||
SIDEKIQ_LATENCY_BUCKETS = [0.1, 0.25, 0.5, 1, 2.5, 5, 10, 60, 300, 600].freeze
|
||||
# This information is better viewed in logs, but these buckets cover
|
||||
# most of the durations for cpu, gitaly, db and elasticsearch
|
||||
SIDEKIQ_LATENCY_BUCKETS = [0.1, 0.5, 1, 2.5].freeze
|
||||
|
||||
# These are the buckets we currently use for alerting, we will likely
|
||||
# replace these histograms with Application SLIs
|
||||
# https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1313
|
||||
SIDEKIQ_JOB_DURATION_BUCKETS = [10, 300].freeze
|
||||
SIDEKIQ_QUEUE_DURATION_BUCKETS = [10, 60].freeze
|
||||
|
||||
class << self
|
||||
include ::Gitlab::SidekiqMiddleware::MetricsHelper
|
||||
|
||||
def metrics
|
||||
{
|
||||
sidekiq_jobs_cpu_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_cpu_seconds, 'Seconds of cpu time to run Sidekiq job', {}, SIDEKIQ_LATENCY_BUCKETS),
|
||||
sidekiq_jobs_completion_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_completion_seconds, 'Seconds to complete Sidekiq job', {}, SIDEKIQ_LATENCY_BUCKETS),
|
||||
sidekiq_jobs_cpu_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_cpu_seconds, 'Seconds this Sidekiq job spent on the CPU', {}, SIDEKIQ_LATENCY_BUCKETS),
|
||||
sidekiq_jobs_completion_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_completion_seconds, 'Seconds to complete Sidekiq job', {}, SIDEKIQ_JOB_DURATION_BUCKETS),
|
||||
sidekiq_jobs_db_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_db_seconds, 'Seconds of database time to run Sidekiq job', {}, SIDEKIQ_LATENCY_BUCKETS),
|
||||
sidekiq_jobs_gitaly_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_gitaly_seconds, 'Seconds of Gitaly time to run Sidekiq job', {}, SIDEKIQ_LATENCY_BUCKETS),
|
||||
sidekiq_jobs_queue_duration_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_queue_duration_seconds, 'Duration in seconds that a Sidekiq job was queued before being executed', {}, SIDEKIQ_LATENCY_BUCKETS),
|
||||
sidekiq_jobs_queue_duration_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_queue_duration_seconds, 'Duration in seconds that a Sidekiq job was queued before being executed', {}, SIDEKIQ_QUEUE_DURATION_BUCKETS),
|
||||
sidekiq_redis_requests_duration_seconds: ::Gitlab::Metrics.histogram(:sidekiq_redis_requests_duration_seconds, 'Duration in seconds that a Sidekiq job spent requests a Redis server', {}, Gitlab::Instrumentation::Redis::QUERY_TIME_BUCKETS),
|
||||
sidekiq_elasticsearch_requests_duration_seconds: ::Gitlab::Metrics.histogram(:sidekiq_elasticsearch_requests_duration_seconds, 'Duration in seconds that a Sidekiq job spent in requests to an Elasticsearch server', {}, SIDEKIQ_LATENCY_BUCKETS),
|
||||
sidekiq_jobs_failed_total: ::Gitlab::Metrics.counter(:sidekiq_jobs_failed_total, 'Sidekiq jobs failed'),
|
||||
|
|
|
@ -9269,6 +9269,9 @@ msgstr ""
|
|||
msgid "Configure which lists are shown for anyone who visits this board"
|
||||
msgstr ""
|
||||
|
||||
msgid "Configure with a merge request"
|
||||
msgstr ""
|
||||
|
||||
msgid "Configure your environments to be deployed to specific geographical regions"
|
||||
msgstr ""
|
||||
|
||||
|
@ -10352,9 +10355,6 @@ msgstr ""
|
|||
msgid "Create user"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create via merge request"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create wildcard: %{searchTerm}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -15646,9 +15646,6 @@ msgstr ""
|
|||
msgid "Filter users"
|
||||
msgstr ""
|
||||
|
||||
msgid "Filter your repositories by name"
|
||||
msgstr ""
|
||||
|
||||
msgid "Filter..."
|
||||
msgstr ""
|
||||
|
||||
|
@ -32758,6 +32755,9 @@ msgstr ""
|
|||
msgid "SecurityOrchestration|Update scan policies"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|View policy project"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|a"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
"@gitlab/at.js": "1.5.7",
|
||||
"@gitlab/favicon-overlay": "2.0.0",
|
||||
"@gitlab/svgs": "2.6.0",
|
||||
"@gitlab/ui": "36.7.1",
|
||||
"@gitlab/ui": "37.3.0",
|
||||
"@gitlab/visual-review-tools": "1.6.1",
|
||||
"@rails/actioncable": "6.1.4-6",
|
||||
"@rails/ujs": "6.1.4-6",
|
||||
|
|
|
@ -56,7 +56,13 @@ RSpec.describe "Admin > Admin sees background migrations" do
|
|||
context 'when there are failed migrations' do
|
||||
before do
|
||||
allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class|
|
||||
allow(batch_class).to receive(:next_batch).with(anything, anything, batch_min_value: 6, batch_size: 5).and_return([6, 10])
|
||||
allow(batch_class).to receive(:next_batch).with(
|
||||
anything,
|
||||
anything,
|
||||
batch_min_value: 6,
|
||||
batch_size: 5,
|
||||
job_arguments: failed_migration.job_arguments
|
||||
).and_return([6, 10])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -398,21 +398,6 @@ describe('ErrorTrackingList', () => {
|
|||
});
|
||||
|
||||
describe('When pagination is required', () => {
|
||||
describe('and the user is on the first page', () => {
|
||||
beforeEach(() => {
|
||||
store.state.list.loading = false;
|
||||
mountComponent({
|
||||
stubs: {
|
||||
GlPagination: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('shows a disabled Prev button', () => {
|
||||
expect(wrapper.find('.prev-page-item').attributes('aria-disabled')).toBe('true');
|
||||
});
|
||||
});
|
||||
|
||||
describe('and the user is not on the first page', () => {
|
||||
describe('and the previous button is clicked', () => {
|
||||
beforeEach(async () => {
|
||||
|
|
|
@ -16,7 +16,7 @@ describe('ImportProjectsTable', () => {
|
|||
const findFilterField = () =>
|
||||
wrapper
|
||||
.findAllComponents(GlFormInput)
|
||||
.wrappers.find((w) => w.attributes('placeholder') === 'Filter your repositories by name');
|
||||
.wrappers.find((w) => w.attributes('placeholder') === 'Filter by name');
|
||||
|
||||
const providerTitle = 'THE PROVIDER';
|
||||
const providerRepo = {
|
||||
|
|
|
@ -65,8 +65,6 @@ describe('AdminRunnersApp', () => {
|
|||
const findRunnerTypeTabs = () => wrapper.findComponent(RunnerTypeTabs);
|
||||
const findRunnerList = () => wrapper.findComponent(RunnerList);
|
||||
const findRunnerPagination = () => extendedWrapper(wrapper.findComponent(RunnerPagination));
|
||||
const findRunnerPaginationPrev = () =>
|
||||
findRunnerPagination().findByLabelText('Go to previous page');
|
||||
const findRunnerPaginationNext = () => findRunnerPagination().findByLabelText('Go to next page');
|
||||
const findRunnerFilteredSearchBar = () => wrapper.findComponent(RunnerFilteredSearchBar);
|
||||
const findFilteredSearch = () => wrapper.findComponent(FilteredSearch);
|
||||
|
@ -370,14 +368,6 @@ describe('AdminRunnersApp', () => {
|
|||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('more pages can be selected', () => {
|
||||
expect(findRunnerPagination().text()).toMatchInterpolatedText('Previous Next');
|
||||
});
|
||||
|
||||
it('cannot navigate to the previous page', () => {
|
||||
expect(findRunnerPaginationPrev().attributes('aria-disabled')).toBe('true');
|
||||
});
|
||||
|
||||
it('navigates to the next page', async () => {
|
||||
await findRunnerPaginationNext().trigger('click');
|
||||
|
||||
|
|
|
@ -45,14 +45,6 @@ describe('RunnerPagination', () => {
|
|||
expect(findPagination().props('nextPage')).toBe(2);
|
||||
});
|
||||
|
||||
it('Shows prev page disabled', () => {
|
||||
expect(findPagination().find('[aria-disabled]').text()).toBe('Previous');
|
||||
});
|
||||
|
||||
it('Shows next page link', () => {
|
||||
expect(findPagination().find('a').text()).toBe('Next');
|
||||
});
|
||||
|
||||
it('Goes to the second page', () => {
|
||||
findPagination().vm.$emit('input', 2);
|
||||
|
||||
|
@ -124,14 +116,6 @@ describe('RunnerPagination', () => {
|
|||
expect(findPagination().props('prevPage')).toBe(2);
|
||||
expect(findPagination().props('nextPage')).toBe(null);
|
||||
});
|
||||
|
||||
it('Shows previous page link', () => {
|
||||
expect(findPagination().find('a').text()).toBe('Previous');
|
||||
});
|
||||
|
||||
it('Shows next page disabled', () => {
|
||||
expect(findPagination().find('[aria-disabled]').text()).toBe('Next');
|
||||
});
|
||||
});
|
||||
|
||||
describe('When only one page', () => {
|
||||
|
|
|
@ -67,8 +67,6 @@ describe('GroupRunnersApp', () => {
|
|||
const findRunnerList = () => wrapper.findComponent(RunnerList);
|
||||
const findRunnerRow = (id) => extendedWrapper(wrapper.findByTestId(`runner-row-${id}`));
|
||||
const findRunnerPagination = () => extendedWrapper(wrapper.findComponent(RunnerPagination));
|
||||
const findRunnerPaginationPrev = () =>
|
||||
findRunnerPagination().findByLabelText('Go to previous page');
|
||||
const findRunnerPaginationNext = () => findRunnerPagination().findByLabelText('Go to next page');
|
||||
const findRunnerFilteredSearchBar = () => wrapper.findComponent(RunnerFilteredSearchBar);
|
||||
const findFilteredSearch = () => wrapper.findComponent(FilteredSearch);
|
||||
|
@ -340,14 +338,6 @@ describe('GroupRunnersApp', () => {
|
|||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('more pages can be selected', () => {
|
||||
expect(findRunnerPagination().text()).toMatchInterpolatedText('Previous Next');
|
||||
});
|
||||
|
||||
it('cannot navigate to the previous page', () => {
|
||||
expect(findRunnerPaginationPrev().attributes('aria-disabled')).toBe('true');
|
||||
});
|
||||
|
||||
it('navigates to the next page', async () => {
|
||||
await findRunnerPaginationNext().trigger('click');
|
||||
|
||||
|
|
46
spec/graphql/mutations/saved_replies/create_spec.rb
Normal file
46
spec/graphql/mutations/saved_replies/create_spec.rb
Normal file
|
@ -0,0 +1,46 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Mutations::SavedReplies::Create do
|
||||
let_it_be(:current_user) { create(:user) }
|
||||
|
||||
let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
|
||||
|
||||
let(:mutation_arguments) { { name: 'save_reply_name', content: 'Save Reply Content' } }
|
||||
|
||||
describe '#resolve' do
|
||||
subject(:resolve) do
|
||||
mutation.resolve(**mutation_arguments)
|
||||
end
|
||||
|
||||
context 'when feature is disabled' do
|
||||
before do
|
||||
stub_feature_flags(saved_replies: false)
|
||||
end
|
||||
|
||||
it 'raises Gitlab::Graphql::Errors::ResourceNotAvailable' do
|
||||
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature disabled')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature is enabled for current user' do
|
||||
before do
|
||||
stub_feature_flags(saved_replies: current_user)
|
||||
end
|
||||
|
||||
context 'when service fails to create a new saved reply' do
|
||||
let(:mutation_arguments) { { name: '', content: '' } }
|
||||
|
||||
it { expect(subject[:saved_reply]).to be_nil }
|
||||
it { expect(subject[:errors]).to match_array(["Content can't be blank", "Name can't be blank", "Name can contain only lowercase letters, digits, '_' and '-'. Must start with a letter, and cannot end with '-' or '_'"]) }
|
||||
end
|
||||
|
||||
context 'when service successfully creates a new saved reply' do
|
||||
it { expect(subject[:saved_reply].name).to eq(mutation_arguments[:name]) }
|
||||
it { expect(subject[:saved_reply].content).to eq(mutation_arguments[:content]) }
|
||||
it { expect(subject[:errors]).to be_empty }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -37,6 +37,7 @@ RSpec.describe GitlabSchema.types['MergeRequestReviewer'] do
|
|||
gitpodEnabled
|
||||
preferencesGitpodPath
|
||||
profileEnableGitpodPath
|
||||
savedReplies
|
||||
]
|
||||
|
||||
expect(described_class).to have_graphql_fields(*expected_fields)
|
||||
|
|
13
spec/graphql/types/saved_reply_type_spec.rb
Normal file
13
spec/graphql/types/saved_reply_type_spec.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe GitlabSchema.types['SavedReply'] do
|
||||
specify { expect(described_class.graphql_name).to eq('SavedReply') }
|
||||
|
||||
it 'has all the required fields' do
|
||||
expect(described_class).to have_graphql_fields(:id, :content, :name)
|
||||
end
|
||||
|
||||
specify { expect(described_class).to require_graphql_authorizations(:read_saved_replies) }
|
||||
end
|
|
@ -42,6 +42,7 @@ RSpec.describe GitlabSchema.types['User'] do
|
|||
gitpodEnabled
|
||||
preferencesGitpodPath
|
||||
profileEnableGitpodPath
|
||||
savedReplies
|
||||
]
|
||||
|
||||
expect(described_class).to have_graphql_fields(*expected_fields)
|
||||
|
|
|
@ -199,15 +199,17 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
|
|||
end
|
||||
|
||||
describe '#split_and_retry!' do
|
||||
let!(:job) { create(:batched_background_migration_job, :failed, batch_size: 10, min_value: 6, max_value: 15, attempts: 3) }
|
||||
let_it_be(:migration) { create(:batched_background_migration, table_name: :events) }
|
||||
let_it_be(:job) { create(:batched_background_migration_job, :failed, batched_migration: migration, batch_size: 10, min_value: 6, max_value: 15, attempts: 3) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
||||
before_all do
|
||||
(6..16).each do |id|
|
||||
create(:event, id: id, project: project)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when job can be split' do
|
||||
before do
|
||||
allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class|
|
||||
allow(batch_class).to receive(:next_batch).with(anything, anything, batch_min_value: 6, batch_size: 5).and_return([6, 10])
|
||||
end
|
||||
end
|
||||
|
||||
it 'sets the correct attributes' do
|
||||
expect { job.split_and_retry! }.to change { described_class.count }.by(1)
|
||||
|
||||
|
@ -263,9 +265,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
|
|||
|
||||
context 'when computed midpoint is larger than the max value of the batch' do
|
||||
before do
|
||||
allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class|
|
||||
allow(batch_class).to receive(:next_batch).with(anything, anything, batch_min_value: 6, batch_size: 5).and_return([6, 16])
|
||||
end
|
||||
Event.where(id: 6..12).delete_all
|
||||
end
|
||||
|
||||
it 'lowers the batch size and resets the number of attempts' do
|
||||
|
|
|
@ -274,7 +274,13 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
|
|||
|
||||
before do
|
||||
allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class|
|
||||
allow(batch_class).to receive(:next_batch).with(anything, anything, batch_min_value: 6, batch_size: 5).and_return([6, 10])
|
||||
allow(batch_class).to receive(:next_batch).with(
|
||||
anything,
|
||||
anything,
|
||||
batch_min_value: 6,
|
||||
batch_size: 5,
|
||||
job_arguments: batched_migration.job_arguments
|
||||
).and_return([6, 10])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -5,7 +5,9 @@ require 'spec_helper'
|
|||
RSpec.describe UserPresenter do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
subject(:presenter) { described_class.new(user) }
|
||||
let(:current_user) { user }
|
||||
|
||||
subject(:presenter) { described_class.new(user, current_user: current_user) }
|
||||
|
||||
describe '#web_path' do
|
||||
it { expect(presenter.web_path).to eq("/#{user.username}") }
|
||||
|
@ -46,4 +48,33 @@ RSpec.describe UserPresenter do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#saved_replies' do
|
||||
let_it_be(:other_user) { create(:user) }
|
||||
let_it_be(:saved_reply) { create(:saved_reply, user: user) }
|
||||
|
||||
context 'when feature is disabled' do
|
||||
before do
|
||||
stub_feature_flags(saved_replies: false)
|
||||
end
|
||||
|
||||
it { expect(presenter.saved_replies).to eq(::Users::SavedReply.none) }
|
||||
end
|
||||
|
||||
context 'when feature is enabled' do
|
||||
before do
|
||||
stub_feature_flags(saved_replies: current_user)
|
||||
end
|
||||
|
||||
context 'when user has no permission to read saved replies' do
|
||||
let(:current_user) { other_user }
|
||||
|
||||
it { expect(presenter.saved_replies).to eq(::Users::SavedReply.none) }
|
||||
end
|
||||
|
||||
context 'when user has permission to read saved replies' do
|
||||
it { expect(presenter.saved_replies).to eq([saved_reply]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,7 +16,13 @@ RSpec.describe Admin::BackgroundMigrationsController, :enable_admin_mode do
|
|||
create(:batched_background_migration_job, :failed, batched_migration: migration, batch_size: 10, min_value: 6, max_value: 15, attempts: 3)
|
||||
|
||||
allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class|
|
||||
allow(batch_class).to receive(:next_batch).with(anything, anything, batch_min_value: 6, batch_size: 5).and_return([6, 10])
|
||||
allow(batch_class).to receive(:next_batch).with(
|
||||
anything,
|
||||
anything,
|
||||
batch_min_value: 6,
|
||||
batch_size: 5,
|
||||
job_arguments: migration.job_arguments
|
||||
).and_return([6, 10])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ RSpec.describe API::BroadcastMessages do
|
|||
expect(response).to include_pagination_headers
|
||||
expect(json_response).to be_kind_of(Array)
|
||||
expect(json_response.first.keys)
|
||||
.to match_array(%w(id message starts_at ends_at color font active target_path broadcast_type dismissable))
|
||||
.to match_array(%w(id message starts_at ends_at color font active target_access_levels target_path broadcast_type dismissable))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -28,7 +28,7 @@ RSpec.describe API::BroadcastMessages do
|
|||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response['id']).to eq message.id
|
||||
expect(json_response.keys)
|
||||
.to match_array(%w(id message starts_at ends_at color font active target_path broadcast_type dismissable))
|
||||
.to match_array(%w(id message starts_at ends_at color font active target_access_levels target_path broadcast_type dismissable))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -77,6 +77,16 @@ RSpec.describe API::BroadcastMessages do
|
|||
expect(json_response['font']).to eq attrs[:font]
|
||||
end
|
||||
|
||||
it 'accepts target access levels' do
|
||||
target_access_levels = [Gitlab::Access::GUEST, Gitlab::Access::DEVELOPER]
|
||||
attrs = attributes_for(:broadcast_message, target_access_levels: target_access_levels)
|
||||
|
||||
post api('/broadcast_messages', admin), params: attrs
|
||||
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(json_response['target_access_levels']).to eq attrs[:target_access_levels]
|
||||
end
|
||||
|
||||
it 'accepts a target path' do
|
||||
attrs = attributes_for(:broadcast_message, target_path: "*/welcome")
|
||||
|
||||
|
@ -171,6 +181,15 @@ RSpec.describe API::BroadcastMessages do
|
|||
expect { message.reload }.to change { message.message }.to('new message')
|
||||
end
|
||||
|
||||
it 'accepts a new target_access_levels' do
|
||||
attrs = { target_access_levels: [Gitlab::Access::MAINTAINER] }
|
||||
|
||||
put api("/broadcast_messages/#{message.id}", admin), params: attrs
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response['target_access_levels']).to eq attrs[:target_access_levels]
|
||||
end
|
||||
|
||||
it 'accepts a new target_path' do
|
||||
attrs = { target_path: '*/welcome' }
|
||||
|
||||
|
|
44
spec/services/users/saved_replies/create_service_spec.rb
Normal file
44
spec/services/users/saved_replies/create_service_spec.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Users::SavedReplies::CreateService do
|
||||
describe '#execute' do
|
||||
let_it_be(:current_user) { create(:user) }
|
||||
let_it_be(:saved_reply) { create(:saved_reply, user: current_user) }
|
||||
|
||||
subject { described_class.new(current_user: current_user, name: name, content: content).execute }
|
||||
|
||||
context 'when create fails' do
|
||||
let(:name) { saved_reply.name }
|
||||
let(:content) { '' }
|
||||
|
||||
it { is_expected.not_to be_success }
|
||||
|
||||
it 'does not create new Saved Reply in database' do
|
||||
expect { subject }.not_to change(::Users::SavedReply, :count)
|
||||
end
|
||||
|
||||
it 'returns error messages' do
|
||||
expect(subject.errors).to match_array(["Content can't be blank", "Name has already been taken"])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when create succeeds' do
|
||||
let(:name) { 'new_saved_reply_name' }
|
||||
let(:content) { 'New content for Saved Reply' }
|
||||
|
||||
it { is_expected.to be_success }
|
||||
|
||||
it 'creates new Saved Reply in database' do
|
||||
expect { subject }.to change(::Users::SavedReply, :count).by(1)
|
||||
end
|
||||
|
||||
it 'returns new saved reply', :aggregate_failures do
|
||||
expect(subject[:saved_reply]).to eq(::Users::SavedReply.last)
|
||||
expect(subject[:saved_reply].name).to eq(name)
|
||||
expect(subject[:saved_reply].content).to eq(content)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -986,10 +986,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-2.6.0.tgz#49f020b0a732f5df01138bd21b610a0a940badd6"
|
||||
integrity sha512-jI8CHlrriePtUsognRkpZQWVsZe7ZjytmKakeYyU1aKvsnJ4fAeySlVkCAiqKbbZm3T/eeH/6b3jxHn24U0k0Q==
|
||||
|
||||
"@gitlab/ui@36.7.1":
|
||||
version "36.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-36.7.1.tgz#49005f55e3fedc3c3ca054355d29c7ac5ade94b5"
|
||||
integrity sha512-MIohLWzZtKxUVwxB26em/R7JB8RB8a/BbTDZR/06xp8yMa4wpNtGTuTGajBKBnbltdKE1h1jVIOLF9Kkb3Njow==
|
||||
"@gitlab/ui@37.3.0":
|
||||
version "37.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-37.3.0.tgz#7362ed42964abd049959455101540b8b78b42f5f"
|
||||
integrity sha512-eCwXK9d+QdprgIftm/5OAZ/VjG+OdG23xpRlMeXllFigqEcELgnc742nmG7C3JG8Vlm84is7bliBbAQEWBTgbw==
|
||||
dependencies:
|
||||
"@babel/standalone" "^7.0.0"
|
||||
bootstrap-vue "2.20.1"
|
||||
|
|
Loading…
Reference in a new issue