Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-07-24 03:10:24 +00:00
parent 8be0db711f
commit fb8ebc84b8
52 changed files with 434 additions and 176 deletions

View File

@ -1,6 +1,5 @@
For guidance on the overall deprecations, removals and breaking changes workflow, please visit [Breaking changes, deprecations, and removing features](https://about.gitlab.com/handbook/product/gitlab-the-product/#deprecations-removals-and-breaking-changes)
<!-- Use this template as a starting point for deprecations. -->
<!-- For guidance on the overall deprecations, removals and breaking changes workflow, please visit [Breaking changes, deprecations, and removing features](https://handbook.gitlab.com/handbook/marketing/blog/release-posts/#deprecations-removals-and-breaking-changes). -->
### Deprecation Summary
@ -15,12 +14,17 @@ The description of the deprecation should state what actions the user should tak
-->
### Breaking Change
### Breaking Change?
<!-- Does this MR contain a breaking change? If yes:
- Add the ~"breaking change" label to this issue.
- Add instructions for how users can update their workflow. -->
<!--
/label ~"breaking change"
-->
### Affected Topology
<!--
@ -37,41 +41,16 @@ Which tier is this feature available in?
* Ultimate
-->
### Checklists
**Labels**
- [ ] This issue is labeled ~deprecation, and with the relevant `~devops::`, `~group::`, and `~Category:` labels.
- [ ] This issue is labeled ~"breaking change" if the removal of the deprecated item will be a [breaking change](https://about.gitlab.com/handbook/product/gitlab-the-product/#examples-of-breaking-changes).
**Timeline**
Please add links to the relevant merge requests.
- As soon as possible, but no later than the third milestone preceding the major release (for example, given the following release schedule: `14.8, 14.9, 14.10, 15.0` `14.8` is the third milestone preceding the major release):
- [ ] A [deprecation announcement entry](https://about.gitlab.com/handbook/marketing/blog/release-posts/#creating-the-announcement) has been created so the deprecation will appear in release posts and on the [general deprecation page](https://docs.gitlab.com/ee/update/deprecations).
- [ ] Documentation has been updated to mark the feature as [deprecated](https://docs.gitlab.com/ee/development/documentation/versions.html#deprecations-and-removals).
- [ ] On or before the major milestone: A [removal entry](https://about.gitlab.com/handbook/marketing/blog/release-posts/#creating-the-announcement-1) has been created so the removal will appear on the [removals by milestones](https://docs.gitlab.com/ee/update/removals) page and be announced in the release post.
- On the major milestone:
- [ ] The deprecated item has been removed.
- [ ] If the removal of the deprecated item is a [breaking change](https://about.gitlab.com/handbook/product/gitlab-the-product/#examples-of-breaking-changes), the merge request is labeled ~"breaking change".
**Mentions**
- [ ] Your stage's stable counterparts have been `@mentioned` on this issue. For example, Customer Support, Customer Success (Technical Account Manager), Product Marketing Manager.
- To see who the stable counterparts are for a product team visit [product categories](https://about.gitlab.com/handbook/product/categories/)
- If there is no stable counterpart listed for Sales/CS please mention `@timtams`
- If there is no stable counterpart listed for Support please mention `@gitlab-com/support/managers`
- If there is no stable counterpart listed for Marketing please mention `@cfoster3`
- [ ] Your GPM has been `@mentioned` so that they are aware of planned deprecations. The goal is to have reviews happen at least two releases before the final removal of the feature or introduction of a breaking change.
<!-- Choose the Pricing Tier(s) -->
/label ~"GitLab Free" ~"GitLab Premium" ~"GitLab Ultimate"
### Deprecation Milestone
<!-- In which milestone will this deprecation be announced ? -->
<!-- In which milestone will this deprecation be announced? -->
### Planned Removal Milestone
<!-- In which milestone will the feature or functionality be removed and announced? -->
<!-- In which milestone will the feature or functionality be removed? -->
### Links
@ -83,19 +62,47 @@ issues and MRs related to this deprecation/removal to this issue. This can inclu
issues that were created ahead of time, and the MRs doing the actual deprecation/removal work.
-->
### Checklists
<details><summary>Click to expand</summary>
**Labels**
<!-- Populate the Section, Group, and Category -->
/label ~devops:: ~group: ~Category:
- [ ] This issue is labeled ~deprecation, and with the relevant `~devops::`, `~group::`, and `~Category:` labels.
- [ ] This issue is labeled ~"breaking change" if the removal of the deprecated item will be a [breaking change](https://about.gitlab.com/handbook/product/gitlab-the-product/#examples-of-breaking-changes).
**Timeline**
Please add links to the relevant merge requests.
- As soon as possible, but no later than the third milestone preceding the major release (for example, given the following release schedule: `14.8, 14.9, 14.10, 15.0` `14.8` is the third milestone preceding the major release):
- [ ] A [deprecation announcement entry](https://about.gitlab.com/handbook/marketing/blog/release-posts/#creating-the-announcement) has been created so the deprecation will appear in release posts and on the [general deprecation page](https://docs.gitlab.com/ee/update/deprecations).
- [ ] Documentation has been updated to mark the feature as [deprecated](https://docs.gitlab.com/ee/development/documentation/versions.html#deprecations-and-removals).
- On the major milestone:
- [ ] The deprecated item has been removed.
- [ ] If the removal of the deprecated item is a [breaking change](https://about.gitlab.com/handbook/product/gitlab-the-product/#examples-of-breaking-changes), the merge request is labeled ~"breaking change".
**Mentions**
- [ ] Your stage's stable counterparts have been `@mentioned` on this issue. For example, Customer Support, Customer Success (Technical Account Manager), Product Marketing Manager.
- To see who the stable counterparts are for a product team visit [product categories](https://handbook.gitlab.com/handbook/product/categories/)
- If there is no stable counterpart listed for Sales/CS please mention `@timtams`
- If there is no stable counterpart listed for Support please mention `@gitlab-com/support/managers`
- If there is no stable counterpart listed for Marketing please mention `@cfoster3`
- [ ] Your GPM or Director has been `@mentioned` so that they are aware of planned deprecations.
</details>
<!-- Label reminders - you should have one of each of the following labels.
Use the following resources to find the appropriate labels:
- https://gitlab.com/gitlab-org/gitlab/-/labels
- https://about.gitlab.com/handbook/product/categories/features/
-->
<!-- Populate the Section, Group, and Category -->
/label ~devops:: ~group: ~Category:
<!-- Choose the Pricing Tier(s) -->
/label ~"GitLab Free" ~"GitLab Premium" ~"GitLab Ultimate"
<!-- Identifies that this Issue is related to deprecating a feature -->
/label ~"deprecation"
<!-- Add the ~"breaking change" label to this issue if necessary -->

View File

@ -85,9 +85,9 @@ $avatar-sizes: (
width: 40px;
height: 40px;
padding: 0;
background: $gray-10;
background: var(--gl-background-color-subtle);
overflow: hidden;
box-shadow: inset 0 0 0 1px rgba($gray-950, $gl-avatar-border-opacity);
box-shadow: inset 0 0 0 1px var(--gl-avatar-border-color-default);
&.avatar-inline {
float: none;
@ -116,8 +116,8 @@ $avatar-sizes: (
.identicon {
text-align: center;
vertical-align: top;
color: $gray-900;
background-color: $gray-50;
color: var(--gl-text-color-strong);
background-color: var(--gl-avatar-fallback-background-color-neutral);
// Sizes
@each $size, $size-config in $avatar-sizes {
@ -208,7 +208,7 @@ $avatar-sizes: (
&.user-popover-cannot-merge {
.popover-header {
background-color: $orange-50;
background-color: var(--gl-feedback-warning-background-color);
}
}
}

View File

@ -119,6 +119,7 @@ The following table lists the valid `objects` that can be used:
| `dependency_proxy` | [Dependency Proxy](packages/dependency_proxy.md) |
| `terraform_state` | [Terraform state files](terraform_state.md) |
| `pages` | [Pages](pages/index.md) |
| `ci_secure_files` | [Secure files](secure_files.md) |
Within each object type, three parameters can be defined:
@ -166,11 +167,11 @@ supported by the consolidated form, refer to the following guides:
| Object storage type | Supported by consolidated form? |
|---------------------|------------------------------------------|
| [Secure Files](secure_files.md#using-object-storage) | **{dotted-circle}** No |
| [Backups](../administration/backup_restore/backup_gitlab.md#upload-backups-to-a-remote-cloud-storage) | **{dotted-circle}** No |
| [Container registry](packages/container_registry.md#use-object-storage) (optional feature) | **{dotted-circle}** No |
| [Mattermost](https://docs.mattermost.com/configure/file-storage-configuration-settings.html)| **{dotted-circle}** No |
| [Autoscale runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional for improved performance) | **{dotted-circle}** No |
| [Secure Files](secure_files.md#using-object-storage) | **{check-circle}** Yes |
| [Job artifacts](job_artifacts.md#using-object-storage) including archived job logs | **{check-circle}** Yes |
| [LFS objects](lfs/index.md#storing-lfs-objects-in-remote-object-storage) | **{check-circle}** Yes |
| [Uploads](uploads.md#using-object-storage) | **{check-circle}** Yes |
@ -483,6 +484,7 @@ The following example uses AWS S3 to enable object storage for all supported ser
gitlab_rails['object_store']['objects']['packages']['bucket'] = 'gitlab-packages'
gitlab_rails['object_store']['objects']['dependency_proxy']['bucket'] = 'gitlab-dependency-proxy'
gitlab_rails['object_store']['objects']['terraform_state']['bucket'] = 'gitlab-terraform-state'
gitlab_rails['object_store']['objects']['ci_secure_files']['bucket'] = 'gitlab-ci-secure-files'
gitlab_rails['object_store']['objects']['pages']['bucket'] = 'gitlab-pages'
```

View File

@ -97,13 +97,14 @@ DETAILS:
Instead of storing Secure Files on disk, you should use [one of the supported object storage options](object_storage.md#supported-object-storage-providers).
This configuration relies on valid credentials to be configured already.
[Read more about using object storage with GitLab](object_storage.md).
### Consolidated object storage
NOTE:
This feature is not supported by consolidated object storage configuration.
Adding support is proposed in [issue 414673](https://gitlab.com/gitlab-org/gitlab/-/issues/414673).
> - Support for consolidated object storage was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/149873) in GitLab 17.0.
### Object storage settings
Using the [consolidated form](object_storage.md#configure-a-single-storage-connection-for-all-object-types-consolidated-form)
of the object storage is recommended.
### Storage-specific object storage
The following settings are:
@ -120,7 +121,9 @@ The following settings are:
See [the available connection settings for different providers](object_storage.md#configure-the-connection-settings).
**For Linux package installations:**
::Tabs
:::TabTitle Linux package (Omnibus)
1. Edit `/etc/gitlab/gitlab.rb` and add the following lines, but using
the values you want:
@ -147,10 +150,15 @@ See [the available connection settings for different providers](object_storage.m
}
```
1. Save the file and [reconfigure GitLab](restart_gitlab.md#reconfigure-a-linux-package-installation).
1. Save the file and reconfigure GitLab:
```shell
sudo gitlab-ctl reconfigure
```
1. [Migrate any existing local states to the object storage](#migrate-to-object-storage).
**For self-compiled installations**
:::TabTitle Self-compiled (source)
1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines:
@ -167,9 +175,20 @@ See [the available connection settings for different providers](object_storage.m
region: eu-central-1
```
1. Save the file and [restart GitLab](restart_gitlab.md#self-compiled-installations) for the changes to take effect.
1. Save the file and restart GitLab:
```shell
# For systems running systemd
sudo systemctl restart gitlab.target
# For systems running SysV init
sudo service gitlab restart
```
1. [Migrate any existing local states to the object storage](#migrate-to-object-storage).
::EndTabs
### Migrate to object storage
> - [Introduced](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/readme/-/issues/125) in GitLab 16.1.

View File

@ -73,6 +73,10 @@ In each place, if you hover over the failed job you can see the reason it failed
You can also see the reason it failed on the Job detail page.
### Troubleshoot a failed job with root cause analysis
You can use root cause analysis in GitLab Duo Chat to [troubleshoot failed CI/CD jobs](../../user/gitlab_duo_chat/examples.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis).
## The order of jobs in a pipeline
The order of jobs in a pipeline depends on the type of pipeline graph.

View File

@ -116,7 +116,7 @@ Prerequisites:
- You must be an administrator.
1. Sign in to the [GitLab Customers Portal](https://customers.gitlab.com/).
1. On the **GitLab Duo Pro** section of your subscription card click **Add seats** button.
1. On the **GitLab Duo Pro** section of your subscription card select **Add seats**.
1. Enter the number of seats. The amount cannot be higher than the number of seats in the subscription.
1. Review the **Purchase summary** section.
1. From the **Payment method** dropdown list, select your payment method.

View File

@ -74,30 +74,6 @@ is displayed.
Provide feedback on this experimental feature in [issue 416833](https://gitlab.com/gitlab-org/gitlab/-/issues/416833).
## Troubleshoot failed CI/CD jobs with Root cause analysis
DETAILS:
**Tier:** For a limited time, Ultimate. In the future, [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md).
**Offering:** GitLab.com
**Status:** Experiment
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123692) in GitLab 16.2 as an [experiment](../../policy/experiment-beta-support.md#experiment).
Determine the root cause of a CI/CD job failure by analyzing the logs.
Prerequisites:
- You must belong to at least one group with the [experiment and beta features setting](turn_on_off.md#turn-on-beta-and-experimental-features) enabled.
- You must have permission to view the CI/CD job.
To view root cause analysis:
1. Open a merge request.
1. On the **Pipelines** tab, select the failed CI/CD job.
1. Above the job output, select **Troubleshoot**.
An analysis of the reasons for the failure is displayed.
## Summarize an issue with Issue description generation
DETAILS:

View File

@ -43,6 +43,19 @@ the [Duo Chat examples](../gitlab_duo_chat_examples.md).
For self-managed, the models also depend on your GitLab version.
For the most benefit, use the latest GitLab version whenever possible.
#### Root cause analysis
DETAILS:
**Tier:** Ultimate with [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md).
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123692) in GitLab 16.2 as an [experiment](../../policy/experiment-beta-support.md#experiment) on GitLab.com.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/441681) and moved to GitLab Duo Chat in GitLab 17.3.
- Helps you determine the root cause for a CI/CD job failure by analyzing the logs.
- LLM: Anthropic's [Claude 3.5 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
- [View documentation](../gitlab_duo_chat/examples.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis)
### Code Suggestions
DETAILS:
@ -190,7 +203,7 @@ DETAILS:
- Helps you determine the root cause for a CI/CD job failure by analyzing the logs.
- LLM: Vertex AI Codey [`text-bison`](https://console.cloud.google.com/vertex-ai/publishers/google/model-garden/text-bison)
- [View documentation](experiments.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis).
- [View documentation](../gitlab_duo_chat/examples.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis).
### Vulnerability resolution

View File

@ -55,8 +55,8 @@ you need to configure CI/CD.
Please show a .gitignore and .gitlab-ci.yml configuration for a C# project.
```
- If your CI/CD job fails, [Root Cause Analysis](../../user/gitlab_duo/experiments.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis)
can help understand the problem. Alternatively, you can copy the error message into
- If your CI/CD job fails, use root cause analysis to [troubleshoot failed CI/CD jobs](../gitlab_duo_chat/examples.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis).
Alternatively, you can copy the error message into
GitLab Duo Chat, and ask for help:
```markdown

View File

@ -80,7 +80,35 @@ You can also ask to explain specific job errors by copy-pasting the error messag
- `Please explain this CI/CD job error message in the context of a Go project: build.sh: line 14: go command not found`
Alternatively, you can use [root cause analysis in CI/CD](../gitlab_duo/experiments.md#troubleshoot-failed-cicd-jobs-with-root-cause-analysis).
Alternatively, you can use root cause analysis to [troubleshoot failed CI/CD jobs](#troubleshoot-failed-cicd-jobs-with-root-cause-analysis).
## Troubleshoot failed CI/CD jobs with root cause analysis
DETAILS:
**Tier:** Ultimate with [GitLab Duo Enterprise](../../subscriptions/subscription-add-ons.md).
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123692) in GitLab 16.2 as an [experiment](../../policy/experiment-beta-support.md#experiment) on GitLab.com.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/441681) and moved to GitLab Duo Chat in GitLab 17.3.
You can ask GitLab Duo Chat to determine the root cause of a CI/CD job failure by analyzing the logs.
Prerequisites:
- Have permission to view the CI/CD job.
- Have a paid GitLab Duo Enterprise seat.
To troubleshoot a failed CI/CD job:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Build > Jobs**.
1. Select the failed CI/CD job.
1. From the job log page, do one of the following:
- Above the job log, select **Troubleshoot**.
- Open GitLab Duo Chat and type `/troubleshoot`.
An analysis of the reasons for the failure and an example fix is displayed.
## Explain a vulnerability
@ -276,4 +304,5 @@ Use the following commands to quickly accomplish specific tasks.
| /explain | [Explain code](../gitlab_duo_chat/examples.md#explain-code-in-the-ide) |
| /vulnerability_explain | [Explain current vulnerability](../gitlab_duo/index.md#vulnerability-explanation) |
| /refactor | [Refactor the code](../gitlab_duo_chat/examples.md#refactor-code-in-the-ide) |
| /troubleshoot | [Troubleshoot failed CI/CD jobs with root cause analysis](#troubleshoot-failed-cicd-jobs-with-root-cause-analysis) |
| /fix | [Fix the code](../gitlab_duo_chat/examples.md#fix-code-in-the-ide) |

View File

@ -28,11 +28,17 @@ module CsvBuilder
# * +collection+ - The data collection to be used
# * +header_to_value_hash+ - A hash of 'Column Heading' => 'value_method'.
# * +associations_to_preload+ - An array of records to preload with a batch of records.
# * +replace_newlines+ - default: false - If true, replaces newline characters with a literal "\n"
#
# The value method will be called once for each object in the collection, to
# determine the value for that row. It can either be the name of a method on
# the object, or a lamda to call passing in the object.
def self.new(collection, header_to_value_hash, associations_to_preload = [])
CsvBuilder::Builder.new(collection, header_to_value_hash, associations_to_preload)
def self.new(collection, header_to_value_hash, associations_to_preload = [], replace_newlines: false)
CsvBuilder::Builder.new(
collection,
header_to_value_hash,
associations_to_preload,
replace_newlines: replace_newlines
)
end
end

View File

@ -6,12 +6,13 @@ module CsvBuilder
attr_reader :rows_written
def initialize(collection, header_to_value_hash, associations_to_preload = [])
def initialize(collection, header_to_value_hash, associations_to_preload = [], replace_newlines: false)
@header_to_value_hash = header_to_value_hash
@collection = collection
@truncated = false
@rows_written = 0
@associations_to_preload = associations_to_preload
@replace_newlines = replace_newlines
end
# Renders the csv to a string
@ -78,13 +79,19 @@ module CsvBuilder
def row(object)
attributes.map do |attribute|
if attribute.respond_to?(:call)
excel_sanitize(attribute.call(object))
elsif object.is_a?(Hash)
excel_sanitize(object[attribute])
else
excel_sanitize(object.public_send(attribute)) # rubocop:disable GitlabSecurity/PublicSend
end
data = if attribute.respond_to?(:call)
attribute.call(object)
elsif object.is_a?(Hash)
object[attribute]
else
object.public_send(attribute) # rubocop:disable GitlabSecurity/PublicSend -- Not user input
end
next if data.nil?
data = data.gsub("\n", '\n') if data.is_a?(String) && @replace_newlines
excel_sanitize(data)
end
end
@ -104,7 +111,6 @@ module CsvBuilder
end
def excel_sanitize(line)
return if line.nil?
return line unless line.is_a?(String) && line.match?(UNSAFE_EXCEL_PREFIX)
["'", line].join

View File

@ -7,7 +7,7 @@ RSpec.describe CsvBuilder::Gzip do
let(:event_2) { double(title: 'Added sugar', description: 'Just a pinch') }
let(:items) { [event_1, event_2] }
subject(:builder) { described_class.new(items, 'Title' => 'title', 'Description' => 'description') }
subject(:builder) { described_class.new(items, { 'Title' => 'title', 'Description' => 'description' }) }
describe '#render' do
it 'returns yields a tempfile' do

View File

@ -7,7 +7,7 @@ RSpec.describe CsvBuilder::Stream do
let(:event_2) { double(title: 'Added sugar', description: 'Just a pinch') }
let(:fake_relation) { described_class::FakeRelation.new([event_1, event_2]) }
subject(:builder) { described_class.new(fake_relation, 'Title' => 'title', 'Description' => 'description') }
subject(:builder) { described_class.new(fake_relation, { 'Title' => 'title', 'Description' => 'description' }) }
describe '#render' do
before do

View File

@ -7,7 +7,7 @@ RSpec.describe CsvBuilder do
end
let(:subject) do
described_class.new(enumerable, **header_to_value_hash)
described_class.new(enumerable, header_to_value_hash)
end
shared_examples 'csv builder examples' do
@ -113,6 +113,41 @@ RSpec.describe CsvBuilder do
end
end
shared_examples 'builder that replaces newlines' do
let(:object) { double(title: "title", description: "Line 1\n\nLine 2") }
let(:header_to_value_hash) { { 'Title' => 'title', 'Description' => 'description' } }
let(:items) { [object] }
it 'does not replace newlines by default' do
expect(csv_data).to eq("Title,Description\ntitle,\"Line 1\n\nLine 2\"\n")
end
context 'when replace_newlines is set to true' do
let(:subject) { described_class.new(enumerable, header_to_value_hash, replace_newlines: true) }
it 'replaces newlines with a literal "\n"' do
expect(csv_data).to eq("Title,Description\ntitle,Line 1\\n\\nLine 2\n")
end
end
context 'when line is nil' do
let(:object) { double(title: "title", description: nil) }
it 'gracefully generates CSV' do
expect(csv_data).to eq("Title,Description\ntitle,\n")
end
end
context 'when data is not a string' do
let(:object) { double(title: "title", created_at: Date.new(2001, 2, 3)) }
let(:header_to_value_hash) { { 'Title' => 'title', 'Created At' => 'created_at' } }
it 'gracefully generates CSV' do
expect(csv_data).to eq("Title,Created At\ntitle,2001-02-03\n")
end
end
end
context 'when ActiveRecord::Relation like object is given' do
let(:object) { double(question: :answer) }
let(:enumerable) { described_class::FakeRelation.new(items) }
@ -129,6 +164,7 @@ RSpec.describe CsvBuilder do
it_behaves_like 'csv builder examples'
it_behaves_like 'excel sanitization'
it_behaves_like 'builder that replaces newlines'
it_behaves_like 'csv builder with truncation ability' do
let(:big_object) { double(question: 'Long' * 1024) }
let(:question_value) { big_object.question }
@ -141,6 +177,7 @@ RSpec.describe CsvBuilder do
it_behaves_like 'csv builder examples'
it_behaves_like 'excel sanitization'
it_behaves_like 'builder that replaces newlines'
it_behaves_like 'csv builder with truncation ability' do
let(:big_object) { double(question: 'Long' * 1024) }
let(:question_value) { big_object.question }
@ -155,6 +192,7 @@ RSpec.describe CsvBuilder do
end
it_behaves_like 'csv builder examples'
it_behaves_like 'builder that replaces newlines'
it_behaves_like 'excel sanitization' do
let(:dangerous_title) { { title: "=cmd|' /C calc'!A0 title", description: "*safe_desc" } }

View File

@ -1,5 +1,5 @@
variables:
DAST_AUTO_DEPLOY_IMAGE_VERSION: 'v2.99.0'
DAST_AUTO_DEPLOY_IMAGE_VERSION: 'v2.100.0'
.dast-auto-deploy:
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${DAST_AUTO_DEPLOY_IMAGE_VERSION}"

View File

@ -1,5 +1,5 @@
variables:
AUTO_DEPLOY_IMAGE_VERSION: 'v2.99.0'
AUTO_DEPLOY_IMAGE_VERSION: 'v2.100.0'
.auto-deploy:
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}"

View File

@ -1,5 +1,5 @@
variables:
AUTO_DEPLOY_IMAGE_VERSION: 'v2.99.0'
AUTO_DEPLOY_IMAGE_VERSION: 'v2.100.0'
.auto-deploy:
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}"

View File

@ -13,6 +13,19 @@ module Gitlab
MANAGEMENT_LEASE_KEY = 'database_partition_management_%s'
RETAIN_DETACHED_PARTITIONS_FOR = 1.week
LOCK_RETRIES_TIMING_CONFIGURATION = [
[0.1.seconds, 0.05.seconds],
[0.1.seconds, 0.05.seconds],
[0.2.seconds, 0.05.seconds],
[0.3.seconds, 0.10.seconds],
[0.4.seconds, 0.15.seconds],
[0.5.seconds, 2.seconds],
[0.5.seconds, 2.seconds],
[0.5.seconds, 2.seconds],
[0.5.seconds, 2.seconds],
[1.second, 5.seconds]
].freeze
def initialize(model, connection: nil)
@model = model
@connection = connection || model.connection
@ -135,6 +148,7 @@ module Gitlab
def with_lock_retries(&block)
Gitlab::Database::WithLockRetries.new(
timing_configuration: LOCK_RETRIES_TIMING_CONFIGURATION * 2,
klass: self.class,
logger: Gitlab::AppLogger,
connection: connection

View File

@ -70,10 +70,12 @@ module Gitlab
finder.execute(gitaly_pagination: true).tap do |records|
total = finder.total
per_page = params[:per_page].presence || Kaminari.config.default_per_page
total_pages = (total / per_page.to_f).ceil
next_page = total_pages > 1 ? 2 : nil
Gitlab::Pagination::OffsetHeaderBuilder.new(
request_context: request_context, per_page: per_page, page: 1, next_page: 2,
total: total, total_pages: (total / per_page) + 1
request_context: request_context, per_page: per_page, page: 1, next_page: next_page,
total: total, total_pages: total_pages
).execute
end
end

View File

@ -2,18 +2,21 @@
require 'spec_helper'
RSpec.describe Mutations::Groups::Update do
RSpec.describe Mutations::Groups::Update, feature_category: :api do
include GraphqlHelpers
using RSpec::Parameterized::TableSyntax
let_it_be_with_reload(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let(:params) { { full_path: group.full_path } }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
specify { expect(described_class).to require_graphql_authorizations(:admin_group) }
describe '#resolve' do
subject { described_class.new(object: group, context: { current_user: user }, field: nil).resolve(**params) }
subject { described_class.new(object: group, context: context, field: nil).resolve(**params) }
shared_examples 'updating the group shared runners setting' do
it 'updates the group shared runners setting' do

View File

@ -2,7 +2,9 @@
require 'spec_helper'
RSpec.describe Mutations::IncidentManagement::TimelineEvent::Create do
RSpec.describe Mutations::IncidentManagement::TimelineEvent::Create, feature_category: :api do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:incident) { create(:incident, project: project) }
@ -11,6 +13,8 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Create do
end
let(:args) { { note: 'note', occurred_at: Time.current } }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: current_user }) }
specify { expect(described_class).to require_graphql_authorizations(:admin_incident_management_timeline_event) }
@ -147,7 +151,7 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Create do
private
def mutation_for(project, user)
described_class.new(object: project, context: { current_user: user }, field: nil)
def mutation_for(project, _user)
described_class.new(object: project, context: context, field: nil)
end
end

View File

@ -2,13 +2,16 @@
require 'spec_helper'
RSpec.describe Mutations::IncidentManagement::TimelineEvent::Destroy do
RSpec.describe Mutations::IncidentManagement::TimelineEvent::Destroy, feature_category: :api do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:incident) { create(:incident, project: project) }
let(:timeline_event) { create(:incident_management_timeline_event, incident: incident, project: project) }
let(:args) { { id: timeline_event.to_global_id } }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: current_user }) }
specify { expect(described_class).to require_graphql_authorizations(:admin_incident_management_timeline_event) }
@ -60,7 +63,7 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Destroy do
private
def mutation_for(project, user)
described_class.new(object: project, context: { current_user: user }, field: nil)
def mutation_for(project, _user)
described_class.new(object: project, context: context, field: nil)
end
end

View File

@ -2,7 +2,8 @@
require 'spec_helper'
RSpec.describe Mutations::IncidentManagement::TimelineEvent::PromoteFromNote do
RSpec.describe Mutations::IncidentManagement::TimelineEvent::PromoteFromNote, feature_category: :api do
include GraphqlHelpers
include NotesHelper
let_it_be(:current_user) { create(:user) }
@ -15,6 +16,8 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::PromoteFromNote do
let_it_be(:alert_comment) { create(:note, project: project, noteable: alert) }
let(:args) { { note_id: comment.to_global_id.to_s } }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: current_user }) }
specify { expect(described_class).to require_graphql_authorizations(:admin_incident_management_timeline_event) }
@ -82,7 +85,7 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::PromoteFromNote do
private
def mutation_for(project, user)
described_class.new(object: project, context: { current_user: user }, field: nil)
def mutation_for(project, _user)
described_class.new(object: project, context: context, field: nil)
end
end

View File

@ -2,7 +2,9 @@
require 'spec_helper'
RSpec.describe Mutations::IncidentManagement::TimelineEvent::Update do
RSpec.describe Mutations::IncidentManagement::TimelineEvent::Update, feature_category: :api do
include GraphqlHelpers
let_it_be(:developer) { create(:user) }
let_it_be(:reporter) { create(:user) }
let_it_be(:project) { create(:project, developers: developer, reporters: reporter) }
@ -30,6 +32,9 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Update do
}
end
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: current_user }) }
let(:note) { 'Updated Note' }
let(:timeline_event_id) { GitlabSchema.id_from_object(timeline_event).to_s }
let(:occurred_at) { 1.minute.ago }
@ -154,7 +159,7 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Update do
private
def mutation_for(user)
described_class.new(object: nil, context: { current_user: user }, field: nil)
def mutation_for(_user)
described_class.new(object: nil, context: context, field: nil)
end
end

View File

@ -2,11 +2,14 @@
require 'spec_helper'
RSpec.describe Mutations::IncidentManagement::TimelineEventTag::Create do
RSpec.describe Mutations::IncidentManagement::TimelineEventTag::Create, feature_category: :api do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
let_it_be_with_reload(:project) { create(:project, maintainers: current_user) }
let(:args) { { name: 'Test tag 1' } }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: current_user }) }
specify { expect(described_class).to require_graphql_authorizations(:admin_incident_management_timeline_event_tag) }
@ -42,7 +45,7 @@ RSpec.describe Mutations::IncidentManagement::TimelineEventTag::Create do
private
def mutation_for(project, user)
described_class.new(object: project, context: { current_user: user }, field: nil)
def mutation_for(project, _user)
described_class.new(object: project, context: context, field: nil)
end
end

View File

@ -2,7 +2,9 @@
require 'spec_helper'
RSpec.describe Mutations::Issues::Create do
RSpec.describe Mutations::Issues::Create, feature_category: :api do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:assignee1) { create(:user) }
@ -40,7 +42,9 @@ RSpec.describe Mutations::Issues::Create do
}
end
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
let(:mutation) { described_class.new(object: nil, context: context, field: nil) }
let(:mutated_issue) { subject[:issue] }
specify { expect(described_class).to require_graphql_authorizations(:create_issue) }

View File

@ -3,6 +3,7 @@
require 'spec_helper'
RSpec.describe Mutations::Issues::LinkAlerts, feature_category: :incident_management do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:guest) { create(:user, guest_of: project) }
let_it_be(:developer) { create(:user, developer_of: project) }
@ -10,7 +11,9 @@ RSpec.describe Mutations::Issues::LinkAlerts, feature_category: :incident_manage
let_it_be(:alert1) { create(:alert_management_alert, project: project) }
let_it_be(:alert2) { create(:alert_management_alert, project: project) }
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
let(:mutation) { described_class.new(object: nil, context: context, field: nil) }
specify { expect(described_class).to require_graphql_authorizations(:update_issue, :admin_issue) }

View File

@ -2,12 +2,17 @@
require 'spec_helper'
RSpec.describe Mutations::Issues::Move do
RSpec.describe Mutations::Issues::Move, feature_category: :api do
include GraphqlHelpers
let_it_be(:issue) { create(:issue) }
let_it_be(:user) { create(:user) }
let_it_be(:target_project) { create(:project) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
describe '#resolve' do
subject(:resolve) { mutation.resolve(project_path: issue.project.full_path, iid: issue.iid, target_project_path: target_project.full_path) }

View File

@ -2,13 +2,17 @@
require 'spec_helper'
RSpec.describe Mutations::Issues::SetAssignees do
RSpec.describe Mutations::Issues::SetAssignees, feature_category: :api do
include GraphqlHelpers
context 'when the user does not have permissions' do
let_it_be(:issue) { create(:issue) }
let_it_be(:user) { create(:user) }
let_it_be(:assignee) { create(:user) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
describe '#resolve' do
subject do

View File

@ -2,12 +2,15 @@
require 'spec_helper'
RSpec.describe Mutations::Issues::SetConfidential do
RSpec.describe Mutations::Issues::SetConfidential, feature_category: :api do
include GraphqlHelpers
let(:project) { create(:project, :private) }
let(:issue) { create(:issue, project: project, assignees: [user]) }
let(:user) { create(:user) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
specify { expect(described_class).to require_graphql_authorizations(:update_issue) }

View File

@ -2,12 +2,16 @@
require 'spec_helper'
RSpec.describe Mutations::Issues::SetDueDate do
RSpec.describe Mutations::Issues::SetDueDate, feature_category: :api do
include GraphqlHelpers
let(:issue) { create(:issue, due_date: '2021-05-01') }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
let_it_be(:user) { create(:user) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
specify { expect(described_class).to require_graphql_authorizations(:update_issue) }

View File

@ -2,14 +2,18 @@
require 'spec_helper'
RSpec.describe Mutations::Issues::SetEscalationStatus do
RSpec.describe Mutations::Issues::SetEscalationStatus, feature_category: :api do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:issue, reload: true) { create(:incident, project: project) }
let_it_be(:escalation_status, reload: true) { create(:incident_management_issuable_escalation_status, issue: issue) }
let(:status) { :acknowledged }
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
let(:mutation) { described_class.new(object: nil, context: context, field: nil) }
describe '#resolve' do
let(:args) { { status: status } }

View File

@ -2,11 +2,15 @@
require 'spec_helper'
RSpec.describe Mutations::Issues::SetLocked do
RSpec.describe Mutations::Issues::SetLocked, feature_category: :api do
include GraphqlHelpers
let_it_be(:issue) { create(:issue) }
let_it_be(:user) { create(:user) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
specify { expect(described_class).to require_graphql_authorizations(:update_issue) }

View File

@ -2,13 +2,17 @@
require 'spec_helper'
RSpec.describe Mutations::Issues::SetSeverity do
RSpec.describe Mutations::Issues::SetSeverity, feature_category: :api do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:guest) { create(:user, guest_of: project) }
let_it_be(:reporter) { create(:user, reporter_of: project) }
let_it_be(:issue) { create(:incident, project: project) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let(:mutation) { described_class.new(object: nil, context: context, field: nil) }
specify { expect(described_class).to require_graphql_authorizations(:update_issue, :admin_issue) }

View File

@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe Mutations::Issues::UnlinkAlert, feature_category: :incident_management do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:another_project) { create(:project) }
let_it_be(:guest) { create(:user, guest_of: project) }
@ -11,7 +13,9 @@ RSpec.describe Mutations::Issues::UnlinkAlert, feature_category: :incident_manag
let_it_be(:external_alert) { create(:alert_management_alert, project: another_project) }
let_it_be(:issue) { create(:incident, project: project, alert_management_alerts: [internal_alert, external_alert]) }
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
let(:mutation) { described_class.new(object: nil, context: context, field: nil) }
specify { expect(described_class).to require_graphql_authorizations(:update_issue, :admin_issue) }

View File

@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe Mutations::Issues::Update, feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:project_label) { create(:label, project: project) }
@ -20,7 +22,9 @@ RSpec.describe Mutations::Issues::Update, feature_category: :team_planning do
}
end
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
let(:mutation) { described_class.new(object: nil, context: context, field: nil) }
let(:mutated_issue) { subject[:issue] }
specify { expect(described_class).to require_graphql_authorizations(:update_issue) }

View File

@ -2,7 +2,9 @@
require 'spec_helper'
RSpec.describe Mutations::Labels::Create do
RSpec.describe Mutations::Labels::Create, feature_category: :api do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
let(:attributes) do
@ -12,7 +14,9 @@ RSpec.describe Mutations::Labels::Create do
}
end
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
let(:mutation) { described_class.new(object: nil, context: context, field: nil) }
let(:mutated_label) { subject[:label] }
shared_examples 'create labels mutation' do

View File

@ -7,9 +7,11 @@ RSpec.describe Mutations::Members::BulkUpdateBase, feature_category: :groups_and
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group, owners: user) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
it 'raises a NotImplementedError error if the source_type method is called on the base class' do
mutation = described_class.new(context: { current_user: user }, object: nil, field: nil)
mutation = described_class.new(context: context, object: nil, field: nil)
expect { mutation.resolve(group_id: group.to_gid.to_s) }.to raise_error(NotImplementedError)
end

View File

@ -2,21 +2,16 @@
require 'spec_helper'
RSpec.describe Mutations::MergeRequests::Accept do
RSpec.describe Mutations::MergeRequests::Accept, feature_category: :api do
include GraphqlHelpers
include AfterNextHelpers
subject(:mutation) { described_class.new(context: context, object: nil, field: nil) }
let_it_be(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
let(:context) do
GraphQL::Query::Context.new(
query: query_double(schema: GitlabSchema),
values: { current_user: user }
)
end
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
subject(:mutation) { described_class.new(context: context, object: nil, field: nil) }
before do
project.repository.expire_all_method_caches

View File

@ -2,19 +2,14 @@
require 'spec_helper'
RSpec.describe Mutations::MergeRequests::Create do
RSpec.describe Mutations::MergeRequests::Create, feature_category: :api do
include GraphqlHelpers
subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
let(:user) { create(:user) }
let(:context) do
GraphQL::Query::Context.new(
query: query_double(schema: nil),
values: { current_user: user }
)
end
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
describe '#resolve' do
subject do

View File

@ -2,13 +2,16 @@
require 'spec_helper'
RSpec.describe Mutations::MergeRequests::SetAssignees do
RSpec.describe Mutations::MergeRequests::SetAssignees, feature_category: :api do
include GraphqlHelpers
context 'when the user does not have permissions' do
let_it_be(:merge_request) { create(:merge_request) }
let_it_be(:user) { create(:user) }
let_it_be(:assignee) { create(:user) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
describe '#resolve' do
subject do

View File

@ -2,11 +2,14 @@
require 'spec_helper'
RSpec.describe Mutations::MergeRequests::SetDraft do
RSpec.describe Mutations::MergeRequests::SetDraft, feature_category: :api do
include GraphqlHelpers
let_it_be(:merge_request) { create(:merge_request) }
let_it_be(:user) { create(:user) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
specify { expect(described_class).to require_graphql_authorizations(:update_merge_request) }

View File

@ -2,11 +2,15 @@
require 'spec_helper'
RSpec.describe Mutations::MergeRequests::SetLabels do
RSpec.describe Mutations::MergeRequests::SetLabels, feature_category: :api do
include GraphqlHelpers
let(:merge_request) { create(:merge_request) }
let(:user) { create(:user) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
specify { expect(described_class).to require_graphql_authorizations(:update_merge_request) }

View File

@ -2,11 +2,15 @@
require 'spec_helper'
RSpec.describe Mutations::MergeRequests::SetLocked do
RSpec.describe Mutations::MergeRequests::SetLocked, feature_category: :api do
include GraphqlHelpers
let(:merge_request) { create(:merge_request) }
let(:user) { create(:user) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
specify { expect(described_class).to require_graphql_authorizations(:update_merge_request) }

View File

@ -2,11 +2,15 @@
require 'spec_helper'
RSpec.describe Mutations::MergeRequests::SetMilestone do
RSpec.describe Mutations::MergeRequests::SetMilestone, feature_category: :api do
include GraphqlHelpers
let(:user) { create(:user) }
let(:project) { create(:project, :private) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project, assignees: [user]) }
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
let(:mutation) { described_class.new(object: nil, context: context, field: nil) }
let(:milestone) { create(:milestone, project: project) }
subject { mutation.resolve(project_path: project.full_path, iid: merge_request.iid, milestone: milestone) }

View File

@ -2,13 +2,17 @@
require 'spec_helper'
RSpec.describe Mutations::MergeRequests::SetReviewers do
RSpec.describe Mutations::MergeRequests::SetReviewers, feature_category: :api do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
let_it_be(:merge_request, reload: true) { create(:merge_request) }
let_it_be(:reviewer) { create(:user) }
let_it_be(:reviewer2) { create(:user) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
describe '#resolve' do
let(:reviewer_usernames) { [reviewer.username] }

View File

@ -3,10 +3,14 @@
require 'spec_helper'
RSpec.describe Mutations::MergeRequests::Update, feature_category: :team_planning do
include GraphqlHelpers
let(:merge_request) { create(:merge_request) }
let(:user) { create(:user) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
specify { expect(described_class).to require_graphql_authorizations(:update_merge_request) }

View File

@ -70,6 +70,11 @@ RSpec.describe Gitlab::Pagination::GitalyKeysetPager, feature_category: :source_
context 'when first page is requested' do
let(:branches) { [branch1, branch2, branch3] }
before do
allow(BranchesFinder).to receive(:===).with(finder).and_return(true)
allow(finder).to receive(:total).and_return(branches.size)
end
it 'keyset pagination is used with offset headers' do
allow(request_context).to receive(:request).and_return(fake_request)
allow(project.repository).to receive(:branch_count).and_return(branches.size)
@ -85,6 +90,26 @@ RSpec.describe Gitlab::Pagination::GitalyKeysetPager, feature_category: :source_
pager.paginate(finder)
end
context 'when second page does not exist' do
let(:base_query) { { per_page: 3 } }
it 'does not set an invalid X-Next-Page header' do
allow(request_context).to receive(:request).and_return(fake_request)
allow(project.repository).to receive(:branch_count).and_return(branches.size)
expect(finder).to receive(:execute).and_return(branches)
expect(request_context).to receive(:header).with('X-Per-Page', '3')
expect(request_context).to receive(:header).with('X-Page', '1')
expect(request_context).to receive(:header).with('X-Next-Page', '')
expect(request_context).to receive(:header).with('X-Prev-Page', '')
expect(request_context).to receive(:header).with('Link', kind_of(String))
expect(request_context).to receive(:header).with('X-Total', '3')
expect(request_context).to receive(:header).with('X-Total-Pages', '1')
pager.paginate(finder)
end
end
end
context 'when second page is requested' do

View File

@ -3,7 +3,11 @@
require 'spec_helper'
RSpec.shared_examples 'a subscribeable not accessible graphql resource' do
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
include GraphqlHelpers
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
let(:mutation) { described_class.new(object: nil, context: context, field: nil) }
subject { mutation.resolve(project_path: resource.project.full_path, iid: resource.iid, subscribed_state: true) }
@ -13,8 +17,12 @@ RSpec.shared_examples 'a subscribeable not accessible graphql resource' do
end
RSpec.shared_examples 'a subscribeable graphql resource' do
include GraphqlHelpers
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
let(:mutated_resource) { subject[resource.class.name.underscore.to_sym] }
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let(:mutation) { described_class.new(object: nil, context: context, field: nil) }
let(:subscribe) { true }
subject { mutation.resolve(project_path: resource.project.full_path, iid: resource.iid, subscribed_state: subscribe) }

View File

@ -3,9 +3,13 @@
require 'spec_helper'
RSpec.shared_examples 'an assignable resource' do
let_it_be(:user) { create(:user) }
include GraphqlHelpers
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let_it_be(:user) { create(:user) }
let(:query) { GraphQL::Query.new(empty_schema, document: nil, context: {}, variables: {}) }
let(:context) { GraphQL::Query::Context.new(query: query, values: { current_user: user }) }
subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
describe '#resolve' do
let_it_be(:assignee) { create(:user) }

View File

@ -41,6 +41,25 @@ RSpec.shared_examples 'a correct instrumented metric query' do |params|
end
end
RSpec.shared_examples 'a correct instrumented database query execution value' do |params|
let(:time_frame) { params[:time_frame] }
let(:options) { params[:options] }
let(:metric) { described_class.new(time_frame: time_frame, options: options) }
around do |example|
freeze_time { example.run }
end
before do
allow(metric.relation).to receive(:transaction_open?).and_return(false)
end
it 'returns correct value' do
query_result = metric.relation.connection.execute(metric.instrumentation).to_a.first.each_value.first
expect(query_result).to eq(expected_value)
end
end
RSpec.shared_examples 'a correct instrumented metric value and query' do |params|
it_behaves_like 'a correct instrumented metric value', params
it_behaves_like 'a correct instrumented metric query', params