Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
4a60d08bd5
commit
62798ed33c
|
|
@ -1 +1 @@
|
|||
81e439b712f147dc54bf0659857c4a038e9999b3
|
||||
9255b4522513ee91d7fef535137d57deb498bbf8
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
4.2.0
|
||||
4.3.0
|
||||
|
|
|
|||
4
Gemfile
4
Gemfile
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ export default () => {
|
|||
showTimelineViewToggle,
|
||||
reportAbusePath: notesDataset.reportAbusePath,
|
||||
newCommentTemplatePath: notesDataset.newCommentTemplatePath,
|
||||
issuableId: noteableData.id,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -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="
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -147,7 +147,8 @@ module Gitlab
|
|||
|
||||
{
|
||||
name: project.name,
|
||||
full_path: project.full_path
|
||||
full_path: project.full_path,
|
||||
type: namespace.type
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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) { {} }
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue