Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
bef48349bb
commit
853c0c530b
2
Gemfile
2
Gemfile
|
@ -187,7 +187,7 @@ gem 'wikicloth', '0.8.1'
|
|||
gem 'asciidoctor', '~> 2.0.17'
|
||||
gem 'asciidoctor-include-ext', '~> 0.4.0', require: false
|
||||
gem 'asciidoctor-plantuml', '~> 0.0.16'
|
||||
gem 'asciidoctor-kroki', '~> 0.5.0', require: false
|
||||
gem 'asciidoctor-kroki', '~> 0.7.0', require: false
|
||||
gem 'rouge', '~> 3.30.0'
|
||||
gem 'truncato', '~> 0.7.12'
|
||||
gem 'bootstrap_form', '~> 4.2.0'
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
{"name":"asana","version":"0.10.13","platform":"ruby","checksum":"36d0d37f8dd6118a54580f1b80224875d7b6a9027598938e1722a508bfc2d7ac"},
|
||||
{"name":"asciidoctor","version":"2.0.17","platform":"ruby","checksum":"ed5b5e399e8d64994cc16f0983f993d6e33990909a8415b6fc8b786cdeb00f3d"},
|
||||
{"name":"asciidoctor-include-ext","version":"0.4.0","platform":"ruby","checksum":"406adb9d2fbfc25536609ca13b787ed704dc06a4e49d6709b83f3bad578f7878"},
|
||||
{"name":"asciidoctor-kroki","version":"0.5.0","platform":"ruby","checksum":"622c8b74796689bdaf8abdf89ad5295b11ce310e3d193e28f19e5baf58d45f12"},
|
||||
{"name":"asciidoctor-kroki","version":"0.7.0","platform":"ruby","checksum":"528ae4e49cae10e98c76e91f9aa40c67bf8540aa5ce4bbd44c5cd57af9f0b121"},
|
||||
{"name":"asciidoctor-plantuml","version":"0.0.16","platform":"ruby","checksum":"407e47cd1186ded5ccc75f0c812e5524c26c571d542247c5132abb8f47bd1793"},
|
||||
{"name":"ast","version":"2.4.2","platform":"ruby","checksum":"1e280232e6a33754cde542bc5ef85520b74db2aac73ec14acef453784447cc12"},
|
||||
{"name":"atlassian-jwt","version":"0.2.0","platform":"ruby","checksum":"52e653e9d6062d7a740c3675b0e79fa08367927c6fc17f5476d1b6b3798c6eb2"},
|
||||
|
|
|
@ -172,7 +172,7 @@ GEM
|
|||
asciidoctor (2.0.17)
|
||||
asciidoctor-include-ext (0.4.0)
|
||||
asciidoctor (>= 1.5.6, < 3.0.0)
|
||||
asciidoctor-kroki (0.5.0)
|
||||
asciidoctor-kroki (0.7.0)
|
||||
asciidoctor (~> 2.0)
|
||||
asciidoctor-plantuml (0.0.16)
|
||||
asciidoctor (>= 2.0.17, < 3.0.0)
|
||||
|
@ -1549,7 +1549,7 @@ DEPENDENCIES
|
|||
asana (~> 0.10.13)
|
||||
asciidoctor (~> 2.0.17)
|
||||
asciidoctor-include-ext (~> 0.4.0)
|
||||
asciidoctor-kroki (~> 0.5.0)
|
||||
asciidoctor-kroki (~> 0.7.0)
|
||||
asciidoctor-plantuml (~> 0.0.16)
|
||||
atlassian-jwt (~> 0.2.0)
|
||||
attr_encrypted (~> 3.2.4)!
|
||||
|
|
|
@ -12,7 +12,10 @@ module Members
|
|||
old_access_level = member.human_access
|
||||
old_expiry = member.expires_at
|
||||
|
||||
if member.update(params)
|
||||
member.attributes = params
|
||||
return success(member: member) unless member.changed?
|
||||
|
||||
if member.save
|
||||
after_execute(action: permission, old_access_level: old_access_level, old_expiry: old_expiry, member: member)
|
||||
|
||||
# Deletes only confidential issues todos for guests
|
||||
|
|
|
@ -16,9 +16,9 @@ You can use the Freeze Periods API to manipulate GitLab [Freeze Period](../user/
|
|||
Only users with Maintainer [permissions](../user/permissions.md) can
|
||||
interact with the Freeze Period API endpoints.
|
||||
|
||||
## List Freeze Periods
|
||||
## List freeze periods
|
||||
|
||||
Paginated list of Freeze Periods, sorted by `created_at` in ascending order.
|
||||
Paginated list of freeze periods, sorted by `created_at` in ascending order.
|
||||
|
||||
```plaintext
|
||||
GET /projects/:id/freeze_periods
|
||||
|
@ -49,9 +49,9 @@ Example response:
|
|||
]
|
||||
```
|
||||
|
||||
## Get a Freeze Period by a `freeze_period_id`
|
||||
## Get a freeze period by a `freeze_period_id`
|
||||
|
||||
Get a Freeze Period for the given `freeze_period_id`.
|
||||
Get a freeze period for the given `freeze_period_id`.
|
||||
|
||||
```plaintext
|
||||
GET /projects/:id/freeze_periods/:freeze_period_id
|
||||
|
@ -60,7 +60,7 @@ GET /projects/:id/freeze_periods/:freeze_period_id
|
|||
| Attribute | Type | Required | Description |
|
||||
| ------------- | -------------- | -------- | ----------------------------------------------------------------------------------- |
|
||||
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
|
||||
| `freeze_period_id` | string | yes | The database ID of the Freeze Period. |
|
||||
| `freeze_period_id` | integer | yes | The ID of the freeze period. |
|
||||
|
||||
Example request:
|
||||
|
||||
|
@ -81,9 +81,9 @@ Example response:
|
|||
}
|
||||
```
|
||||
|
||||
## Create a Freeze Period
|
||||
## Create a freeze period
|
||||
|
||||
Create a Freeze Period.
|
||||
Create a freeze period.
|
||||
|
||||
```plaintext
|
||||
POST /projects/:id/freeze_periods
|
||||
|
@ -92,8 +92,8 @@ POST /projects/:id/freeze_periods
|
|||
| Attribute | Type | Required | Description |
|
||||
| -------------------| --------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
|
||||
| `freeze_start` | string | yes | Start of the Freeze Period in [cron](https://crontab.guru/) format. |
|
||||
| `freeze_end` | string | yes | End of the Freeze Period in [cron](https://crontab.guru/) format. |
|
||||
| `freeze_start` | string | yes | Start of the freeze period in [cron](https://crontab.guru/) format. |
|
||||
| `freeze_end` | string | yes | End of the freeze period in [cron](https://crontab.guru/) format. |
|
||||
| `cron_timezone` | string | no | The time zone for the cron fields, defaults to UTC if not provided. |
|
||||
|
||||
Example request:
|
||||
|
@ -117,9 +117,9 @@ Example response:
|
|||
}
|
||||
```
|
||||
|
||||
## Update a Freeze Period
|
||||
## Update a freeze period
|
||||
|
||||
Update a Freeze Period for the given `freeze_period_id`.
|
||||
Update a freeze period for the given `freeze_period_id`.
|
||||
|
||||
```plaintext
|
||||
PUT /projects/:id/freeze_periods/:freeze_period_id
|
||||
|
@ -128,9 +128,9 @@ PUT /projects/:id/freeze_periods/:freeze_period_id
|
|||
| Attribute | Type | Required | Description |
|
||||
| ------------- | --------------- | -------- | ----------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
|
||||
| `freeze_period_id` | integer or string | yes | The database ID of the Freeze Period. |
|
||||
| `freeze_start` | string | no | Start of the Freeze Period in [cron](https://crontab.guru/) format. |
|
||||
| `freeze_end` | string | no | End of the Freeze Period in [cron](https://crontab.guru/) format. |
|
||||
| `freeze_period_id` | integer | yes | The ID of the freeze period. |
|
||||
| `freeze_start` | string | no | Start of the freeze period in [cron](https://crontab.guru/) format. |
|
||||
| `freeze_end` | string | no | End of the freeze period in [cron](https://crontab.guru/) format. |
|
||||
| `cron_timezone` | string | no | The time zone for the cron fields. |
|
||||
|
||||
Example request:
|
||||
|
@ -154,9 +154,9 @@ Example response:
|
|||
}
|
||||
```
|
||||
|
||||
## Delete a Freeze Period
|
||||
## Delete a freeze period
|
||||
|
||||
Delete a Freeze Period for the given `freeze_period_id`.
|
||||
Delete a freeze period for the given `freeze_period_id`.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/freeze_periods/:freeze_period_id
|
||||
|
@ -165,7 +165,7 @@ DELETE /projects/:id/freeze_periods/:freeze_period_id
|
|||
| Attribute | Type | Required | Description |
|
||||
| ------------- | -------------- | -------- | ----------------------------------------------------------------------------------- |
|
||||
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
|
||||
| `freeze_period_id` | string | yes | The database ID of the Freeze Period. |
|
||||
| `freeze_period_id` | integer | yes | The ID of the freeze period. |
|
||||
|
||||
Example request:
|
||||
|
||||
|
|
|
@ -13,9 +13,9 @@ links. For manipulating other Release assets, see [Release API](index.md).
|
|||
|
||||
GitLab supports links to `http`, `https`, and `ftp` assets.
|
||||
|
||||
## Get links
|
||||
## List links of a release
|
||||
|
||||
Get assets as links from a Release.
|
||||
Get assets as links from a release.
|
||||
|
||||
```plaintext
|
||||
GET /projects/:id/releases/:tag_name/assets/links
|
||||
|
@ -53,9 +53,9 @@ Example response:
|
|||
]
|
||||
```
|
||||
|
||||
## Get a link
|
||||
## Get a release link
|
||||
|
||||
Get an asset as a link from a Release.
|
||||
Get an asset as a link from a release.
|
||||
|
||||
```plaintext
|
||||
GET /projects/:id/releases/:tag_name/assets/links/:link_id
|
||||
|
@ -85,9 +85,9 @@ Example response:
|
|||
}
|
||||
```
|
||||
|
||||
## Create a link
|
||||
## Create a release link
|
||||
|
||||
Create an asset as a link from a Release.
|
||||
Creates an asset as a link from a release.
|
||||
|
||||
```plaintext
|
||||
POST /projects/:id/releases/:tag_name/assets/links
|
||||
|
@ -126,9 +126,9 @@ Example response:
|
|||
}
|
||||
```
|
||||
|
||||
## Update a link
|
||||
## Update a release link
|
||||
|
||||
Update an asset as a link from a Release.
|
||||
Updates an asset as a link from a release.
|
||||
|
||||
```plaintext
|
||||
PUT /projects/:id/releases/:tag_name/assets/links/:link_id
|
||||
|
@ -167,9 +167,9 @@ Example response:
|
|||
}
|
||||
```
|
||||
|
||||
## Delete a link
|
||||
## Delete a release link
|
||||
|
||||
Delete an asset as a link from a Release.
|
||||
Deletes an asset as a link from a release.
|
||||
|
||||
```plaintext
|
||||
DELETE /projects/:id/releases/:tag_name/assets/links/:link_id
|
||||
|
|
|
@ -93,7 +93,7 @@ By default, GitLab fetches the release using `released_at` time. The use of the
|
|||
The assets associated with a release are accessible through a permanent URL.
|
||||
GitLab always redirects this URL to the actual asset
|
||||
location, so even if the assets move to a different location, you can continue
|
||||
to use the same URL. This is defined during [link creation](../../../api/releases/links.md#create-a-link) or [updating](../../../api/releases/links.md#update-a-link) using the `filepath` API attribute.
|
||||
to use the same URL. This is defined during [link creation](../../../api/releases/links.md#create-a-release-link) or [updating](../../../api/releases/links.md#update-a-release-link) using the `filepath` API attribute.
|
||||
|
||||
The format of the URL is:
|
||||
|
||||
|
@ -133,7 +133,7 @@ The format of the URL is:
|
|||
https://host/namespace/project/-/releases/permalink/latest/downloads/:filepath
|
||||
```
|
||||
|
||||
If you have an asset with [`filepath`](../../../api/releases/links.md#create-a-link) for the `v11.9.0-rc2` latest release in the `gitlab-org`
|
||||
If you have an asset with [`filepath`](../../../api/releases/links.md#create-a-release-link) for the `v11.9.0-rc2` latest release in the `gitlab-org`
|
||||
namespace and `gitlab-runner` project on `gitlab.com`, for example:
|
||||
|
||||
```json
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "nokogiri"
|
||||
require "asciidoctor/extensions/asciidoctor_kroki/extension"
|
||||
require 'nokogiri'
|
||||
require 'asciidoctor/extensions/asciidoctor_kroki/version'
|
||||
require 'asciidoctor/extensions/asciidoctor_kroki/extension'
|
||||
|
||||
module Banzai
|
||||
module Filter
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rouge/plugins/common_mark'
|
||||
require "asciidoctor/extensions/asciidoctor_kroki/extension"
|
||||
require 'asciidoctor/extensions/asciidoctor_kroki/version'
|
||||
require 'asciidoctor/extensions/asciidoctor_kroki/extension'
|
||||
|
||||
# Generated HTML is transformed back to GFM by app/assets/javascripts/behaviors/markdown/nodes/code_block.js
|
||||
module Banzai
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
require 'asciidoctor'
|
||||
require 'asciidoctor-plantuml'
|
||||
require 'asciidoctor/extensions/asciidoctor_kroki/version'
|
||||
require 'asciidoctor/extensions/asciidoctor_kroki/extension'
|
||||
require 'asciidoctor/extensions'
|
||||
require 'gitlab/asciidoc/html5_converter'
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'asciidoctor/extensions/asciidoctor_kroki/version'
|
||||
require 'asciidoctor/extensions/asciidoctor_kroki/extension'
|
||||
|
||||
module Gitlab
|
||||
|
|
|
@ -567,7 +567,7 @@ module Gitlab
|
|||
|
||||
it 'does not allow kroki-plantuml-include to be overridden' do
|
||||
input = <<~ADOC
|
||||
[plantuml, test="{counter:kroki-plantuml-include:/etc/passwd}", format="png"]
|
||||
[plantuml, test="{counter:kroki-plantuml-include:README.md}", format="png"]
|
||||
....
|
||||
class BlockProcessor
|
||||
|
||||
|
@ -578,7 +578,7 @@ module Gitlab
|
|||
output = <<~HTML
|
||||
<div>
|
||||
<div>
|
||||
<a class=\"no-attachment-icon\" href=\"https://kroki.io/plantuml/png/eNpLzkksLlZwyslPzg4oyk9OLS7OL-LiQuUr2NTo6ipUJ-eX5pWkFlllF-VnZ-oW5CTmlZTm5uhm5iXnlKak1gIABQEb8A==\" target=\"_blank\" rel=\"noopener noreferrer\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Diagram\" decoding=\"async\" class=\"lazy\" data-src=\"https://kroki.io/plantuml/png/eNpLzkksLlZwyslPzg4oyk9OLS7OL-LiQuUr2NTo6ipUJ-eX5pWkFlllF-VnZ-oW5CTmlZTm5uhm5iXnlKak1gIABQEb8A==\"></a>
|
||||
<a class=\"no-attachment-icon\" href=\"https://kroki.io/plantuml/png/eNpLzkksLlZwyslPzg4oyk9OLS7OL-LiQuUr2NTo6ipUJ-eX5pWkFlllF-VnZ-oW5CTmlZTm5uhm5iXnlKak1gIABQEb8A==?test=README.md\" target=\"_blank\" rel=\"noopener noreferrer\"><img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Diagram\" decoding=\"async\" class=\"lazy\" data-src=\"https://kroki.io/plantuml/png/eNpLzkksLlZwyslPzg4oyk9OLS7OL-LiQuUr2NTo6ipUJ-eX5pWkFlllF-VnZ-oW5CTmlZTm5uhm5iXnlKak1gIABQEb8A==?test=README.md\"></a>
|
||||
</div>
|
||||
</div>
|
||||
HTML
|
||||
|
|
|
@ -6,7 +6,8 @@ RSpec.describe Gitlab::Kroki do
|
|||
|
||||
describe '.formats' do
|
||||
def default_formats
|
||||
%w[bytefield c4plantuml ditaa erd graphviz nomnoml pikchr plantuml svgbob umlet vega vegalite wavedrom].freeze
|
||||
%w[bytefield c4plantuml ditaa erd graphviz nomnoml pikchr plantuml
|
||||
structurizr svgbob umlet vega vegalite wavedrom].freeze
|
||||
end
|
||||
|
||||
subject { described_class.formats(Gitlab::CurrentSettings) }
|
||||
|
|
|
@ -217,120 +217,80 @@ RSpec.describe Gitlab::Redis::MultiStore do
|
|||
allow(secondary_store).to receive(name).and_call_original
|
||||
end
|
||||
|
||||
context 'with feature flag :use_primary_and_secondary_stores_for_test_store' do
|
||||
context 'when reading from the primary is successful' do
|
||||
it 'returns the correct value' do
|
||||
expect(primary_store).to receive(name).with(*args).and_call_original
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
it 'does not execute on the secondary store' do
|
||||
expect(secondary_store).not_to receive(name)
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
include_examples 'reads correct value'
|
||||
end
|
||||
|
||||
context 'when reading from primary instance is raising an exception' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: true)
|
||||
allow(primary_store).to receive(name).with(*args).and_raise(StandardError)
|
||||
allow(Gitlab::ErrorTracking).to receive(:log_exception)
|
||||
end
|
||||
|
||||
context 'when reading from the primary is successful' do
|
||||
it 'returns the correct value' do
|
||||
expect(primary_store).to receive(name).with(*args).and_call_original
|
||||
it 'logs the exception' do
|
||||
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(an_instance_of(StandardError),
|
||||
hash_including(:multi_store_error_message, instance_name: instance_name, command_name: name))
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
it 'does not execute on the secondary store' do
|
||||
expect(secondary_store).not_to receive(name)
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
include_examples 'reads correct value'
|
||||
subject
|
||||
end
|
||||
|
||||
context 'when reading from primary instance is raising an exception' do
|
||||
before do
|
||||
allow(primary_store).to receive(name).with(*args).and_raise(StandardError)
|
||||
allow(Gitlab::ErrorTracking).to receive(:log_exception)
|
||||
end
|
||||
include_examples 'fallback read from the secondary store'
|
||||
end
|
||||
|
||||
it 'logs the exception' do
|
||||
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(an_instance_of(StandardError),
|
||||
hash_including(:multi_store_error_message, instance_name: instance_name, command_name: name))
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
include_examples 'fallback read from the secondary store'
|
||||
context 'when reading from primary instance return no value' do
|
||||
before do
|
||||
allow(primary_store).to receive(name).and_return(nil)
|
||||
end
|
||||
|
||||
context 'when reading from primary instance return no value' do
|
||||
before do
|
||||
allow(primary_store).to receive(name).and_return(nil)
|
||||
end
|
||||
include_examples 'fallback read from the secondary store'
|
||||
end
|
||||
|
||||
include_examples 'fallback read from the secondary store'
|
||||
context 'when the command is executed within pipelined block' do
|
||||
subject do
|
||||
multi_store.pipelined do |pipeline|
|
||||
pipeline.send(name, *args)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the command is executed within pipelined block' do
|
||||
subject do
|
||||
multi_store.pipelined do |pipeline|
|
||||
pipeline.send(name, *args)
|
||||
it 'is executed only 1 time on primary and secondary instance' do
|
||||
expect(primary_store).to receive(:pipelined).and_call_original
|
||||
expect(secondary_store).to receive(:pipelined).and_call_original
|
||||
|
||||
2.times do
|
||||
expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
|
||||
expect(pipeline).to receive(name).with(*args).once.and_call_original
|
||||
end
|
||||
end
|
||||
|
||||
it 'is executed only 1 time on primary and secondary instance' do
|
||||
expect(primary_store).to receive(:pipelined).and_call_original
|
||||
expect(secondary_store).to receive(:pipelined).and_call_original
|
||||
|
||||
2.times do
|
||||
expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
|
||||
expect(pipeline).to receive(name).with(*args).once.and_call_original
|
||||
end
|
||||
end
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
if params[:block]
|
||||
subject do
|
||||
multi_store.send(name, *args, &block)
|
||||
end
|
||||
|
||||
context 'when block is provided' do
|
||||
it 'yields to the block' do
|
||||
expect(primary_store).to receive(name).and_yield(value)
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
include_examples 'reads correct value'
|
||||
end
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context 'with feature flag :use_primary_and_secondary_stores_for_test_store' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: false)
|
||||
if params[:block]
|
||||
subject do
|
||||
multi_store.send(name, *args, &block)
|
||||
end
|
||||
|
||||
context 'with feature flag :use_primary_store_as_default_for_test_store is disabled' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_store_as_default_for_test_store: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'secondary store'
|
||||
end
|
||||
|
||||
context 'with feature flag :use_primary_store_as_default_for_test_store is enabled' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_store_as_default_for_test_store: true)
|
||||
end
|
||||
|
||||
it 'execute on the primary instance' do
|
||||
expect(primary_store).to receive(name).with(*args).and_call_original
|
||||
context 'when block is provided' do
|
||||
it 'yields to the block' do
|
||||
expect(primary_store).to receive(name).and_yield(value)
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
include_examples 'reads correct value'
|
||||
|
||||
it 'does not execute on the secondary store' do
|
||||
expect(secondary_store).not_to receive(name)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -411,100 +371,58 @@ RSpec.describe Gitlab::Redis::MultiStore do
|
|||
allow(secondary_store).to receive(name).and_call_original
|
||||
end
|
||||
|
||||
context 'with feature flag :use_primary_and_secondary_stores_for_test_store' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: true)
|
||||
context 'when executing on primary instance is successful' do
|
||||
it 'executes on both primary and secondary redis store', :aggregate_errors do
|
||||
expect(primary_store).to receive(name).with(*expected_args).and_call_original
|
||||
expect(secondary_store).to receive(name).with(*expected_args).and_call_original
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
context 'when executing on primary instance is successful' do
|
||||
it 'executes on both primary and secondary redis store', :aggregate_errors do
|
||||
expect(primary_store).to receive(name).with(*expected_args).and_call_original
|
||||
expect(secondary_store).to receive(name).with(*expected_args).and_call_original
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
include_examples 'verify that store contains values', :primary_store
|
||||
include_examples 'verify that store contains values', :secondary_store
|
||||
end
|
||||
|
||||
context 'when executing on the primary instance is raising an exception' do
|
||||
before do
|
||||
allow(primary_store).to receive(name).with(*expected_args).and_raise(StandardError)
|
||||
allow(Gitlab::ErrorTracking).to receive(:log_exception)
|
||||
end
|
||||
|
||||
it 'logs the exception and execute on secondary instance', :aggregate_errors do
|
||||
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(an_instance_of(StandardError),
|
||||
hash_including(:multi_store_error_message, command_name: name, instance_name: instance_name))
|
||||
expect(secondary_store).to receive(name).with(*expected_args).and_call_original
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
include_examples 'verify that store contains values', :secondary_store
|
||||
end
|
||||
|
||||
context 'when the command is executed within pipelined block' do
|
||||
subject do
|
||||
multi_store.pipelined do |pipeline|
|
||||
pipeline.send(name, *args)
|
||||
end
|
||||
end
|
||||
|
||||
it 'is executed only 1 time on each instance', :aggregate_errors do
|
||||
expect(primary_store).to receive(:pipelined).and_call_original
|
||||
expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
|
||||
expect(pipeline).to receive(name).with(*expected_args).once.and_call_original
|
||||
end
|
||||
|
||||
expect(secondary_store).to receive(:pipelined).and_call_original
|
||||
expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
|
||||
expect(pipeline).to receive(name).with(*expected_args).once.and_call_original
|
||||
end
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
include_examples 'verify that store contains values', :primary_store
|
||||
include_examples 'verify that store contains values', :secondary_store
|
||||
end
|
||||
include_examples 'verify that store contains values', :primary_store
|
||||
include_examples 'verify that store contains values', :secondary_store
|
||||
end
|
||||
|
||||
context 'with feature flag :use_primary_and_secondary_stores_for_test_store is disabled' do
|
||||
context 'when executing on the primary instance is raising an exception' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: false)
|
||||
allow(primary_store).to receive(name).with(*expected_args).and_raise(StandardError)
|
||||
allow(Gitlab::ErrorTracking).to receive(:log_exception)
|
||||
end
|
||||
|
||||
context 'with feature flag :use_primary_store_as_default_for_test_store is disabled' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_store_as_default_for_test_store: false)
|
||||
end
|
||||
it 'logs the exception and execute on secondary instance', :aggregate_errors do
|
||||
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(an_instance_of(StandardError),
|
||||
hash_including(:multi_store_error_message, command_name: name, instance_name: instance_name))
|
||||
expect(secondary_store).to receive(name).with(*expected_args).and_call_original
|
||||
|
||||
it 'executes only on the secondary redis store', :aggregate_errors do
|
||||
expect(secondary_store).to receive(name).with(*expected_args)
|
||||
expect(primary_store).not_to receive(name).with(*expected_args)
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
include_examples 'verify that store contains values', :secondary_store
|
||||
subject
|
||||
end
|
||||
|
||||
context 'with feature flag :use_primary_store_as_default_for_test_store is enabled' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_store_as_default_for_test_store: true)
|
||||
include_examples 'verify that store contains values', :secondary_store
|
||||
end
|
||||
|
||||
context 'when the command is executed within pipelined block' do
|
||||
subject do
|
||||
multi_store.pipelined do |pipeline|
|
||||
pipeline.send(name, *args)
|
||||
end
|
||||
|
||||
it 'executes only on the primary_redis redis store', :aggregate_errors do
|
||||
expect(primary_store).to receive(name).with(*expected_args)
|
||||
expect(secondary_store).not_to receive(name).with(*expected_args)
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
include_examples 'verify that store contains values', :primary_store
|
||||
end
|
||||
|
||||
it 'is executed only 1 time on each instance', :aggregate_errors do
|
||||
expect(primary_store).to receive(:pipelined).and_call_original
|
||||
expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
|
||||
expect(pipeline).to receive(name).with(*expected_args).once.and_call_original
|
||||
end
|
||||
|
||||
expect(secondary_store).to receive(:pipelined).and_call_original
|
||||
expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
|
||||
expect(pipeline).to receive(name).with(*expected_args).once.and_call_original
|
||||
end
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
include_examples 'verify that store contains values', :primary_store
|
||||
include_examples 'verify that store contains values', :secondary_store
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -539,151 +457,109 @@ RSpec.describe Gitlab::Redis::MultiStore do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with feature flag :use_primary_and_secondary_stores_for_test_store' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: true)
|
||||
context 'when executing on primary instance is successful' do
|
||||
it 'executes on both primary and secondary redis store', :aggregate_errors do
|
||||
expect(primary_store).to receive(name).and_call_original
|
||||
expect(secondary_store).to receive(name).and_call_original
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
context 'when executing on primary instance is successful' do
|
||||
it 'executes on both primary and secondary redis store', :aggregate_errors do
|
||||
expect(primary_store).to receive(name).and_call_original
|
||||
expect(secondary_store).to receive(name).and_call_original
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
include_examples 'verify that store contains values', :primary_store
|
||||
include_examples 'verify that store contains values', :secondary_store
|
||||
end
|
||||
|
||||
context 'when executing on the primary instance is raising an exception' do
|
||||
before do
|
||||
allow(primary_store).to receive(name).and_raise(StandardError)
|
||||
allow(Gitlab::ErrorTracking).to receive(:log_exception)
|
||||
end
|
||||
|
||||
it 'logs the exception and execute on secondary instance', :aggregate_errors do
|
||||
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(an_instance_of(StandardError),
|
||||
hash_including(:multi_store_error_message, command_name: name))
|
||||
expect(secondary_store).to receive(name).and_call_original
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
include_examples 'verify that store contains values', :secondary_store
|
||||
end
|
||||
|
||||
describe 'return values from a pipelined command' do
|
||||
RSpec::Matchers.define :pipeline_diff_error_with_stacktrace do |message|
|
||||
match do |object|
|
||||
expect(object).to be_a(Gitlab::Redis::MultiStore::PipelinedDiffError)
|
||||
expect(object.backtrace).not_to be_nil
|
||||
expect(object.message).to eq(message)
|
||||
end
|
||||
end
|
||||
|
||||
subject do
|
||||
multi_store.send(name) do |redis|
|
||||
redis.get(key1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the value exists on both and are equal' do
|
||||
before do
|
||||
primary_store.set(key1, value1)
|
||||
secondary_store.set(key1, value1)
|
||||
end
|
||||
|
||||
it 'returns the value' do
|
||||
expect(Gitlab::ErrorTracking).not_to receive(:log_exception)
|
||||
|
||||
expect(subject).to eq([value1])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the value exists on both but differ' do
|
||||
before do
|
||||
primary_store.set(key1, value1)
|
||||
secondary_store.set(key1, value2)
|
||||
end
|
||||
|
||||
it 'returns the value from the secondary store, logging an error' do
|
||||
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
|
||||
pipeline_diff_error_with_stacktrace(
|
||||
'Pipelined command executed on both stores successfully but results differ between them. ' \
|
||||
"Result from the primary: [#{value1.inspect}]. Result from the secondary: [#{value2.inspect}]."
|
||||
),
|
||||
hash_including(command_name: name, instance_name: instance_name)
|
||||
).and_call_original
|
||||
expect(counter).to receive(:increment).with(command: name, instance_name: instance_name)
|
||||
|
||||
expect(subject).to eq([value2])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the value does not exist on the primary but it does on the secondary' do
|
||||
before do
|
||||
secondary_store.set(key1, value2)
|
||||
end
|
||||
|
||||
it 'returns the value from the secondary store, logging an error' do
|
||||
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
|
||||
pipeline_diff_error_with_stacktrace(
|
||||
'Pipelined command executed on both stores successfully but results differ between them. ' \
|
||||
"Result from the primary: [nil]. Result from the secondary: [#{value2.inspect}]."
|
||||
),
|
||||
hash_including(command_name: name, instance_name: instance_name)
|
||||
)
|
||||
expect(counter).to receive(:increment).with(command: name, instance_name: instance_name)
|
||||
|
||||
expect(subject).to eq([value2])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the value does not exist in either' do
|
||||
it 'returns nil without logging an error' do
|
||||
expect(Gitlab::ErrorTracking).not_to receive(:log_exception)
|
||||
expect(counter).not_to receive(:increment)
|
||||
|
||||
expect(subject).to eq([nil])
|
||||
end
|
||||
end
|
||||
end
|
||||
include_examples 'verify that store contains values', :primary_store
|
||||
include_examples 'verify that store contains values', :secondary_store
|
||||
end
|
||||
|
||||
context 'with feature flag :use_primary_and_secondary_stores_for_test_store is disabled' do
|
||||
context 'when executing on the primary instance is raising an exception' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: false)
|
||||
allow(primary_store).to receive(name).and_raise(StandardError)
|
||||
allow(Gitlab::ErrorTracking).to receive(:log_exception)
|
||||
end
|
||||
|
||||
context 'with feature flag :use_primary_store_as_default_for_test_store is disabled' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_store_as_default_for_test_store: false)
|
||||
end
|
||||
it 'logs the exception and execute on secondary instance', :aggregate_errors do
|
||||
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(an_instance_of(StandardError),
|
||||
hash_including(:multi_store_error_message, command_name: name))
|
||||
expect(secondary_store).to receive(name).and_call_original
|
||||
|
||||
it 'executes only on the secondary redis store', :aggregate_errors do
|
||||
expect(secondary_store).to receive(name)
|
||||
expect(primary_store).not_to receive(name)
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
include_examples 'verify that store contains values', :secondary_store
|
||||
subject
|
||||
end
|
||||
|
||||
context 'with feature flag :use_primary_store_as_default_for_test_store is enabled' do
|
||||
include_examples 'verify that store contains values', :secondary_store
|
||||
end
|
||||
|
||||
describe 'return values from a pipelined command' do
|
||||
RSpec::Matchers.define :pipeline_diff_error_with_stacktrace do |message|
|
||||
match do |object|
|
||||
expect(object).to be_a(Gitlab::Redis::MultiStore::PipelinedDiffError)
|
||||
expect(object.backtrace).not_to be_nil
|
||||
expect(object.message).to eq(message)
|
||||
end
|
||||
end
|
||||
|
||||
subject do
|
||||
multi_store.send(name) do |redis|
|
||||
redis.get(key1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the value exists on both and are equal' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_store_as_default_for_test_store: true)
|
||||
primary_store.set(key1, value1)
|
||||
secondary_store.set(key1, value1)
|
||||
end
|
||||
|
||||
it 'executes only on the primary_redis redis store', :aggregate_errors do
|
||||
expect(primary_store).to receive(name)
|
||||
expect(secondary_store).not_to receive(name)
|
||||
it 'returns the value' do
|
||||
expect(Gitlab::ErrorTracking).not_to receive(:log_exception)
|
||||
|
||||
subject
|
||||
expect(subject).to eq([value1])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the value exists on both but differ' do
|
||||
before do
|
||||
primary_store.set(key1, value1)
|
||||
secondary_store.set(key1, value2)
|
||||
end
|
||||
|
||||
include_examples 'verify that store contains values', :primary_store
|
||||
it 'returns the value from the secondary store, logging an error' do
|
||||
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
|
||||
pipeline_diff_error_with_stacktrace(
|
||||
'Pipelined command executed on both stores successfully but results differ between them. ' \
|
||||
"Result from the primary: [#{value1.inspect}]. Result from the secondary: [#{value2.inspect}]."
|
||||
),
|
||||
hash_including(command_name: name, instance_name: instance_name)
|
||||
).and_call_original
|
||||
expect(counter).to receive(:increment).with(command: name, instance_name: instance_name)
|
||||
|
||||
expect(subject).to eq([value2])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the value does not exist on the primary but it does on the secondary' do
|
||||
before do
|
||||
secondary_store.set(key1, value2)
|
||||
end
|
||||
|
||||
it 'returns the value from the secondary store, logging an error' do
|
||||
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
|
||||
pipeline_diff_error_with_stacktrace(
|
||||
'Pipelined command executed on both stores successfully but results differ between them. ' \
|
||||
"Result from the primary: [nil]. Result from the secondary: [#{value2.inspect}]."
|
||||
),
|
||||
hash_including(command_name: name, instance_name: instance_name)
|
||||
)
|
||||
expect(counter).to receive(:increment).with(command: name, instance_name: instance_name)
|
||||
|
||||
expect(subject).to eq([value2])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the value does not exist in either' do
|
||||
it 'returns nil without logging an error' do
|
||||
expect(Gitlab::ErrorTracking).not_to receive(:log_exception)
|
||||
expect(counter).not_to receive(:increment)
|
||||
|
||||
expect(subject).to eq([nil])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -827,40 +703,8 @@ RSpec.describe Gitlab::Redis::MultiStore do
|
|||
describe '#to_s' do
|
||||
subject { multi_store.to_s }
|
||||
|
||||
context 'with feature flag :use_primary_and_secondary_stores_for_test_store is enabled' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: true)
|
||||
end
|
||||
|
||||
it 'returns same value as primary_store' do
|
||||
is_expected.to eq(primary_store.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with feature flag :use_primary_and_secondary_stores_for_test_store is disabled' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: false)
|
||||
end
|
||||
|
||||
context 'with feature flag :use_primary_store_as_default_for_test_store is enabled' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_store_as_default_for_test_store: true)
|
||||
end
|
||||
|
||||
it 'returns same value as primary_store' do
|
||||
is_expected.to eq(primary_store.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with feature flag :use_primary_store_as_default_for_test_store is disabled' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_store_as_default_for_test_store: false)
|
||||
end
|
||||
|
||||
it 'returns same value as primary_store' do
|
||||
is_expected.to eq(secondary_store.to_s)
|
||||
end
|
||||
end
|
||||
it 'returns same value as primary_store' do
|
||||
is_expected.to eq(primary_store.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -871,24 +715,8 @@ RSpec.describe Gitlab::Redis::MultiStore do
|
|||
end
|
||||
|
||||
describe '#use_primary_and_secondary_stores?' do
|
||||
context 'with feature flag :use_primary_and_secondary_stores_for_test_store is enabled' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: true)
|
||||
end
|
||||
|
||||
it 'multi store is disabled' do
|
||||
expect(multi_store.use_primary_and_secondary_stores?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'with feature flag :use_primary_and_secondary_stores_for_test_store is disabled' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_and_secondary_stores_for_test_store: false)
|
||||
end
|
||||
|
||||
it 'multi store is disabled' do
|
||||
expect(multi_store.use_primary_and_secondary_stores?).to be false
|
||||
end
|
||||
it 'multi store is enabled' do
|
||||
expect(multi_store.use_primary_and_secondary_stores?).to be true
|
||||
end
|
||||
|
||||
context 'with empty DB' do
|
||||
|
@ -913,24 +741,8 @@ RSpec.describe Gitlab::Redis::MultiStore do
|
|||
end
|
||||
|
||||
describe '#use_primary_store_as_default?' do
|
||||
context 'with feature flag :use_primary_store_as_default_for_test_store is enabled' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_store_as_default_for_test_store: true)
|
||||
end
|
||||
|
||||
it 'multi store is disabled' do
|
||||
expect(multi_store.use_primary_store_as_default?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'with feature flag :use_primary_store_as_default_for_test_store is disabled' do
|
||||
before do
|
||||
stub_feature_flags(use_primary_store_as_default_for_test_store: false)
|
||||
end
|
||||
|
||||
it 'multi store is disabled' do
|
||||
expect(multi_store.use_primary_store_as_default?).to be false
|
||||
end
|
||||
it 'multi store is disabled' do
|
||||
expect(multi_store.use_primary_store_as_default?).to be true
|
||||
end
|
||||
|
||||
context 'with empty DB' do
|
||||
|
|
|
@ -14,6 +14,11 @@ RSpec.describe Members::UpdateService do
|
|||
{ access_level: access_level }
|
||||
end
|
||||
|
||||
before do
|
||||
project.add_developer(member_user)
|
||||
group.add_developer(member_user)
|
||||
end
|
||||
|
||||
subject { described_class.new(current_user, params).execute(member, permission: permission) }
|
||||
|
||||
shared_examples 'a service raising Gitlab::Access::AccessDeniedError' do
|
||||
|
@ -83,11 +88,6 @@ RSpec.describe Members::UpdateService do
|
|||
end
|
||||
end
|
||||
|
||||
before do
|
||||
project.add_developer(member_user)
|
||||
group.add_developer(member_user)
|
||||
end
|
||||
|
||||
context 'when current user cannot update the given member' do
|
||||
it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do
|
||||
let(:source) { project }
|
||||
|
@ -183,4 +183,54 @@ RSpec.describe Members::UpdateService do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'authorization updates' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
shared_examples 'manages authorization updates' do
|
||||
context 'access level changes' do
|
||||
let(:params) do
|
||||
{ access_level: Gitlab::Access::MAINTAINER }
|
||||
end
|
||||
|
||||
it 'authorization update callback is triggered' do
|
||||
expect(member).to receive(:refresh_member_authorized_projects).once
|
||||
|
||||
described_class.new(current_user, params).execute(member, permission: permission)
|
||||
end
|
||||
end
|
||||
|
||||
context 'no attribute changes' do
|
||||
let(:params) do
|
||||
{ access_level: Gitlab::Access::DEVELOPER }
|
||||
end
|
||||
|
||||
it 'authorization update callback is not triggered' do
|
||||
expect(member).not_to receive(:refresh_member_authorized_projects)
|
||||
|
||||
described_class.new(current_user, params).execute(member, permission: permission)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'group member' do
|
||||
let(:source) { group }
|
||||
|
||||
before do
|
||||
group.add_owner(current_user)
|
||||
end
|
||||
|
||||
include_examples 'manages authorization updates'
|
||||
end
|
||||
|
||||
context 'project member' do
|
||||
let(:source) { project }
|
||||
|
||||
before do
|
||||
project.add_maintainer(current_user)
|
||||
end
|
||||
|
||||
include_examples 'manages authorization updates'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue