Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-04-13 09:11:23 +00:00
parent 4a60d08bd5
commit 62798ed33c
35 changed files with 1049 additions and 553 deletions

View File

@ -1 +1 @@
81e439b712f147dc54bf0659857c4a038e9999b3
9255b4522513ee91d7fef535137d57deb498bbf8

View File

@ -1 +1 @@
4.2.0
4.3.0

View File

@ -544,6 +544,10 @@ gem 'lru_redux'
# Locked as long as quoted-printable encoding issues are not resolved
# Monkey-patched in `config/initializers/mail_encoding_patch.rb`
# See https://gitlab.com/gitlab-org/gitlab/issues/197386
#
# `config/initializers/mail_starttls_patch.rb` has also been patched to
# fix STARTTLS handling until https://github.com/mikel/mail/pull/1536 is
# released.
gem 'mail', '= 2.8.1'
gem 'mail-smtp_pool', '~> 0.1.0', path: 'vendor/gems/mail-smtp_pool', require: false

View File

@ -177,6 +177,7 @@ export default {
:has-project-filter="false"
:start-date="createdAfter"
:end-date="createdBefore"
:group-path="groupPath"
@setDateRange="onSetDateRange"
/>
<div class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row">

View File

@ -35,6 +35,10 @@ export default {
type: String,
required: true,
},
groupPath: {
type: String,
required: true,
},
startDate: {
type: Date,
required: false,
@ -79,7 +83,7 @@ export default {
<projects-dropdown-filter
v-if="hasProjectFilter"
class="js-projects-dropdown-filter project-select gl-mb-2 gl-lg-mb-0"
:group-namespace="namespacePath"
:group-namespace="groupPath"
:query-params="projectsQueryParams"
:multi-select="$options.multiProjectSelect"
:default-projects="selectedProjects"

View File

@ -59,6 +59,7 @@ export default () => {
showTimelineViewToggle,
reportAbusePath: notesDataset.reportAbusePath,
newCommentTemplatePath: notesDataset.newCommentTemplatePath,
issuableId: noteableData.id,
},
data() {
return {

View File

@ -28,6 +28,8 @@ export default {
GlTab,
DrawioToolbarButton,
CommentTemplatesDropdown,
AiActionsDropdown: () =>
import('ee_component/vue_shared/components/markdown/ai_actions_dropdown.vue'),
},
directives: {
GlTooltip: GlTooltipDirective,
@ -37,6 +39,7 @@ export default {
newCommentTemplatePath: {
default: null,
},
issuableId: { default: null },
},
props: {
previewMarkdown: {
@ -118,6 +121,14 @@ export default {
const expandText = s__('MarkdownEditor|Click to expand');
return [`<details><summary>${expandText}</summary>`, `{text}`, '</details>'].join('\n');
},
showAiActions() {
return (
this.issuableId &&
this.glFeatures.openaiExperimentation &&
this.glFeatures.summarizeNotes &&
this.glFeatures.summarizeComments
);
},
},
watch: {
showSuggestPopover() {
@ -269,6 +280,7 @@ export default {
</gl-button>
</gl-popover>
</template>
<ai-actions-dropdown v-if="showAiActions" :issuable-id="issuableId" />
<toolbar-button
tag="**"
:button-title="

View File

@ -74,7 +74,7 @@ module Analytics
query = <<~SQL
INSERT INTO #{quoted_table_name}
(
stage_event_hash_id,
stage_event_hash_id,
#{connection.quote_column_name(issuable_id_column)},
group_id,
project_id,

View File

@ -11,7 +11,7 @@ module Issuable
@old_milestone = issuable.milestone
if params[:milestone_id].blank? || params[:milestone_id] == IssuableFinder::Params::NONE
if params[:milestone_id].blank? || params[:milestone_id].to_s == IssuableFinder::Params::NONE
issuable.milestone = nil
return

View File

@ -0,0 +1,87 @@
# frozen_string_literal: true
require 'mail/network/delivery_methods/smtp'
# Monkey patch mail 2.8.1 to make it possible to disable STARTTLS.
# without having to change existing settings.
# This brings in changes from https://github.com/mikel/mail/pull/1536,
# which has not been released yet.
module Mail
class SMTP
def initialize(values)
self.settings = DEFAULTS
settings[:enable_starttls_auto] = nil
settings.merge!(values)
end
private
# `key` is said to be provided when `settings` has a non-nil value for `key`.
def setting_provided?(key)
!settings[key].nil?
end
# Yields one of `:always`, `:auto` or `false` based on `enable_starttls` and `enable_starttls_auto` flags.
# Yields `false` when `smtp_tls?`.
def smtp_starttls
return false if smtp_tls?
if setting_provided?(:enable_starttls) && settings[:enable_starttls]
# enable_starttls: provided and truthy
case settings[:enable_starttls]
when :auto then :auto
when :always then :always
else
:always
end
elsif setting_provided?(:enable_starttls_auto)
# enable_starttls: not provided or false
settings[:enable_starttls_auto] ? :auto : false
else
# enable_starttls_auto: not provided
# enable_starttls: when provided then false
# use :auto when neither enable_starttls* provided
setting_provided?(:enable_starttls) ? false : :auto
end
end
def smtp_tls?
(setting_provided?(:tls) && settings[:tls]) || (setting_provided?(:ssl) && settings[:ssl])
end
def start_smtp_session(&block)
build_smtp_session.start(settings[:domain], settings[:user_name], settings[:password],
settings[:authentication], &block)
end
def build_smtp_session
if smtp_tls? && (settings[:enable_starttls] || settings[:enable_starttls_auto])
# rubocop:disable Layout/LineLength
raise ArgumentError,
":enable_starttls and :tls are mutually exclusive. Set :tls if you're on an SMTPS connection. Set :enable_starttls if you're on an SMTP connection and using STARTTLS for secure TLS upgrade."
# rubocop:enable Layout/LineLength
end
Net::SMTP.new(settings[:address], settings[:port]).tap do |smtp|
if smtp_tls?
smtp.disable_starttls
smtp.enable_tls(ssl_context)
else
smtp.disable_tls
case smtp_starttls
when :always
smtp.enable_starttls(ssl_context)
when :auto
smtp.enable_starttls_auto(ssl_context)
else
smtp.disable_starttls
end
end
smtp.open_timeout = settings[:open_timeout] if settings[:open_timeout]
smtp.read_timeout = settings[:read_timeout] if settings[:read_timeout]
end
end
end
end

View File

@ -18,8 +18,8 @@
# REQUIRED FIELDS
#
- title: "Feature A is deprecated" # (required) Clearly explain the change, or planned change. For example, "The `confidential` field for a `Note` is deprecated" or "CI/CD job names will be limited to 250 characters."
announcement_milestone: "XX.YY" # (required) The milestone when this feature was first announced as deprecated.
removal_milestone: "XX.YY" # (required) The milestone when this feature is planned to be removed
announcement_milestone: "XX.YY" # (required) The milestone when this feature was first announced as deprecated.
breaking_change: true # (required) Change to false if this is not a breaking change.
reporter: exampleuser # (required) GitLab username of the person reporting the change
stage: stage # (required) String value of the stage that the feature was created in. e.g., Growth

View File

@ -14,9 +14,7 @@
#
- title: "Feature A is removed." # (required) Clearly explain the change. For example, "The `confidential` field for a `Note` is removed" or "CI/CD job names are limited to 250 characters."
announcement_milestone: "XX.YY" # (required) The milestone when this feature was deprecated.
announcement_date: "YYYY-MM-DD" # (required) The date of the milestone release when this feature was deprecated. This should almost always be the 22nd of a month (YYYY-MM-DD), unless you did an out of band blog post.
removal_milestone: "XX.YY" # (required) The milestone when this feature is being removed.
removal_date: "YYYY-MM-DD" # (required) This should almost always be the 22nd of a month (YYYY-MM-DD), the date of the milestone release when this feature will be removed.
breaking_change: true # (required) Change to false if this is not a breaking change.
reporter: exampleuser # (required) GitLab username of the person reporting the removal
stage: stage # (required) String value of the stage that the feature was created in. e.g., Growth

View File

@ -65,12 +65,12 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitla
GET /bulk_imports
```
| Attribute | Type | Required | Description |
|:-----------|:--------|:---------|:--------------------------------------------------------------------------------------------|
| `per_page` | integer | no | Number of records to return per page. |
| `page` | integer | no | Page to retrieve. |
| `sort` | string | no | Return GitLab migration sorted in `asc` or `desc` order by creation date. Default is `desc` |
| `status` | string | no | Import status. |
| Attribute | Type | Required | Description |
|:-----------|:--------|:---------|:-----------------------------------------------------------------------------------|
| `per_page` | integer | no | Number of records to return per page. |
| `page` | integer | no | Page to retrieve. |
| `sort` | string | no | Return records sorted in `asc` or `desc` order by creation date. Default is `desc` |
| `status` | string | no | Import status. |
The status can be one of the following:
@ -108,12 +108,12 @@ curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab
GET /bulk_imports/entities
```
| Attribute | Type | Required | Description |
|:-----------|:--------|:---------|:-----------------------------------------------------------------------------------------------------|
| `per_page` | integer | no | Number of records to return per page. |
| `page` | integer | no | Page to retrieve. |
| `sort` | string | no | Return GitLab migration entities sorted in `asc` or `desc` order by creation date. Default is `desc` |
| `status` | string | no | Import status. |
| Attribute | Type | Required | Description |
|:-----------|:--------|:---------|:-----------------------------------------------------------------------------------|
| `per_page` | integer | no | Number of records to return per page. |
| `page` | integer | no | Page to retrieve. |
| `sort` | string | no | Return records sorted in `asc` or `desc` order by creation date. Default is `desc` |
| `status` | string | no | Import status. |
The status can be one of the following:
@ -196,12 +196,12 @@ curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab
GET /bulk_imports/:id/entities
```
| Attribute | Type | Required | Description |
|:-----------|:--------|:---------|:--------------------------------------------------------------------------------------------|
| `per_page` | integer | no | Number of records to return per page. |
| `page` | integer | no | Page to retrieve. |
| `sort` | string | no | Return GitLab migration sorted in `asc` or `desc` order by creation date. Default is `desc` |
| `status` | string | no | Import status. |
| Attribute | Type | Required | Description |
|:-----------|:--------|:---------|:-----------------------------------------------------------------------------------|
| `per_page` | integer | no | Number of records to return per page. |
| `page` | integer | no | Page to retrieve. |
| `sort` | string | no | Return records sorted in `asc` or `desc` order by creation date. Default is `desc` |
| `status` | string | no | Import status. |
The status can be one of the following:

View File

@ -130,9 +130,8 @@ See [database guidelines](database/index.md).
The following integration guides are internal. Some integrations require access to administrative accounts of third-party services and are available only for GitLab team members to contribute to:
- [Jira app development](https://gitlab.com/gitlab-org/manage/integrations/team/-/blob/main/integrations/jira.md)
- [Slack app development](https://gitlab.com/gitlab-org/manage/integrations/team/-/blob/main/integrations/slack.md)
- [ZenTao development](https://gitlab.com/gitlab-org/manage/integrations/team/-/blob/main/integrations/zentao.md)
- [Jira app development](https://gitlab.com/gitlab-org/manage/integrate/team/-/blob/main/integrations/jira.md)
- [GitLab for Slack app development](https://gitlab.com/gitlab-org/manage/integrate/team/-/blob/main/integrations/slack.md)
## Testing guides

View File

@ -20,31 +20,25 @@ are accessible.
- **Self-managed GitLab**: Your GitLab instance must be accessible by Jira.
- **Jira Server**: Your network must allow access to your instance.
NOTE:
When using GitLab 15.0 and later (including GitLab.com) with Jira Server, you might experience a [session token bug in Jira](https://jira.atlassian.com/browse/JSWSERVER-21389). As a workaround, ensure Jira Server is version 9.1.0 and later or 8.20.11 and later.
## Smart Commits
When connecting GitLab with Jira with DVCS, you can process your Jira issues using
special commands, called
[Smart Commits](https://support.atlassian.com/jira-software-cloud/docs/process-issues-with-smart-commits/),
in your commit messages. With Smart Commits, you can:
When connecting GitLab and Jira with the Jira DVCS connector, you can process your Jira issues with
special commands called [Smart Commits](https://support.atlassian.com/jira-software-cloud/docs/process-issues-with-smart-commits/).
With Smart Commits, you can:
- Comment on issues.
- Record time-tracking information against issues.
- Transition issues to any status defined in the Jira project's workflow.
Commands must be in the first line of the commit message. The
[Jira Software documentation](https://support.atlassian.com/jira-software-cloud/docs/process-issues-with-smart-commits/)
contains more information about how Smart Commits work, and what commands are available
for your use.
Commands must be in the first line of the commit message. For more information about how Smart Commits work and what commands are available
for use, see the [Atlassian documentation](https://support.atlassian.com/jira-software-cloud/docs/process-issues-with-smart-commits/).
For Smart Commits to work, the committing user on GitLab must have a corresponding
user on Jira with the same email address or username.
For Smart Commits to work, the GitLab user must have a corresponding
Jira user with the same email address or username.
### Smart Commit syntax
Smart Commits should follow the pattern of:
Smart Commits must follow this pattern:
```plaintext
<ISSUE_KEY> <ignored text> #<command> <optional command parameters>
@ -56,7 +50,7 @@ Some examples:
- Record time tracking: `KEY-123 #time 2w 4d 10h 52m Tracking work time.`
- Close an issue: `KEY-123 #close Closing issue`
A Smart Commit message must not span more than one line (no carriage returns) but
A Smart Commit message must not span more than one line (no carriage returns), but
you can still perform multiple actions in a single commit. For example:
- Add time tracking, add a comment, and transition to **Closed**:
@ -73,64 +67,60 @@ you can still perform multiple actions in a single commit. For example:
## Configure a GitLab application for DVCS
For projects in a single group we recommend you create a [group application](../../oauth_provider.md#create-a-group-owned-application).
For projects across multiple groups we recommend you create and use a `jira` user in GitLab, and use the account
only for integration work. A separate account ensures regular account
maintenance does not affect your integration. If a `jira` user or group application is not feasible,
you can set up this integration as an [instance-wide application](../../oauth_provider.md#create-an-instance-wide-application)
or with a [user owned application](../../oauth_provider.md#create-a-user-owned-application) instead.
For projects in a single group, you should create a [group application](../../oauth_provider.md#create-a-group-owned-application).
1. Navigate to the [appropriate **Applications** section](../../oauth_provider.md).
1. In the **Name** field, enter a descriptive name for the integration, such as `Jira`.
1. In the **Redirect URI** field, enter the URI appropriate for your version of GitLab,
replacing `<gitlab.example.com>` with your GitLab instance domain:
- *For GitLab versions 13.0 and later* **and** *Jira versions 8.14 and later,* use the
For projects across multiple groups, you should create a new user account in GitLab for Jira integration work only.
A separate account ensures regular account maintenance does not affect your integration.
If a separate user account or group application is not possible, you can set up this integration
as an [instance-wide application](../../oauth_provider.md#create-an-instance-wide-application)
or with a [user-owned application](../../oauth_provider.md#create-a-user-owned-application).
1. Go to the [appropriate **Applications** section](../../oauth_provider.md).
1. In the **Name** text box, enter a descriptive name for the integration (for example, `Jira`).
1. In the **Redirect URI** text box, enter the appropriate URI.
Replace `<gitlab.example.com>` with your GitLab instance domain:
- **In Jira 8.14 and later**, use the
generated `Redirect URL` from
[Linking GitLab accounts with Jira](https://confluence.atlassian.com/adminjiraserver/linking-gitlab-accounts-1027142272.html).
- *For GitLab versions 13.0 and later* **and** *Jira Cloud,* use `https://<gitlab.example.com>/login/oauth/callback`.
- *For GitLab versions 11.3 and later* **and** *Jira versions 8.13 and earlier,* use `https://<gitlab.example.com>/login/oauth/callback`.
[linking GitLab accounts](https://confluence.atlassian.com/adminjiraserver/linking-gitlab-accounts-1027142272.html).
- **In Jira 8.13 and earlier**, use `https://<gitlab.example.com>/login/oauth/callback`.
If you use GitLab.com, the URL is `https://gitlab.com/login/oauth/callback`.
- *For GitLab versions 11.2 and earlier,* use
`https://<gitlab.example.com>/-/jira/login/oauth/callback`.
1. For **Scopes**, select `api` and clear any other checkboxes.
- The DVCS connector requires a _write-enabled_ `api` scope to automatically create and manage required webhooks.
1. In **Scopes**, select `api` and clear any other checkboxes.
The DVCS connector requires a **write-enabled** `api` scope to automatically create and manage required webhooks.
1. Select **Submit**.
1. Copy the **Application ID** and **Secret** values.
You need them to configure Jira.
You need these values to configure Jira.
## Configure Jira for DVCS
Configure this connection when you want to import all GitLab commits and branches,
for the groups you specify, into Jira. This import takes a few minutes and, after
To import all GitLab commits and branches into Jira for the groups you specify,
configure Jira for DVCS. This import takes a few minutes and, after
it completes, refreshes every 60 minutes:
1. Complete the [GitLab configuration](#configure-a-gitlab-application-for-dvcs).
1. Go to your DVCS accounts:
- *For Jira Server,* select **Settings (gear) > Applications > DVCS accounts**.
- *For Jira Cloud,* select **Settings (gear) > Products > DVCS accounts**.
1. Go to your DVCS account:
- **For Jira Server**, select **Settings (gear) > Applications > DVCS accounts**.
1. To create a new integration, select the appropriate value for **Host**:
- *For Jira versions 8.14 and later:* Select **GitLab** or
- **In Jira 8.14 and later**, select **GitLab** or
**GitLab Self-Managed**.
- *For Jira Cloud or Jira versions 8.13 and earlier:* Select **GitHub Enterprise**.
1. For **Team or User Account**, enter either:
- *For Jira versions 8.14 and later:*
- **In Jira 8.13 and earlier**, select **GitHub Enterprise**.
1. For **Team or User Account**, enter:
- **In Jira 8.14 and later**:
- The relative path of a top-level GitLab group that
[the GitLab user](#configure-a-gitlab-application-for-dvcs) has access to.
- *For Jira Cloud or Jira versions 8.13 and earlier:*
[the GitLab user](#configure-a-gitlab-application-for-dvcs) can access.
- **In Jira 8.13 and earlier**:
- The relative path of a top-level GitLab group that
[the GitLab user](#configure-a-gitlab-application-for-dvcs) has access to.
[the GitLab user](#configure-a-gitlab-application-for-dvcs) can access.
- The relative path of your personal namespace.
1. In the **Host URL** field, enter the URI appropriate for your version of GitLab,
replacing `<gitlab.example.com>` with your GitLab instance domain:
- *For GitLab versions 11.3 and later,* use `https://<gitlab.example.com>`.
- *For GitLab versions 11.2 and earlier,* use
`https://<gitlab.example.com>/-/jira`.
1. In the **Host URL** text box, enter the appropriate URL.
Replace `<gitlab.example.com>` with your GitLab instance domain.
Use `https://<gitlab.example.com>`.
1. For **Client ID**, use the **Application ID** value from the previous section.
1. For **Client Secret**, use the **Secret** value from the previous section.
1. Ensure that the rest of the checkboxes are selected.
1. For **Client ID**, use the [**Application ID** value](#configure-a-gitlab-application-for-dvcs).
1. For **Client Secret**, use the [**Secret** value](#configure-a-gitlab-application-for-dvcs).
1. Ensure that all other checkboxes are selected.
1. To create the DVCS account, select **Add** and then **Continue**.
1. Jira redirects to GitLab where you have to confirm the authorization.
GitLab then redirects back to Jira where the synced
@ -139,7 +129,7 @@ it completes, refreshes every 60 minutes:
To connect additional GitLab projects from other GitLab top-level groups or
personal namespaces, repeat the previous steps with additional Jira DVCS accounts.
After you configure the integration, read more about [how to test and use it](../development_panel.md).
For more information about how to use the integration, see [Jira development panel](../development_panel.md).
## Refresh data imported to Jira
@ -164,11 +154,11 @@ For more information, see [Install the GitLab for Jira Cloud app](../connect-app
| Smart Commits | **{check-circle}** Yes | **{check-circle}** Yes |
| Sync MRs | **{check-circle}** Yes | **{check-circle}** Yes |
| Sync branches | **{check-circle}** Yes | **{check-circle}** Yes |
| Sync commits | **{check-circle}** Yes | **{check-circle}** Yes|
| Sync commits | **{check-circle}** Yes | **{check-circle}** Yes |
| Sync builds | **{dotted-circle}** No | **{check-circle}** Yes |
| Sync deployments | **{dotted-circle}** No | **{check-circle}** Yes |
| Sync feature flags | **{dotted-circle}** No | **{check-circle}** Yes |
| Sync interval | 60 Minutes | Real time |
| Sync interval | 60 Minutes | Real time |
| Create branches | **{dotted-circle}** No | **{check-circle}** Yes (Only GitLab SaaS) |
| Historic data sync | **{check-circle}** Yes | **{dotted-circle}** No |

View File

@ -18,6 +18,12 @@ appear in any logs:
Error obtaining access token. Cannot access https://gitlab.example.com from Jira.
```
## Session token bug in Jira
When using GitLab 15.0 and later (including GitLab.com) with Jira Server, you might experience
a [session token bug in Jira](https://jira.atlassian.com/browse/JSWSERVER-21389). As a workaround,
ensure Jira Server is version 9.1.0 and later or 8.20.11 and later.
## SSL and TLS problems
Problems with SSL and TLS can cause this error message:

View File

@ -247,6 +247,25 @@ To change this setting for a specific group:
To change this setting globally, see [Default project creation protection](../admin_area/settings/visibility_and_access_controls.md#define-which-roles-can-create-projects).
## Add Group README
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/390230) in GitLab 15.11 [with a flag](../../administration/feature_flags.md) named `show_group_readme`. Disabled by default.
As a group owner or member, you can use a README to provide more information about your team, and invite users to contribute to your projects.
The README is displayed on the group overview page, and can be changed in the group settings. All group members can edit the README.
Prerequisite:
- To create the README from the group settings, you must have the Owner role for the group.
To add a group README:
1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. In the **Group README** section, select **Add README**. This action creates a new project `gitlab-profile` that contains the `README.md` file.
1. On the prompt for creating a README, select **Create and add README**. You're redirected to the Web IDE, where a README file is created.
1. In the Web IDE, edit and commit the `README.md` file.
## Change the owner of a group
You can change the owner of a group. Each group must always have at least one

View File

@ -41,7 +41,7 @@ module Gitlab
end
def average_in_seconds
Arel::Nodes::Extract.new(average, :epoch)
Arel::Nodes::NamedFunction.new('CAST', [Arel::Nodes::Extract.new(average, :epoch).as('double precision')])
end
end
end

View File

@ -38,7 +38,8 @@ module Gitlab
end
def median_duration_in_seconds
Arel::Nodes::Extract.new(percentile_cont, :epoch)
Arel::Nodes::NamedFunction.new('CAST',
[Arel::Nodes::Extract.new(percentile_cont, :epoch).as('double precision')])
end
end
end

View File

@ -147,7 +147,8 @@ module Gitlab
{
name: project.name,
full_path: project.full_path
full_path: project.full_path,
type: namespace.type
}
end

View File

@ -13,7 +13,9 @@ module Gitlab
end
def round_duration_to_seconds
Arel::Nodes::NamedFunction.new('ROUND', [Arel::Nodes::Extract.new(duration, :epoch)])
Arel::Nodes::NamedFunction.new('ROUND', [
Arel::Nodes::NamedFunction.new('CAST', [Arel::Nodes::Extract.new(duration, :epoch).as('double precision')])
])
end
def duration

View File

@ -76,7 +76,13 @@ module Gitlab
end
def get_from_auth_hash_or_info(key)
coerce_utf8(auth_hash[key]) || get_info(key)
if auth_hash.key?(key)
coerce_utf8(auth_hash[key])
elsif auth_hash.key?(:extra) && auth_hash.extra.key?(:raw_info) && !auth_hash.extra.raw_info[key].nil?
coerce_utf8(auth_hash.extra.raw_info[key])
else
get_info(key)
end
end
# Allow for configuring a custom username claim per provider from

View File

@ -1845,6 +1845,9 @@ msgstr ""
msgid "ACTION REQUIRED: Something went wrong while obtaining the Let's Encrypt certificate for GitLab Pages domain '%{domain}'"
msgstr ""
msgid "AI actions"
msgstr ""
msgid "AI|Close the Code Explanation"
msgstr ""
@ -12566,6 +12569,9 @@ msgstr ""
msgid "Creates a branch and a merge request to resolve this issue."
msgstr ""
msgid "Creates a summary of all comments"
msgstr ""
msgid "Creates branch '%{branch_name}' and a merge request to resolve this issue."
msgstr ""
@ -42945,6 +42951,9 @@ msgstr ""
msgid "Suite"
msgstr ""
msgid "Summarize comments"
msgstr ""
msgid "Summary"
msgstr ""

View File

@ -136,6 +136,7 @@ describe('Value stream analytics component', () => {
it('passes the paths to the filter bar', () => {
expect(findFilters().props()).toEqual({
groupPath,
namespacePath: groupPath,
endDate: createdBefore,
hasDateRangeFilter: true,

View File

@ -10,11 +10,14 @@ import {
selectedProjects,
} from './mock_data';
const { path } = currentGroup;
const groupPath = `groups/${path}`;
function createComponent(props = {}) {
return shallowMount(ValueStreamFilters, {
propsData: {
selectedProjects,
groupId: currentGroup.id,
groupPath,
namespacePath: currentGroup.fullPath,
startDate,
endDate,

View File

@ -0,0 +1,86 @@
# frozen_string_literal: true
# rubocop:disable RSpec/VariableDefinition, RSpec/VariableName
require 'spec_helper'
require 'mail'
require_relative '../../config/initializers/mail_starttls_patch'
RSpec.describe 'Mail STARTTLS patch', feature_category: :integrations do
using RSpec::Parameterized::TableSyntax
let(:message) do
Mail.new do
from 'sender@example.com'
to 'receiver@example.com'
subject 'test mesage'
end
end
# Taken from https://github.com/mikel/mail/pull/1536#issue-1490438378
where(:ssl, :tls, :enable_starttls, :enable_starttls_auto, :smtp_tls, :smtp_starttls_mode) do
true | nil | nil | nil | true | false
nil | false | nil | nil | false | :auto
nil | false | nil | true | false | :auto
false | false | true | false | false | :always
false | nil | false | false | false | false
false | false | false | nil | false | false
false | nil | :always | nil | false | :always
false | nil | :auto | nil | false | :auto
end
with_them do
let(:values) do
{
ssl: ssl,
tls: tls,
enable_starttls: enable_starttls,
enable_starttls_auto: enable_starttls_auto
}
end
let(:mail) { Mail::SMTP.new(values) }
let(:smtp) { double }
it 'sets TLS and STARTTLS settings properly' do
expect(smtp).to receive(:open_timeout=)
expect(smtp).to receive(:read_timeout=)
expect(smtp).to receive(:start)
if smtp_tls
expect(smtp).to receive(:enable_tls)
expect(smtp).to receive(:disable_starttls)
else
expect(smtp).to receive(:disable_tls)
case smtp_starttls_mode
when :always
expect(smtp).to receive(:enable_starttls)
when :auto
expect(smtp).to receive(:enable_starttls_auto)
when false
expect(smtp).to receive(:disable_starttls)
end
end
allow(Net::SMTP).to receive(:new).and_return(smtp)
mail.deliver!(message)
end
end
context 'when enable_starttls and tls are enabled' do
let(:values) do
{
tls: true,
enable_starttls: true
}
end
let(:mail) { Mail::SMTP.new(values) }
it 'raises an argument exception' do
expect { mail.deliver!(message) }.to raise_error(ArgumentError)
end
end
end
# rubocop:enable RSpec/VariableDefinition, RSpec/VariableName

View File

@ -17,7 +17,8 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::RequestParams, feature_categor
expect(attributes).to match(hash_including({
namespace: {
name: project.name,
full_path: project.full_path
full_path: project.full_path,
type: "Project"
}
}))
end

View File

@ -2,14 +2,19 @@
require 'spec_helper'
RSpec.describe Gitlab::Auth::OAuth::AuthHash do
RSpec.describe Gitlab::Auth::OAuth::AuthHash, feature_category: :user_management do
let(:provider) { 'ldap' }
let(:auth_hash) do
described_class.new(
OmniAuth::AuthHash.new(
provider: provider,
uid: uid_ascii,
info: info_hash
info: info_hash,
extra: {
raw_info: {
'https://example.com/claims/username': username_claim_utf8
}
}
)
)
end
@ -24,6 +29,7 @@ RSpec.describe Gitlab::Auth::OAuth::AuthHash do
let(:first_name_raw) { +'Onur' }
let(:last_name_raw) { +"K\xC3\xBC\xC3\xA7\xC3\xBCk" }
let(:name_raw) { +"Onur K\xC3\xBC\xC3\xA7\xC3\xBCk" }
let(:username_claim_raw) { +'onur.partner' }
let(:uid_ascii) { uid_raw.force_encoding(Encoding::ASCII_8BIT) }
let(:email_ascii) { email_raw.force_encoding(Encoding::ASCII_8BIT) }
@ -37,6 +43,7 @@ RSpec.describe Gitlab::Auth::OAuth::AuthHash do
let(:nickname_utf8) { nickname_ascii.force_encoding(Encoding::UTF_8) }
let(:name_utf8) { name_ascii.force_encoding(Encoding::UTF_8) }
let(:first_name_utf8) { first_name_ascii.force_encoding(Encoding::UTF_8) }
let(:username_claim_utf8) { username_claim_raw.force_encoding(Encoding::ASCII_8BIT) }
let(:info_hash) do
{
@ -98,10 +105,16 @@ RSpec.describe Gitlab::Auth::OAuth::AuthHash do
allow(Gitlab::Auth::OAuth::Provider).to receive(:config_for).and_return(provider_config)
end
it 'uses the custom field for the username' do
it 'uses the custom field for the username within info' do
expect(auth_hash.username).to eql first_name_utf8
end
it 'uses the custom field for the username within extra.raw_info' do
provider_config['args']['gitlab_username_claim'] = 'https://example.com/claims/username'
expect(auth_hash.username).to eql username_claim_utf8
end
it 'uses the default claim for the username when the custom claim is not found' do
provider_config['args']['gitlab_username_claim'] = 'nonexistent'
@ -146,4 +159,66 @@ RSpec.describe Gitlab::Auth::OAuth::AuthHash do
expect(auth_hash.password.encoding).to eql Encoding::UTF_8
end
end
describe '#get_from_auth_hash_or_info' do
context 'for a key not within auth_hash' do
let(:auth_hash) do
described_class.new(
OmniAuth::AuthHash.new(
provider: provider,
uid: uid_ascii,
info: info_hash
)
)
end
let(:info_hash) { { nickname: nickname_ascii } }
it 'provides username from info_hash' do
expect(auth_hash.username).to eql nickname_utf8
end
end
context 'for a key within auth_hash' do
let(:auth_hash) do
described_class.new(
OmniAuth::AuthHash.new(
provider: provider,
uid: uid_ascii,
info: info_hash,
username: nickname_ascii
)
)
end
let(:info_hash) { { something: nickname_ascii } }
it 'provides username from auth_hash' do
expect(auth_hash.username).to eql nickname_utf8
end
end
context 'for a key within auth_hash extra' do
let(:auth_hash) do
described_class.new(
OmniAuth::AuthHash.new(
provider: provider,
uid: uid_ascii,
info: info_hash,
extra: {
raw_info: {
nickname: nickname_ascii
}
}
)
)
end
let(:info_hash) { { something: nickname_ascii } }
it 'provides username from auth_hash extra' do
expect(auth_hash.username).to eql nickname_utf8
end
end
end
end

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category: :importers do
RSpec.describe API::ProjectExport, :aggregate_failures, :clean_gitlab_redis_cache, feature_category: :importers do
let_it_be(:project) { create(:project) }
let_it_be(:project_none) { create(:project) }
let_it_be(:project_started) { create(:project) }
@ -45,21 +45,27 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
end
describe 'GET /projects/:project_id/export' do
it_behaves_like 'GET request permissions for admin mode' do
let(:failed_status_code) { :not_found }
end
shared_examples_for 'get project export status not found' do
it_behaves_like '404 response' do
let(:request) { get api(path, user) }
subject(:request) { get api(path, user) }
end
end
shared_examples_for 'get project export status denied' do
it_behaves_like '403 response' do
let(:request) { get api(path, user) }
subject(:request) { get api(path, user) }
end
end
shared_examples_for 'get project export status ok' do
let_it_be(:admin_mode) { false }
it 'is none' do
get api(path_none, user)
get api(path_none, user, admin_mode: admin_mode)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/project/export_status')
@ -72,7 +78,7 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
end
it 'returns status started' do
get api(path_started, user)
get api(path_started, user, admin_mode: admin_mode)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/project/export_status')
@ -82,7 +88,7 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
context 'when project export has finished' do
it 'returns status finished' do
get api(path_finished, user)
get api(path_finished, user, admin_mode: admin_mode)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/project/export_status')
@ -96,7 +102,7 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
end
it 'returns status regeneration_in_progress' do
get api(path_finished, user)
get api(path_finished, user, admin_mode: admin_mode)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('public_api/v4/project/export_status')
@ -106,14 +112,16 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
end
it_behaves_like 'when project export is disabled' do
let(:request) { get api(path, admin) }
subject(:request) { get api(path, admin, admin_mode: true) }
end
context 'when project export is enabled' do
context 'when user is an admin' do
let(:user) { admin }
it_behaves_like 'get project export status ok'
it_behaves_like 'get project export status ok' do
let(:admin_mode) { true }
end
end
context 'when user is a maintainer' do
@ -159,29 +167,34 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
end
describe 'GET /projects/:project_id/export/download' do
it_behaves_like 'GET request permissions for admin mode' do
let(:path) { download_path_finished }
let(:failed_status_code) { :not_found }
end
shared_examples_for 'get project export download not found' do
it_behaves_like '404 response' do
let(:request) { get api(download_path, user) }
subject(:request) { get api(download_path, user) }
end
end
shared_examples_for 'get project export download denied' do
it_behaves_like '403 response' do
let(:request) { get api(download_path, user) }
subject(:request) { get api(download_path, user) }
end
end
shared_examples_for 'get project export download' do
it_behaves_like '404 response' do
let(:request) { get api(download_path_none, user) }
subject(:request) { get api(download_path_none, user, admin_mode: admin_mode) }
end
it_behaves_like '404 response' do
let(:request) { get api(download_path_started, user) }
subject(:request) { get api(download_path_started, user, admin_mode: admin_mode) }
end
it 'downloads' do
get api(download_path_finished, user)
get api(download_path_finished, user, admin_mode: admin_mode)
expect(response).to have_gitlab_http_status(:ok)
end
@ -190,7 +203,7 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
shared_examples_for 'get project export upload after action' do
context 'and is uploading' do
it 'downloads' do
get api(download_path_export_action, user)
get api(download_path_export_action, user, admin_mode: admin_mode)
expect(response).to have_gitlab_http_status(:ok)
end
@ -202,7 +215,7 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
end
it 'returns 404' do
get api(download_path_export_action, user)
get api(download_path_export_action, user, admin_mode: admin_mode)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('The project export file is not available yet')
@ -219,12 +232,14 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
end
it_behaves_like '404 response' do
let(:request) { get api(download_path_export_action, user) }
subject(:request) { get api(download_path_export_action, user, admin_mode: admin_mode) }
end
end
end
shared_examples_for 'get project download by strategy' do
let_it_be(:admin_mode) { false }
context 'when upload strategy set' do
it_behaves_like 'get project export upload after action'
end
@ -235,17 +250,19 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
end
it_behaves_like 'when project export is disabled' do
let(:request) { get api(download_path, admin) }
subject(:request) { get api(download_path, admin, admin_mode: true) }
end
context 'when project export is enabled' do
context 'when user is an admin' do
let(:user) { admin }
it_behaves_like 'get project download by strategy'
it_behaves_like 'get project download by strategy' do
let(:admin_mode) { true }
end
context 'when rate limit is exceeded' do
let(:request) { get api(download_path, admin) }
subject(:request) { get api(download_path, admin, admin_mode: true) }
before do
allow_next_instance_of(Gitlab::ApplicationRateLimiter::BaseStrategy) do |strategy|
@ -271,7 +288,7 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
# simulate prior request to the same namespace, which increments the rate limit counter for that scope
Gitlab::ApplicationRateLimiter.throttled?(:project_download_export, scope: [user, project_finished.namespace])
get api(download_path_finished, user)
get api(download_path_finished, user, admin_mode: true)
expect(response).to have_gitlab_http_status(:too_many_requests)
end
@ -280,7 +297,7 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
Gitlab::ApplicationRateLimiter.throttled?(:project_download_export,
scope: [user, create(:project, :with_export).namespace])
get api(download_path_finished, user)
get api(download_path_finished, user, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
end
end
@ -345,30 +362,41 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
end
describe 'POST /projects/:project_id/export' do
let(:admin_mode) { false }
let(:params) { {} }
it_behaves_like 'POST request permissions for admin mode' do
let(:params) { { 'upload[url]' => 'http://gitlab.com' } }
let(:failed_status_code) { :not_found }
let(:success_status_code) { :accepted }
end
subject(:request) { post api(path, user, admin_mode: admin_mode), params: params }
shared_examples_for 'post project export start not found' do
it_behaves_like '404 response' do
let(:request) { post api(path, user) }
end
it_behaves_like '404 response'
end
shared_examples_for 'post project export start denied' do
it_behaves_like '403 response' do
let(:request) { post api(path, user) }
end
it_behaves_like '403 response'
end
shared_examples_for 'post project export start' do
let_it_be(:admin_mode) { false }
context 'with upload strategy' do
context 'when params invalid' do
it_behaves_like '400 response' do
let(:request) { post(api(path, user, admin_mode: user.admin?), params: { 'upload[url]' => 'whatever' }) }
let(:params) { { 'upload[url]' => 'whatever' } }
end
end
it 'starts' do
allow_any_instance_of(Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy).to receive(:send_file)
post(api(path, user), params: { 'upload[url]' => 'http://gitlab.com' })
request do
let(:params) { { 'upload[url]' => 'http://gitlab.com' } }
end
expect(response).to have_gitlab_http_status(:accepted)
end
@ -388,7 +416,7 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
it 'starts' do
expect_any_instance_of(Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy).not_to receive(:send_file)
post api(path, user)
request
expect(response).to have_gitlab_http_status(:accepted)
end
@ -396,20 +424,21 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
it 'removes previously exported archive file' do
expect(project).to receive(:remove_exports).once
post api(path, user)
request
end
end
end
it_behaves_like 'when project export is disabled' do
let(:request) { post api(path, admin) }
end
it_behaves_like 'when project export is disabled'
context 'when project export is enabled' do
context 'when user is an admin' do
let(:user) { admin }
let(:admin_mode) { true }
it_behaves_like 'post project export start'
it_behaves_like 'post project export start' do
let(:admin_mode) { true }
end
context 'with project export size limit' do
before do
@ -417,7 +446,7 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
end
it 'starts if limit not exceeded' do
post api(path, user)
request
expect(response).to have_gitlab_http_status(:accepted)
end
@ -425,7 +454,7 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
it '400 response if limit exceeded' do
project.statistics.update!(lfs_objects_size: 2.megabytes, repository_size: 2.megabytes)
post api(path, user)
request
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response["message"]).to include('The project size exceeds the export limit.')
@ -441,7 +470,7 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
end
it 'prevents requesting project export' do
post api(path, admin)
request
expect(response).to have_gitlab_http_status(:too_many_requests)
expect(json_response['message']['error']).to eq('This endpoint has been requested too many times. Try again later.')
@ -559,7 +588,7 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
let(:relation) { ::BulkImports::FileTransfer::ProjectConfig.new(project).skipped_relations.first }
it_behaves_like '400 response' do
let(:request) { get api(download_path, user) }
subject(:request) { get api(download_path, user) }
end
end
@ -595,7 +624,7 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
describe 'POST /projects/:id/export_relations' do
it_behaves_like '404 response' do
let(:request) { post api(path, user) }
subject(:request) { post api(path, user) }
end
end
@ -608,13 +637,13 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
end
it_behaves_like '404 response' do
let(:request) { post api(path, user) }
subject(:request) { post api(path, user) }
end
end
describe 'GET /projects/:id/export_relations/status' do
it_behaves_like '404 response' do
let(:request) { get api(status_path, user) }
subject(:request) { get api(status_path, user) }
end
end
end
@ -629,26 +658,26 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
describe 'POST /projects/:id/export_relations' do
it_behaves_like '403 response' do
let(:request) { post api(path, developer) }
subject(:request) { post api(path, developer) }
end
end
describe 'GET /projects/:id/export_relations/download' do
it_behaves_like '403 response' do
let(:request) { get api(download_path, developer) }
subject(:request) { get api(download_path, developer) }
end
end
describe 'GET /projects/:id/export_relations/status' do
it_behaves_like '403 response' do
let(:request) { get api(status_path, developer) }
subject(:request) { get api(status_path, developer) }
end
end
end
context 'when bulk import is disabled' do
it_behaves_like '404 response' do
let(:request) { get api(path, user) }
subject(:request) { get api(path, user) }
end
end
end

View File

@ -2,11 +2,12 @@
require 'spec_helper'
RSpec.describe API::ProjectSnapshots, feature_category: :source_code_management do
RSpec.describe API::ProjectSnapshots, :aggregate_failures, feature_category: :source_code_management do
include WorkhorseHelpers
let(:project) { create(:project) }
let(:admin) { create(:admin) }
let(:path) { "/projects/#{project.id}/snapshot" }
before do
allow(Feature::Gitaly).to receive(:server_feature_flags).and_return({
@ -32,27 +33,29 @@ RSpec.describe API::ProjectSnapshots, feature_category: :source_code_management
expect(response.parsed_body).to be_empty
end
it_behaves_like 'GET request permissions for admin mode'
it 'returns authentication error as project owner' do
get api("/projects/#{project.id}/snapshot", project.first_owner)
get api(path, project.first_owner)
expect(response).to have_gitlab_http_status(:forbidden)
end
it 'returns authentication error as unauthenticated user' do
get api("/projects/#{project.id}/snapshot", nil)
get api(path, nil)
expect(response).to have_gitlab_http_status(:unauthorized)
end
it 'requests project repository raw archive as administrator' do
get api("/projects/#{project.id}/snapshot", admin), params: { wiki: '0' }
get api(path, admin, admin_mode: true), params: { wiki: '0' }
expect(response).to have_gitlab_http_status(:ok)
expect_snapshot_response_for(project.repository)
end
it 'requests wiki repository raw archive as administrator' do
get api("/projects/#{project.id}/snapshot", admin), params: { wiki: '1' }
get api(path, admin, admin_mode: true), params: { wiki: '1' }
expect(response).to have_gitlab_http_status(:ok)
expect_snapshot_response_for(project.wiki.repository)

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe API::ProjectSnippets, feature_category: :source_code_management do
RSpec.describe API::ProjectSnippets, :aggregate_failures, feature_category: :source_code_management do
include SnippetHelpers
let_it_be(:project) { create(:project, :public) }
@ -14,8 +14,12 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
describe "GET /projects/:project_id/snippets/:id/user_agent_detail" do
let_it_be(:user_agent_detail) { create(:user_agent_detail, subject: public_snippet) }
it_behaves_like 'GET request permissions for admin mode' do
let(:path) { "/projects/#{public_snippet.project.id}/snippets/#{public_snippet.id}/user_agent_detail" }
end
it 'exposes known attributes' do
get api("/projects/#{project.id}/snippets/#{public_snippet.id}/user_agent_detail", admin)
get api("/projects/#{project.id}/snippets/#{public_snippet.id}/user_agent_detail", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['user_agent']).to eq(user_agent_detail.user_agent)
@ -26,7 +30,7 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
it 'respects project scoping' do
other_project = create(:project)
get api("/projects/#{other_project.id}/snippets/#{public_snippet.id}/user_agent_detail", admin)
get api("/projects/#{other_project.id}/snippets/#{public_snippet.id}/user_agent_detail", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:not_found)
end
@ -38,7 +42,7 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
context 'with snippets disabled' do
it_behaves_like '403 response' do
let(:request) { get api("/projects/#{project_no_snippets.id}/snippets/#{non_existing_record_id}/user_agent_detail", admin, admin_mode: true) }
subject(:request) { get api("/projects/#{project_no_snippets.id}/snippets/#{non_existing_record_id}/user_agent_detail", admin, admin_mode: true) }
end
end
end
@ -72,7 +76,7 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
context 'with snippets disabled' do
it_behaves_like '403 response' do
let(:request) { get api("/projects/#{project_no_snippets.id}/snippets", user) }
subject(:request) { get api("/projects/#{project_no_snippets.id}/snippets", user) }
end
end
end
@ -83,16 +87,14 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
it 'returns snippet json' do
get api("/projects/#{project.id}/snippets/#{snippet.id}", user)
aggregate_failures do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['title']).to eq(snippet.title)
expect(json_response['description']).to eq(snippet.description)
expect(json_response['file_name']).to eq(snippet.file_name_on_repo)
expect(json_response['files']).to eq(snippet.blobs.map { |blob| snippet_blob_file(blob) })
expect(json_response['ssh_url_to_repo']).to eq(snippet.ssh_url_to_repo)
expect(json_response['http_url_to_repo']).to eq(snippet.http_url_to_repo)
end
expect(json_response['title']).to eq(snippet.title)
expect(json_response['description']).to eq(snippet.description)
expect(json_response['file_name']).to eq(snippet.file_name_on_repo)
expect(json_response['files']).to eq(snippet.blobs.map { |blob| snippet_blob_file(blob) })
expect(json_response['ssh_url_to_repo']).to eq(snippet.ssh_url_to_repo)
expect(json_response['http_url_to_repo']).to eq(snippet.http_url_to_repo)
end
it 'returns 404 for invalid snippet id' do
@ -104,7 +106,7 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
context 'with snippets disabled' do
it_behaves_like '403 response' do
let(:request) { get api("/projects/#{project_no_snippets.id}/snippets/#{non_existing_record_id}", user) }
subject(:request) { get api("/projects/#{project_no_snippets.id}/snippets/#{non_existing_record_id}", user) }
end
end
@ -126,22 +128,25 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
let(:file_content) { 'puts "hello world"' }
let(:file_params) { { files: [{ file_path: file_path, content: file_content }] } }
let(:params) { base_params.merge(file_params) }
let(:admin_mode) { false }
subject { post api("/projects/#{project.id}/snippets/", actor, admin_mode: actor.admin?), params: params }
subject(:request) { post api("/projects/#{project.id}/snippets/", actor, admin_mode: admin_mode), params: params }
it_behaves_like 'POST request permissions for admin mode' do
let(:path) { "/projects/#{project.id}/snippets/" }
end
shared_examples 'project snippet repository actions' do
let(:snippet) { ProjectSnippet.find(json_response['id']) }
it 'commit the files to the repository' do
subject
request
aggregate_failures do
expect(snippet.repository.exists?).to be_truthy
expect(snippet.repository.exists?).to be_truthy
blob = snippet.repository.blob_at(snippet.default_branch, file_path)
blob = snippet.repository.blob_at(snippet.default_branch, file_path)
expect(blob.data).to eq file_content
end
expect(blob.data).to eq file_content
end
end
@ -152,7 +157,7 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
it 'creates a new snippet' do
project.add_developer(actor)
subject
request
expect(response).to have_gitlab_http_status(:created)
end
@ -160,7 +165,7 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
context 'that does not belong to the project' do
it 'does not create a new snippet' do
subject
request
expect(response).to have_gitlab_http_status(:forbidden)
end
@ -180,7 +185,7 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
end
it 'creates a new snippet' do
subject
request
expect(response).to have_gitlab_http_status(:created)
snippet = ProjectSnippet.find(json_response['id'])
@ -196,9 +201,10 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
context 'with an admin' do
let(:actor) { admin }
let(:admin_mode) { true }
it 'creates a new snippet' do
subject
request
expect(response).to have_gitlab_http_status(:created)
snippet = ProjectSnippet.find(json_response['id'])
@ -214,7 +220,7 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
it 'returns 400 for missing parameters' do
params.delete(:title)
subject
request
expect(response).to have_gitlab_http_status(:bad_request)
end
@ -226,7 +232,7 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
it 'returns 400 if title is blank' do
params[:title] = ''
subject
request
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq 'title is empty'
@ -235,6 +241,7 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
context 'when save fails because the repository could not be created' do
let(:actor) { admin }
let(:admin_mode) { true }
before do
allow_next_instance_of(Snippets::CreateService) do |instance|
@ -243,7 +250,7 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
end
it 'returns 400' do
subject
request
expect(response).to have_gitlab_http_status(:bad_request)
end
@ -264,7 +271,7 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
it 'creates the snippet' do
params['visibility'] = 'private'
expect { subject }.to change { Snippet.count }.by(1)
expect { request }.to change { Snippet.count }.by(1)
end
end
@ -274,13 +281,13 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
end
it 'rejects the snippet' do
expect { subject }.not_to change { Snippet.count }
expect { request }.not_to change { Snippet.count }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']['error']).to match(/snippet has been recognized as spam/)
end
it 'creates a spam log' do
expect { subject }
expect { request }
.to log_spam(title: 'Test Title', user_id: user.id, noteable_type: 'ProjectSnippet')
end
end
@ -288,7 +295,7 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
context 'with snippets disabled' do
it_behaves_like '403 response' do
let(:request) { post api("/projects/#{project_no_snippets.id}/snippets", user), params: params }
subject(:request) { post api("/projects/#{project_no_snippets.id}/snippets", user), params: params }
end
end
end
@ -296,6 +303,11 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
describe 'PUT /projects/:project_id/snippets/:id/' do
let(:visibility_level) { Snippet::PUBLIC }
let(:snippet) { create(:project_snippet, :repository, author: admin, visibility_level: visibility_level, project: project) }
let(:params) { { title: 'Foo' } }
it_behaves_like 'PUT request permissions for admin mode' do
let(:path) { "/projects/#{snippet.project.id}/snippets/#{snippet.id}" }
end
it_behaves_like 'snippet file updates'
it_behaves_like 'snippet non-file updates'
@ -317,7 +329,7 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
let(:visibility_level) { Snippet::PRIVATE }
it 'creates the snippet' do
expect { update_snippet(params: { title: 'Foo' }) }
expect { update_snippet(admin_mode: true, params: params) }
.to change { snippet.reload.title }.to('Foo')
end
end
@ -326,12 +338,12 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
let(:visibility_level) { Snippet::PUBLIC }
it 'rejects the snippet' do
expect { update_snippet(params: { title: 'Foo' }) }
expect { update_snippet(params: params) }
.not_to change { snippet.reload.title }
end
it 'creates a spam log' do
expect { update_snippet(params: { title: 'Foo' }) }
expect { update_snippet(params: params) }
.to log_spam(title: 'Foo', user_id: admin.id, noteable_type: 'ProjectSnippet')
end
end
@ -340,7 +352,7 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
let(:visibility_level) { Snippet::PRIVATE }
it 'rejects the snippet' do
expect { update_snippet(params: { title: 'Foo', visibility: 'public' }) }
expect { update_snippet(admin_mode: true, params: { title: 'Foo', visibility: 'public' }) }
.not_to change { snippet.reload.title }
expect(response).to have_gitlab_http_status(:bad_request)
@ -348,7 +360,7 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
end
it 'creates a spam log' do
expect { update_snippet(params: { title: 'Foo', visibility: 'public' }) }
expect { update_snippet(admin_mode: true, params: { title: 'Foo', visibility: 'public' }) }
.to log_spam(title: 'Foo', user_id: admin.id, noteable_type: 'ProjectSnippet')
end
end
@ -356,47 +368,55 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
context 'with snippets disabled' do
it_behaves_like '403 response' do
let(:request) { put api("/projects/#{project_no_snippets.id}/snippets/#{non_existing_record_id}", admin, admin_mode: true), params: { description: 'foo' } }
subject(:request) { put api("/projects/#{project_no_snippets.id}/snippets/#{non_existing_record_id}", admin, admin_mode: true), params: { description: 'foo' } }
end
end
def update_snippet(snippet_id: snippet.id, params: {})
put api("/projects/#{snippet.project.id}/snippets/#{snippet_id}", admin), params: params
def update_snippet(snippet_id: snippet.id, admin_mode: false, params: {})
put api("/projects/#{snippet.project.id}/snippets/#{snippet_id}", admin, admin_mode: admin_mode), params: params
end
end
describe 'DELETE /projects/:project_id/snippets/:id/' do
let_it_be(:snippet, refind: true) { public_snippet }
let(:path) { "/projects/#{snippet.project.id}/snippets/#{snippet.id}/" }
it_behaves_like 'DELETE request permissions for admin mode'
it 'deletes snippet' do
delete api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin)
delete api(path, admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:no_content)
end
it 'returns 404 for invalid snippet id' do
delete api("/projects/#{snippet.project.id}/snippets/#{non_existing_record_id}", admin)
delete api("/projects/#{snippet.project.id}/snippets/#{non_existing_record_id}", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('404 Snippet Not Found')
end
it_behaves_like '412 response' do
let(:request) { api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin, admin_mode: true) }
subject(:request) { api(path, admin, admin_mode: true) }
end
context 'with snippets disabled' do
it_behaves_like '403 response' do
let(:request) { delete api("/projects/#{project_no_snippets.id}/snippets/#{non_existing_record_id}", admin, admin_mode: true) }
subject(:request) { delete api("/projects/#{project_no_snippets.id}/snippets/#{non_existing_record_id}", admin, admin_mode: true) }
end
end
end
describe 'GET /projects/:project_id/snippets/:id/raw' do
let_it_be(:snippet) { create(:project_snippet, :repository, :public, author: admin, project: project) }
let(:path) { "/projects/#{snippet.project.id}/snippets/#{snippet.id}/raw" }
it_behaves_like 'GET request permissions for admin mode' do
let(:failed_status_code) { :ok }
end
it 'returns raw text' do
get api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/raw", admin)
get api(path, admin)
expect(response).to have_gitlab_http_status(:ok)
expect(response.media_type).to eq 'text/plain'
@ -404,19 +424,17 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
end
it 'returns 404 for invalid snippet id' do
get api("/projects/#{snippet.project.id}/snippets/#{non_existing_record_id}/raw", admin)
get api("/projects/#{snippet.project.id}/snippets/#{non_existing_record_id}/raw", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('404 Snippet Not Found')
end
it_behaves_like 'project snippet access levels' do
let(:path) { "/projects/#{snippet.project.id}/snippets/#{snippet.id}/raw" }
end
it_behaves_like 'project snippet access levels'
context 'with snippets disabled' do
it_behaves_like '403 response' do
let(:request) { get api("/projects/#{project_no_snippets.id}/snippets/#{non_existing_record_id}/raw", admin, admin_mode: true) }
subject(:request) { get api("/projects/#{project_no_snippets.id}/snippets/#{non_existing_record_id}/raw", admin, admin_mode: true) }
end
end
@ -424,19 +442,23 @@ RSpec.describe API::ProjectSnippets, feature_category: :source_code_management d
let_it_be(:snippet_with_empty_repo) { create(:project_snippet, :empty_repo, author: admin, project: project) }
let_it_be(:admin_mode) { snippet.author.admin? }
subject { get api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/raw", snippet.author, admin_mode: admin_mode) }
subject { get api(path, snippet.author, admin_mode: admin_mode) }
end
end
describe 'GET /projects/:project_id/snippets/:id/files/:ref/:file_path/raw' do
let_it_be(:snippet) { create(:project_snippet, :repository, author: admin, project: project) }
let(:path) { "/projects/#{snippet.project.id}/snippets/#{snippet.id}/files/master/%2Egitattributes/raw" }
it_behaves_like 'GET request permissions for admin mode' do
let(:failed_status_code) { :not_found }
end
it_behaves_like 'raw snippet files' do
let(:api_path) { "/projects/#{snippet.project.id}/snippets/#{snippet_id}/files/#{ref}/#{file_path}/raw" }
end
it_behaves_like 'project snippet access levels' do
let(:path) { "/projects/#{snippet.project.id}/snippets/#{snippet.id}/files/master/%2Egitattributes/raw" }
end
it_behaves_like 'project snippet access levels'
end
end

File diff suppressed because it is too large Load Diff

View File

@ -73,6 +73,14 @@ RSpec.describe Issuable::Callbacks::Milestone, feature_category: :team_planning
end
end
context "when milestone_id is '0'" do
let(:params) { { milestone_id: 0 } }
it "unsets the issuable's milestone" do
expect { callback.after_initialize }.to change { issuable.milestone }.from(project_milestone).to(nil)
end
end
context 'when milestone_id is not given' do
let(:params) { {} }

View File

@ -357,13 +357,7 @@ RSpec.configure do |config|
# The ongoing implementation of Admin Mode for API is behind the :admin_mode_for_api feature flag.
# All API specs will be adapted continuously. The following list contains the specs that have not yet been adapted.
# The feature flag is disabled for these specs as long as they are not yet adapted.
admin_mode_for_api_feature_flag_paths = %w[
./spec/requests/api/project_export_spec.rb
./spec/requests/api/project_repository_storage_moves_spec.rb
./spec/requests/api/project_snapshots_spec.rb
./spec/requests/api/project_snippets_spec.rb
./spec/requests/api/projects_spec.rb
]
admin_mode_for_api_feature_flag_paths = %w[]
if example.metadata[:file_path].start_with?(*admin_mode_for_api_feature_flag_paths)
stub_feature_flags(admin_mode_for_api: false)

View File

@ -34,5 +34,14 @@ RSpec.shared_examples 'issuable update endpoint' do
expect(json_response['labels']).to include '&'
expect(json_response['labels']).to include '?'
end
it 'clears milestone when milestone_id=0' do
entity.update!(milestone: milestone)
put api(url, user), params: { milestone_id: 0 }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['milestone']).to be_nil
end
end
end