Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
8827c1d184
commit
e69aae81ea
4
Gemfile
4
Gemfile
|
@ -41,7 +41,7 @@ gem 'omniauth-oauth2-generic', '~> 0.2.2'
|
||||||
gem 'omniauth-saml', '~> 1.10'
|
gem 'omniauth-saml', '~> 1.10'
|
||||||
gem 'omniauth-shibboleth', '~> 1.3.0'
|
gem 'omniauth-shibboleth', '~> 1.3.0'
|
||||||
gem 'omniauth-twitter', '~> 1.4'
|
gem 'omniauth-twitter', '~> 1.4'
|
||||||
gem 'omniauth_crowd', '~> 2.2.0'
|
gem 'omniauth_crowd', '~> 2.4.0'
|
||||||
gem 'omniauth-authentiq', '~> 0.3.3'
|
gem 'omniauth-authentiq', '~> 0.3.3'
|
||||||
gem 'omniauth_openid_connect', '~> 0.3.5'
|
gem 'omniauth_openid_connect', '~> 0.3.5'
|
||||||
gem 'omniauth-salesforce', '~> 1.0.5'
|
gem 'omniauth-salesforce', '~> 1.0.5'
|
||||||
|
@ -188,7 +188,7 @@ gem 'acts-as-taggable-on', '~> 6.0'
|
||||||
# Background jobs
|
# Background jobs
|
||||||
gem 'sidekiq', '~> 5.2.7'
|
gem 'sidekiq', '~> 5.2.7'
|
||||||
gem 'sidekiq-cron', '~> 1.0'
|
gem 'sidekiq-cron', '~> 1.0'
|
||||||
gem 'redis-namespace', '~> 1.6.0'
|
gem 'redis-namespace', '~> 1.7.0'
|
||||||
gem 'gitlab-sidekiq-fetcher', '0.5.2', require: 'sidekiq-reliable-fetch'
|
gem 'gitlab-sidekiq-fetcher', '0.5.2', require: 'sidekiq-reliable-fetch'
|
||||||
|
|
||||||
# Cron Parser
|
# Cron Parser
|
||||||
|
|
|
@ -740,7 +740,7 @@ GEM
|
||||||
omniauth-twitter (1.4.0)
|
omniauth-twitter (1.4.0)
|
||||||
omniauth-oauth (~> 1.1)
|
omniauth-oauth (~> 1.1)
|
||||||
rack
|
rack
|
||||||
omniauth_crowd (2.2.3)
|
omniauth_crowd (2.4.0)
|
||||||
activesupport
|
activesupport
|
||||||
nokogiri (>= 1.4.4)
|
nokogiri (>= 1.4.4)
|
||||||
omniauth (~> 1.0)
|
omniauth (~> 1.0)
|
||||||
|
@ -877,7 +877,7 @@ GEM
|
||||||
redis-activesupport (5.2.0)
|
redis-activesupport (5.2.0)
|
||||||
activesupport (>= 3, < 7)
|
activesupport (>= 3, < 7)
|
||||||
redis-store (>= 1.3, < 2)
|
redis-store (>= 1.3, < 2)
|
||||||
redis-namespace (1.6.0)
|
redis-namespace (1.7.0)
|
||||||
redis (>= 3.0.4)
|
redis (>= 3.0.4)
|
||||||
redis-rack (2.1.2)
|
redis-rack (2.1.2)
|
||||||
rack (>= 2.0.8, < 3)
|
rack (>= 2.0.8, < 3)
|
||||||
|
@ -1337,7 +1337,7 @@ DEPENDENCIES
|
||||||
omniauth-saml (~> 1.10)
|
omniauth-saml (~> 1.10)
|
||||||
omniauth-shibboleth (~> 1.3.0)
|
omniauth-shibboleth (~> 1.3.0)
|
||||||
omniauth-twitter (~> 1.4)
|
omniauth-twitter (~> 1.4)
|
||||||
omniauth_crowd (~> 2.2.0)
|
omniauth_crowd (~> 2.4.0)
|
||||||
omniauth_openid_connect (~> 0.3.5)
|
omniauth_openid_connect (~> 0.3.5)
|
||||||
org-ruby (~> 0.9.12)
|
org-ruby (~> 0.9.12)
|
||||||
parallel (~> 1.19)
|
parallel (~> 1.19)
|
||||||
|
@ -1365,7 +1365,7 @@ DEPENDENCIES
|
||||||
re2 (~> 1.2.0)
|
re2 (~> 1.2.0)
|
||||||
recaptcha (~> 4.11)
|
recaptcha (~> 4.11)
|
||||||
redis (~> 4.0)
|
redis (~> 4.0)
|
||||||
redis-namespace (~> 1.6.0)
|
redis-namespace (~> 1.7.0)
|
||||||
redis-rails (~> 5.0.2)
|
redis-rails (~> 5.0.2)
|
||||||
request_store (~> 1.5)
|
request_store (~> 1.5)
|
||||||
responders (~> 3.0)
|
responders (~> 3.0)
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
} from '@gitlab/ui';
|
} from '@gitlab/ui';
|
||||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||||
import { s__ } from '~/locale';
|
import { s__ } from '~/locale';
|
||||||
import { mergeUrlParams } from '~/lib/utils/url_utility';
|
import { mergeUrlParams, joinPaths, visitUrl } from '~/lib/utils/url_utility';
|
||||||
import getIncidents from '../graphql/queries/get_incidents.query.graphql';
|
import getIncidents from '../graphql/queries/get_incidents.query.graphql';
|
||||||
import { I18N } from '../constants';
|
import { I18N } from '../constants';
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ const tdClass =
|
||||||
'table-col gl-display-flex d-md-table-cell gl-align-items-center gl-white-space-nowrap';
|
'table-col gl-display-flex d-md-table-cell gl-align-items-center gl-white-space-nowrap';
|
||||||
const thClass = 'gl-hover-bg-blue-50';
|
const thClass = 'gl-hover-bg-blue-50';
|
||||||
const bodyTrClass =
|
const bodyTrClass =
|
||||||
'gl-border-1 gl-border-t-solid gl-border-gray-100 gl-hover-bg-blue-50 gl-hover-border-b-solid gl-hover-border-blue-200';
|
'gl-border-1 gl-border-t-solid gl-border-gray-100 gl-hover-cursor-pointer gl-hover-bg-blue-50 gl-hover-border-b-solid gl-hover-border-blue-200';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
i18n: I18N,
|
i18n: I18N,
|
||||||
|
@ -56,7 +56,7 @@ export default {
|
||||||
directives: {
|
directives: {
|
||||||
GlTooltip: GlTooltipDirective,
|
GlTooltip: GlTooltipDirective,
|
||||||
},
|
},
|
||||||
inject: ['projectPath', 'newIssuePath', 'incidentTemplateName'],
|
inject: ['projectPath', 'newIssuePath', 'incidentTemplateName', 'issuePath'],
|
||||||
apollo: {
|
apollo: {
|
||||||
incidents: {
|
incidents: {
|
||||||
query: getIncidents,
|
query: getIncidents,
|
||||||
|
@ -102,6 +102,9 @@ export default {
|
||||||
hasAssignees(assignees) {
|
hasAssignees(assignees) {
|
||||||
return Boolean(assignees.nodes?.length);
|
return Boolean(assignees.nodes?.length);
|
||||||
},
|
},
|
||||||
|
navigateToIncidentDetails({ iid }) {
|
||||||
|
return visitUrl(joinPaths(this.issuePath, iid));
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -138,6 +141,7 @@ export default {
|
||||||
:tbody-tr-class="tbodyTrClass"
|
:tbody-tr-class="tbodyTrClass"
|
||||||
:no-local-sorting="true"
|
:no-local-sorting="true"
|
||||||
fixed
|
fixed
|
||||||
|
@row-clicked="navigateToIncidentDetails"
|
||||||
>
|
>
|
||||||
<template #cell(title)="{ item }">
|
<template #cell(title)="{ item }">
|
||||||
<div class="gl-max-w-full text-truncate" :title="item.title">{{ item.title }}</div>
|
<div class="gl-max-w-full text-truncate" :title="item.title">{{ item.title }}</div>
|
||||||
|
|
|
@ -8,7 +8,7 @@ export default () => {
|
||||||
const selector = '#js-incidents';
|
const selector = '#js-incidents';
|
||||||
|
|
||||||
const domEl = document.querySelector(selector);
|
const domEl = document.querySelector(selector);
|
||||||
const { projectPath, newIssuePath, incidentTemplateName } = domEl.dataset;
|
const { projectPath, newIssuePath, incidentTemplateName, issuePath } = domEl.dataset;
|
||||||
|
|
||||||
const apolloProvider = new VueApollo({
|
const apolloProvider = new VueApollo({
|
||||||
defaultClient: createDefaultClient(),
|
defaultClient: createDefaultClient(),
|
||||||
|
@ -20,6 +20,7 @@ export default () => {
|
||||||
projectPath,
|
projectPath,
|
||||||
incidentTemplateName,
|
incidentTemplateName,
|
||||||
newIssuePath,
|
newIssuePath,
|
||||||
|
issuePath,
|
||||||
},
|
},
|
||||||
apolloProvider,
|
apolloProvider,
|
||||||
components: {
|
components: {
|
||||||
|
|
|
@ -15,12 +15,7 @@ module Groups
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
update_result = Ci::ChangeVariablesService.new(
|
if @group.update(group_variables_params)
|
||||||
container: @group, current_user: current_user,
|
|
||||||
params: group_variables_params
|
|
||||||
).execute
|
|
||||||
|
|
||||||
if update_result
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.json { render_group_variables }
|
format.json { render_group_variables }
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,8 @@ module Projects::IncidentsHelper
|
||||||
{
|
{
|
||||||
'project-path' => project.full_path,
|
'project-path' => project.full_path,
|
||||||
'new-issue-path' => new_project_issue_path(project),
|
'new-issue-path' => new_project_issue_path(project),
|
||||||
'incident-template-name' => 'incident'
|
'incident-template-name' => 'incident',
|
||||||
|
'issue-path' => project_issues_path(project)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Ci
|
|
||||||
class ChangeVariableService < BaseContainerService
|
|
||||||
def execute
|
|
||||||
case params[:action]
|
|
||||||
when :create
|
|
||||||
container.variables.create(params[:variable_params])
|
|
||||||
when :update
|
|
||||||
variable.tap do |target_variable|
|
|
||||||
target_variable.update(params[:variable_params].except(:key))
|
|
||||||
end
|
|
||||||
when :destroy
|
|
||||||
variable.tap do |target_variable|
|
|
||||||
target_variable.destroy
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def variable
|
|
||||||
container.variables.find_by!(params[:variable_params].slice(:key)) # rubocop:disable CodeReuse/ActiveRecord
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
::Ci::ChangeVariableService.prepend_if_ee('EE::Ci::ChangeVariableService')
|
|
|
@ -1,11 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Ci
|
|
||||||
class ChangeVariablesService < BaseContainerService
|
|
||||||
def execute
|
|
||||||
container.update(params)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
::Ci::ChangeVariablesService.prepend_if_ee('EE::Ci::ChangeVariablesService')
|
|
|
@ -1,4 +1,4 @@
|
||||||
%li
|
%li{ data: { qa_selector: 'wiki_directory_content' } }
|
||||||
= wiki_directory.slug
|
= wiki_directory.slug
|
||||||
%ul
|
%ul
|
||||||
= render wiki_directory.pages, context: context
|
= render wiki_directory.pages, context: context
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add link to issue details page from Incident list page
|
||||||
|
merge_request: 37814
|
||||||
|
author:
|
||||||
|
type: other
|
|
@ -126,6 +126,33 @@ these epics/issues:
|
||||||
- [Unreplicated Data Types](https://gitlab.com/groups/gitlab-org/-/epics/893)
|
- [Unreplicated Data Types](https://gitlab.com/groups/gitlab-org/-/epics/893)
|
||||||
- [Verify all replicated data](https://gitlab.com/groups/gitlab-org/-/epics/1430)
|
- [Verify all replicated data](https://gitlab.com/groups/gitlab-org/-/epics/1430)
|
||||||
|
|
||||||
|
### Replicated data types behind a feature flag
|
||||||
|
|
||||||
|
The replication for some data types is behind a corresponding feature flag:
|
||||||
|
|
||||||
|
> - They're deployed behind a feature flag, enabled by default.
|
||||||
|
> - They're enabled on GitLab.com.
|
||||||
|
> - They can't be enabled or disabled per-project.
|
||||||
|
> - They are recommended for production use.
|
||||||
|
> - For GitLab self-managed instances, GitLab administrators can opt to [disable them](#enable-or-disable-replication-for-some-data-types-core-only). **(CORE ONLY)**
|
||||||
|
|
||||||
|
#### Enable or disable replication (for some data types) **(CORE ONLY)**
|
||||||
|
|
||||||
|
Replication for some data types are released behind feature flags that are **enabled by default**.
|
||||||
|
[GitLab administrators with access to the GitLab Rails console](../../feature_flags.md) can opt to disable it for your instance. You can find feature flag names of each of those data types in the notes column of the table below.
|
||||||
|
|
||||||
|
To disable, such as for package file replication:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Feature.disable(:geo_package_file_replication)
|
||||||
|
```
|
||||||
|
|
||||||
|
To enable, such as for package file replication:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Feature.enable(:geo_package_file_replication)
|
||||||
|
```
|
||||||
|
|
||||||
DANGER: **Danger:**
|
DANGER: **Danger:**
|
||||||
Features not on this list, or with **No** in the **Replicated** column,
|
Features not on this list, or with **No** in the **Replicated** column,
|
||||||
are not replicated on the **secondary** node. Failing over without manually
|
are not replicated on the **secondary** node. Failing over without manually
|
||||||
|
@ -151,12 +178,12 @@ successfully, you must replicate their data using some other means.
|
||||||
| [Elasticsearch integration](../../../integration/elasticsearch.md) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/1186) | No | |
|
| [Elasticsearch integration](../../../integration/elasticsearch.md) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/1186) | No | |
|
||||||
| [GitLab Pages](../../pages/index.md) | [No](https://gitlab.com/groups/gitlab-org/-/epics/589) | No | |
|
| [GitLab Pages](../../pages/index.md) | [No](https://gitlab.com/groups/gitlab-org/-/epics/589) | No | |
|
||||||
| [Container Registry](../../packages/container_registry.md) | **Yes** (12.3) | No | |
|
| [Container Registry](../../packages/container_registry.md) | **Yes** (12.3) | No | |
|
||||||
| [NPM Registry](../../../user/packages/npm_registry/index.md) | **Yes** (13.2) | No | |
|
| [NPM Registry](../../../user/packages/npm_registry/index.md) | **Yes** (13.2) | No | Behind feature flag `geo_package_file_replication`, enabled by default | |
|
||||||
| [Maven Repository](../../../user/packages/maven_repository/index.md) | **Yes** (13.2) | No | |
|
| [Maven Repository](../../../user/packages/maven_repository/index.md) | **Yes** (13.2) | No | Behind feature flag `geo_package_file_replication`, enabled by default | |
|
||||||
| [Conan Repository](../../../user/packages/conan_repository/index.md) | **Yes** (13.2) | No | |
|
| [Conan Repository](../../../user/packages/conan_repository/index.md) | **Yes** (13.2) | No | Behind feature flag `geo_package_file_replication`, enabled by default | |
|
||||||
| [NuGet Repository](../../../user/packages/nuget_repository/index.md) | **Yes** (13.2) | No | |
|
| [NuGet Repository](../../../user/packages/nuget_repository/index.md) | **Yes** (13.2) | No | Behind feature flag `geo_package_file_replication`, enabled by default | |
|
||||||
| [PyPi Repository](../../../user/packages/pypi_repository/index.md) | **Yes** (13.2) | No | |
|
| [PyPi Repository](../../../user/packages/pypi_repository/index.md) | **Yes** (13.2) | No | Behind feature flag `geo_package_file_replication`, enabled by default | |
|
||||||
| [Composer Repository](../../../user/packages/composer_repository/index.md) | **Yes** (13.2) | No | |
|
| [Composer Repository](../../../user/packages/composer_repository/index.md) | **Yes** (13.2) | No | Behind feature flag `geo_package_file_replication`, enabled by default | |
|
||||||
| [External merge request diffs](../../merge_request_diffs.md) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/33817) | No | |
|
| [External merge request diffs](../../merge_request_diffs.md) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/33817) | No | |
|
||||||
| [Terraform State](../../terraform_state.md) | [No](https://gitlab.com/groups/gitlab-org/-/epics/3112)(*3*) | No | |
|
| [Terraform State](../../terraform_state.md) | [No](https://gitlab.com/groups/gitlab-org/-/epics/3112)(*3*) | No | |
|
||||||
| [Vulnerability Export](../../../user/application_security/security_dashboard/#export-vulnerabilities) | [No](https://gitlab.com/groups/gitlab-org/-/epics/3111)(*3*) | No | | |
|
| [Vulnerability Export](../../../user/application_security/security_dashboard/#export-vulnerabilities) | [No](https://gitlab.com/groups/gitlab-org/-/epics/3111)(*3*) | No | | |
|
||||||
|
|
|
@ -141,7 +141,15 @@ keeping in mind the task name in this case is `gitlab:uploads:migrate_to_local`.
|
||||||
|
|
||||||
To migrate uploads from object storage to local storage:
|
To migrate uploads from object storage to local storage:
|
||||||
|
|
||||||
1. Disable both `direct_upload` and `background_upload` under `uploads` settings in `gitlab.rb`.
|
1. Disable both `direct_upload` and `background_upload` under `uploads` settings in `gitlab.rb`:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
gitlab_rails['uploads_object_store_direct_upload'] = false
|
||||||
|
gitlab_rails['uploads_object_store_background_upload'] = false
|
||||||
|
```
|
||||||
|
|
||||||
|
Save the file and [reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure).
|
||||||
|
|
||||||
1. Run the Rake task:
|
1. Run the Rake task:
|
||||||
|
|
||||||
**Omnibus Installation**
|
**Omnibus Installation**
|
||||||
|
|
|
@ -15,16 +15,15 @@ There are several messages that can be displayed depending on the status of the
|
||||||
|
|
||||||
### "Checking pipeline status"
|
### "Checking pipeline status"
|
||||||
|
|
||||||
This message is shown when the merge request has no pipeline associated with the latest commit yet and [Pipelines must succeed](../user/project/merge_requests/merge_when_pipeline_succeeds.md#only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds) is turned on. This might be because:
|
This message is shown when the merge request has no pipeline associated with the latest commit yet. This might be because:
|
||||||
|
|
||||||
- GitLab hasn't finished creating the pipeline yet.
|
- GitLab hasn't finished creating the pipeline yet.
|
||||||
- You are using an external CI service and GitLab hasn't heard back from the service yet.
|
- You are using an external CI service and GitLab hasn't heard back from the service yet.
|
||||||
- You are not using CI/CD pipelines in your project.
|
- You are not using CI/CD pipelines in your project.
|
||||||
|
- The latest pipeline was deleted (this is a [known issue](https://gitlab.com/gitlab-org/gitlab/-/issues/214323)).
|
||||||
|
|
||||||
After the pipeline is created, the message will update with the pipeline status.
|
After the pipeline is created, the message will update with the pipeline status.
|
||||||
|
|
||||||
Note: Currently if you delete the latest pipeline of a Merge Request, this message will be shown instead of a meaningful error message. This is a known issue and should be resolved soon.
|
|
||||||
|
|
||||||
## Merge request ability to merge widget
|
## Merge request ability to merge widget
|
||||||
|
|
||||||
The merge request status widget shows the **Merge** button and whether or not a merge request is ready to merge. If the merge request can't be merged, the reason for this is displayed.
|
The merge request status widget shows the **Merge** button and whether or not a merge request is ready to merge. If the merge request can't be merged, the reason for this is displayed.
|
||||||
|
|
|
@ -90,6 +90,14 @@ module Geo
|
||||||
def self.model
|
def self.model
|
||||||
::Packages::PackageFile
|
::Packages::PackageFile
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Change this to `true` to release replication of this model. Then remove
|
||||||
|
# this override in the next release.
|
||||||
|
# The feature flag follows the format `geo_#{replicable_name}_replication`,
|
||||||
|
# so here it would be `geo_package_file_replication`
|
||||||
|
def self.replication_enabled_by_default?
|
||||||
|
false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
|
@ -69,6 +69,8 @@ You can filter the vulnerabilities by:
|
||||||
- Severity
|
- Severity
|
||||||
- Report type
|
- Report type
|
||||||
|
|
||||||
|
By default, detected and confirmed vulnerabilities will be displayed.
|
||||||
|
|
||||||
You can also dismiss vulnerabilities in the table:
|
You can also dismiss vulnerabilities in the table:
|
||||||
|
|
||||||
1. Select the checkbox for each vulnerability you want to dismiss.
|
1. Select the checkbox for each vulnerability you want to dismiss.
|
||||||
|
@ -97,6 +99,8 @@ You can filter which vulnerabilities the Security Dashboard displays by:
|
||||||
- Report type
|
- Report type
|
||||||
- Project
|
- Project
|
||||||
|
|
||||||
|
By default, detected and confirmed vulnerabilities will be displayed.
|
||||||
|
|
||||||
A table lists the vulnerabilities, sorted by severity. The table shows each vulnerability's status,
|
A table lists the vulnerabilities, sorted by severity. The table shows each vulnerability's status,
|
||||||
severity, and description. Clicking a vulnerability takes you to its [Vulnerability Details](../vulnerabilities)
|
severity, and description. Clicking a vulnerability takes you to its [Vulnerability Details](../vulnerabilities)
|
||||||
page to view more information about that vulnerability.
|
page to view more information about that vulnerability.
|
||||||
|
|
|
@ -51,11 +51,9 @@ module API
|
||||||
optional :variable_type, type: String, values: ::Ci::GroupVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var'
|
optional :variable_type, type: String, values: ::Ci::GroupVariable.variable_types.keys, desc: 'The type of variable, must be one of env_var or file. Defaults to env_var'
|
||||||
end
|
end
|
||||||
post ':id/variables' do
|
post ':id/variables' do
|
||||||
variable = ::Ci::ChangeVariableService.new(
|
variable_params = declared_params(include_missing: false)
|
||||||
container: user_group,
|
|
||||||
current_user: current_user,
|
variable = user_group.variables.create(variable_params)
|
||||||
params: { action: :create, variable_params: declared_params(include_missing: false) }
|
|
||||||
).execute
|
|
||||||
|
|
||||||
if variable.valid?
|
if variable.valid?
|
||||||
present variable, with: Entities::Variable
|
present variable, with: Entities::Variable
|
||||||
|
@ -76,19 +74,17 @@ module API
|
||||||
end
|
end
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
# rubocop: disable CodeReuse/ActiveRecord
|
||||||
put ':id/variables/:key' do
|
put ':id/variables/:key' do
|
||||||
variable = ::Ci::ChangeVariableService.new(
|
variable = user_group.variables.find_by(key: params[:key])
|
||||||
container: user_group,
|
|
||||||
current_user: current_user,
|
|
||||||
params: { action: :update, variable_params: declared_params(include_missing: false) }
|
|
||||||
).execute
|
|
||||||
|
|
||||||
if variable.valid?
|
break not_found!('GroupVariable') unless variable
|
||||||
|
|
||||||
|
variable_params = declared_params(include_missing: false).except(:key)
|
||||||
|
|
||||||
|
if variable.update(variable_params)
|
||||||
present variable, with: Entities::Variable
|
present variable, with: Entities::Variable
|
||||||
else
|
else
|
||||||
render_validation_error!(variable)
|
render_validation_error!(variable)
|
||||||
end
|
end
|
||||||
rescue ::ActiveRecord::RecordNotFound
|
|
||||||
not_found!('GroupVariable')
|
|
||||||
end
|
end
|
||||||
# rubocop: enable CodeReuse/ActiveRecord
|
# rubocop: enable CodeReuse/ActiveRecord
|
||||||
|
|
||||||
|
@ -100,17 +96,10 @@ module API
|
||||||
end
|
end
|
||||||
# rubocop: disable CodeReuse/ActiveRecord
|
# rubocop: disable CodeReuse/ActiveRecord
|
||||||
delete ':id/variables/:key' do
|
delete ':id/variables/:key' do
|
||||||
variable = user_group.variables.find_by!(key: params[:key])
|
variable = user_group.variables.find_by(key: params[:key])
|
||||||
|
not_found!('GroupVariable') unless variable
|
||||||
|
|
||||||
destroy_conditionally!(variable) do |target_variable|
|
destroy_conditionally!(variable)
|
||||||
::Ci::ChangeVariableService.new(
|
|
||||||
container: user_group,
|
|
||||||
current_user: current_user,
|
|
||||||
params: { action: :destroy, variable_params: declared_params(include_missing: false) }
|
|
||||||
).execute
|
|
||||||
end
|
|
||||||
rescue ::ActiveRecord::RecordNotFound
|
|
||||||
not_found!('GroupVariable')
|
|
||||||
end
|
end
|
||||||
# rubocop: enable CodeReuse/ActiveRecord
|
# rubocop: enable CodeReuse/ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,7 @@ gem 'gitlab-qa'
|
||||||
gem 'activesupport', '~> 6.0.3.1' # This should stay in sync with the root's Gemfile
|
gem 'activesupport', '~> 6.0.3.1' # This should stay in sync with the root's Gemfile
|
||||||
gem 'capybara', '~> 3.29.0'
|
gem 'capybara', '~> 3.29.0'
|
||||||
gem 'capybara-screenshot', '~> 1.0.23'
|
gem 'capybara-screenshot', '~> 1.0.23'
|
||||||
gem 'rake', '~> 12.3.0'
|
gem 'rake', '~> 12.3.3'
|
||||||
gem 'rspec', '~> 3.7'
|
gem 'rspec', '~> 3.7'
|
||||||
gem 'selenium-webdriver', '~> 3.12'
|
gem 'selenium-webdriver', '~> 3.12'
|
||||||
gem 'airborne', '~> 0.3.4'
|
gem 'airborne', '~> 0.3.4'
|
||||||
|
|
|
@ -128,7 +128,7 @@ DEPENDENCIES
|
||||||
nokogiri (~> 1.10.9)
|
nokogiri (~> 1.10.9)
|
||||||
parallel_tests (~> 2.29)
|
parallel_tests (~> 2.29)
|
||||||
pry-byebug (~> 3.5.1)
|
pry-byebug (~> 3.5.1)
|
||||||
rake (~> 12.3.0)
|
rake (~> 12.3.3)
|
||||||
rest-client (~> 2.1.0)
|
rest-client (~> 2.1.0)
|
||||||
rotp (~> 3.1.0)
|
rotp (~> 3.1.0)
|
||||||
rspec (~> 3.7)
|
rspec (~> 3.7)
|
||||||
|
|
|
@ -2,7 +2,7 @@ GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
rack (2.0.6)
|
rack (2.0.6)
|
||||||
rake (12.3.0)
|
rake (12.3.3)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
@ -12,4 +12,4 @@ DEPENDENCIES
|
||||||
rake
|
rake
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.17.1
|
1.17.3
|
||||||
|
|
|
@ -18,6 +18,10 @@ module QA
|
||||||
base.view 'app/views/shared/wikis/_sidebar_wiki_page.html.haml' do
|
base.view 'app/views/shared/wikis/_sidebar_wiki_page.html.haml' do
|
||||||
element :wiki_page_link
|
element :wiki_page_link
|
||||||
end
|
end
|
||||||
|
|
||||||
|
base.view 'app/views/shared/wikis/_wiki_directory.html.haml' do
|
||||||
|
element :wiki_directory_content
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def click_clone_repository
|
def click_clone_repository
|
||||||
|
@ -35,6 +39,10 @@ module QA
|
||||||
def has_page_listed?(page_title)
|
def has_page_listed?(page_title)
|
||||||
has_element? :wiki_page_link, page_name: page_title
|
has_element? :wiki_page_link, page_name: page_title
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def has_directory?(directory)
|
||||||
|
has_element? :wiki_directory_content, text: directory
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module QA
|
||||||
|
RSpec.describe 'Create' do
|
||||||
|
context 'Wiki' do
|
||||||
|
let(:initial_wiki) { Resource::Wiki::ProjectPage.fabricate_via_api! }
|
||||||
|
let(:new_path) { "a/new/path" }
|
||||||
|
|
||||||
|
before do
|
||||||
|
Flow::Login.sign_in
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has changed the directory' do
|
||||||
|
initial_wiki.visit!
|
||||||
|
|
||||||
|
Page::Project::Wiki::Show.perform(&:click_edit)
|
||||||
|
|
||||||
|
Page::Project::Wiki::Edit.perform do |edit|
|
||||||
|
edit.set_title("#{new_path}/home")
|
||||||
|
edit.set_message('changing the path of the home page')
|
||||||
|
end
|
||||||
|
|
||||||
|
Page::Project::Wiki::Edit.perform(&:click_save_changes)
|
||||||
|
|
||||||
|
Page::Project::Wiki::Show.perform do |wiki|
|
||||||
|
expect(wiki).to have_directory(new_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,10 +1,17 @@
|
||||||
import { mount } from '@vue/test-utils';
|
import { mount } from '@vue/test-utils';
|
||||||
import { GlAlert, GlLoadingIcon, GlTable, GlAvatar } from '@gitlab/ui';
|
import { GlAlert, GlLoadingIcon, GlTable, GlAvatar } from '@gitlab/ui';
|
||||||
|
import { visitUrl, joinPaths } from '~/lib/utils/url_utility';
|
||||||
import IncidentsList from '~/incidents/components/incidents_list.vue';
|
import IncidentsList from '~/incidents/components/incidents_list.vue';
|
||||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||||
import { I18N } from '~/incidents/constants';
|
import { I18N } from '~/incidents/constants';
|
||||||
import mockIncidents from '../mocks/incidents.json';
|
import mockIncidents from '../mocks/incidents.json';
|
||||||
|
|
||||||
|
jest.mock('~/lib/utils/url_utility', () => ({
|
||||||
|
visitUrl: jest.fn().mockName('visitUrlMock'),
|
||||||
|
joinPaths: jest.fn().mockName('joinPaths'),
|
||||||
|
mergeUrlParams: jest.fn().mockName('mergeUrlParams'),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('Incidents List', () => {
|
describe('Incidents List', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
const newIssuePath = 'namespace/project/-/issues/new';
|
const newIssuePath = 'namespace/project/-/issues/new';
|
||||||
|
@ -36,6 +43,7 @@ describe('Incidents List', () => {
|
||||||
projectPath: '/project/path',
|
projectPath: '/project/path',
|
||||||
newIssuePath,
|
newIssuePath,
|
||||||
incidentTemplateName,
|
incidentTemplateName,
|
||||||
|
issuePath: '/project/isssues',
|
||||||
},
|
},
|
||||||
stubs: {
|
stubs: {
|
||||||
GlButton: true,
|
GlButton: true,
|
||||||
|
@ -111,6 +119,13 @@ describe('Incidents List', () => {
|
||||||
expect(label).toBe(name);
|
expect(label).toBe(name);
|
||||||
expect(src).toBe(avatarUrl);
|
expect(src).toBe(avatarUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('contains a link to the issue details', () => {
|
||||||
|
findTableRows()
|
||||||
|
.at(0)
|
||||||
|
.trigger('click');
|
||||||
|
expect(visitUrl).toHaveBeenCalledWith(joinPaths(`/project/isssues/`, mockIncidents[0].iid));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -124,9 +139,6 @@ describe('Incidents List', () => {
|
||||||
|
|
||||||
it('shows the button linking to new incidents page with prefilled incident template', () => {
|
it('shows the button linking to new incidents page with prefilled incident template', () => {
|
||||||
expect(findCreateIncidentBtn().exists()).toBe(true);
|
expect(findCreateIncidentBtn().exists()).toBe(true);
|
||||||
expect(findCreateIncidentBtn().attributes('href')).toBe(
|
|
||||||
`${newIssuePath}?issuable_template=${incidentTemplateName}`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets button loading on click', () => {
|
it('sets button loading on click', () => {
|
||||||
|
|
|
@ -8,6 +8,7 @@ RSpec.describe Projects::IncidentsHelper do
|
||||||
let(:project) { create(:project) }
|
let(:project) { create(:project) }
|
||||||
let(:project_path) { project.full_path }
|
let(:project_path) { project.full_path }
|
||||||
let(:new_issue_path) { new_project_issue_path(project) }
|
let(:new_issue_path) { new_project_issue_path(project) }
|
||||||
|
let(:issue_path) { project_issues_path(project) }
|
||||||
|
|
||||||
describe '#incidents_data' do
|
describe '#incidents_data' do
|
||||||
subject(:data) { helper.incidents_data(project) }
|
subject(:data) { helper.incidents_data(project) }
|
||||||
|
@ -16,7 +17,8 @@ RSpec.describe Projects::IncidentsHelper do
|
||||||
expect(data).to match(
|
expect(data).to match(
|
||||||
'project-path' => project_path,
|
'project-path' => project_path,
|
||||||
'new-issue-path' => new_issue_path,
|
'new-issue-path' => new_issue_path,
|
||||||
'incident-template-name' => 'incident'
|
'incident-template-name' => 'incident',
|
||||||
|
'issue-path' => issue_path
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -169,14 +169,6 @@ RSpec.describe API::GroupVariables do
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:not_found)
|
expect(response).to have_gitlab_http_status(:not_found)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'responds with 400 if the update fails' do
|
|
||||||
put api("/groups/#{group.id}/variables/#{variable.key}", user), params: { value: 'shrt', masked: true }
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:bad_request)
|
|
||||||
expect(variable.reload.masked).to eq(false)
|
|
||||||
expect(json_response['message']).to eq('value' => ['is invalid'])
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'authorized user with invalid permissions' do
|
context 'authorized user with invalid permissions' do
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe Ci::ChangeVariableService do
|
|
||||||
let(:service) { described_class.new(container: group, current_user: user, params: params) }
|
|
||||||
|
|
||||||
let_it_be(:user) { create(:user) }
|
|
||||||
let(:group) { create(:group) }
|
|
||||||
|
|
||||||
describe '#execute' do
|
|
||||||
subject(:execute) { service.execute }
|
|
||||||
|
|
||||||
context 'when creating a variable' do
|
|
||||||
let(:params) { { variable_params: { key: 'new_variable', value: 'variable_value' }, action: :create } }
|
|
||||||
|
|
||||||
it 'persists a variable' do
|
|
||||||
expect { execute }.to change(Ci::GroupVariable, :count).from(0).to(1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when updating a variable' do
|
|
||||||
let!(:variable) { create(:ci_group_variable, value: 'old_value') }
|
|
||||||
let(:params) { { variable_params: { key: variable.key, value: 'new_value' }, action: :update } }
|
|
||||||
|
|
||||||
before do
|
|
||||||
group.variables << variable
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'updates a variable' do
|
|
||||||
expect { execute }.to change { variable.reload.value }.from('old_value').to('new_value')
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when the variable does not exist' do
|
|
||||||
before do
|
|
||||||
variable.destroy!
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'raises a record not found error' do
|
|
||||||
expect { execute }.to raise_error(::ActiveRecord::RecordNotFound)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when destroying a variable' do
|
|
||||||
let!(:variable) { create(:ci_group_variable) }
|
|
||||||
let(:params) { { variable_params: { key: variable.key }, action: :destroy } }
|
|
||||||
|
|
||||||
before do
|
|
||||||
group.variables << variable
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'destroys a variable' do
|
|
||||||
expect { execute }.to change { Ci::GroupVariable.exists?(variable.id) }.from(true).to(false)
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when the variable does not exist' do
|
|
||||||
before do
|
|
||||||
variable.destroy!
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'raises a record not found error' do
|
|
||||||
expect { execute }.to raise_error(::ActiveRecord::RecordNotFound)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,21 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
RSpec.describe Ci::ChangeVariablesService do
|
|
||||||
let(:service) { described_class.new(container: group, current_user: user, params: params) }
|
|
||||||
|
|
||||||
let_it_be(:user) { create(:user) }
|
|
||||||
let(:group) { spy(:group, variables: []) }
|
|
||||||
let(:params) { { variables_attributes: [{ key: 'new_variable', value: 'variable_value' }] } }
|
|
||||||
|
|
||||||
describe '#execute' do
|
|
||||||
subject(:execute) { service.execute }
|
|
||||||
|
|
||||||
it 'delegates to ActiveRecord update' do
|
|
||||||
execute
|
|
||||||
|
|
||||||
expect(group).to have_received(:update).with(params)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in New Issue