提交 5abd2b70 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 1902e256
......@@ -371,10 +371,8 @@ RSpec/LeakyConstantDeclaration:
- 'spec/models/concerns/bulk_insertable_associations_spec.rb'
- 'spec/models/concerns/triggerable_hooks_spec.rb'
- 'spec/models/repository_spec.rb'
- 'spec/serializers/commit_entity_spec.rb'
- 'spec/services/clusters/applications/check_installation_progress_service_spec.rb'
- 'spec/services/clusters/applications/check_uninstall_progress_service_spec.rb'
- 'spec/support/shared_contexts/spam_constants.rb'
- 'spec/support/shared_examples/quick_actions/issuable/issuable_quick_actions_shared_examples.rb'
- 'spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb'
......
......@@ -20,6 +20,8 @@ import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { ALERTS_SEVERITY_LABELS } from '../constants';
import updateAlertStatus from '../graphql/mutations/update_alert_status.graphql';
import createIssueQuery from '../graphql/mutations/create_issue_from_alert.graphql';
import { visitUrl, joinPaths } from '~/lib/utils/url_utility';
export default {
statuses: {
......@@ -60,7 +62,7 @@ export default {
type: String,
required: true,
},
newIssuePath: {
projectIssuesPath: {
type: String,
required: true,
},
......@@ -85,7 +87,13 @@ export default {
},
},
data() {
return { alert: null, errored: false, isErrorDismissed: false };
return {
alert: null,
errored: false,
isErrorDismissed: false,
createIssueError: '',
issueCreationInProgress: false,
};
},
computed: {
loading() {
......@@ -122,6 +130,33 @@ export default {
);
});
},
createIssue() {
this.issueCreationInProgress = true;
this.$apollo
.mutate({
mutation: createIssueQuery,
variables: {
iid: this.alert.iid,
projectPath: this.projectPath,
},
})
.then(({ data: { createAlertIssue: { errors, issue } } }) => {
if (errors?.length) {
[this.createIssueError] = errors;
this.issueCreationInProgress = false;
} else if (issue) {
visitUrl(this.issuePath(issue.iid));
}
})
.catch(error => {
this.createIssueError = error;
this.issueCreationInProgress = false;
});
},
issuePath(issueId) {
return joinPaths(this.projectIssuesPath, issueId);
},
},
};
</script>
......@@ -130,6 +165,14 @@ export default {
<gl-alert v-if="showErrorMsg" variant="danger" @dismiss="dismissError">
{{ $options.i18n.errorMsg }}
</gl-alert>
<gl-alert
v-if="createIssueError"
variant="danger"
data-testid="issueCreationError"
@dismiss="createIssueError = null"
>
{{ createIssueError }}
</gl-alert>
<div v-if="loading"><gl-loading-icon size="lg" class="gl-mt-5" /></div>
<div v-if="alert" class="alert-management-details gl-relative">
<div
......@@ -158,16 +201,29 @@ export default {
<template #tool>{{ alert.monitoringTool }}</template>
</gl-sprintf>
</div>
<gl-button
v-if="glFeatures.createIssueFromAlertEnabled"
class="gl-mt-3 mt-sm-0 align-self-center align-self-sm-baseline alert-details-create-issue-button"
data-testid="createIssueBtn"
:href="newIssuePath"
category="primary"
variant="success"
>
{{ s__('AlertManagement|Create issue') }}
</gl-button>
<div v-if="glFeatures.alertManagementCreateAlertIssue">
<gl-button
v-if="alert.issueIid"
class="gl-mt-3 mt-sm-0 align-self-center align-self-sm-baseline alert-details-issue-button"
data-testid="viewIssueBtn"
:href="issuePath(alert.issueIid)"
category="primary"
variant="success"
>
{{ s__('AlertManagement|View issue') }}
</gl-button>
<gl-button
v-else
class="gl-mt-3 mt-sm-0 align-self-center align-self-sm-baseline alert-details-issue-button"
data-testid="createIssueBtn"
:loading="issueCreationInProgress"
category="primary"
variant="success"
@click="createIssue()"
>
{{ s__('AlertManagement|Create issue') }}
</gl-button>
</div>
</div>
<div
v-if="alert"
......
......@@ -8,7 +8,7 @@ Vue.use(VueApollo);
export default selector => {
const domEl = document.querySelector(selector);
const { alertId, projectPath, newIssuePath } = domEl.dataset;
const { alertId, projectPath, projectIssuesPath } = domEl.dataset;
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(
......@@ -39,7 +39,7 @@ export default selector => {
props: {
alertId,
projectPath,
newIssuePath,
projectIssuesPath,
},
});
},
......
......@@ -6,4 +6,5 @@ fragment AlertListItem on AlertManagementAlert {
startedAt
endedAt
eventCount
issueIid
}
mutation ($projectPath: ID!, $iid: String!) {
createAlertIssue(input: { iid: $iid, projectPath: $projectPath }) {
errors
issue {
iid
}
}
}
......@@ -92,7 +92,7 @@ export default {
@submit="onSubmit"
@cancel="onCancel"
>
<template slot="body" slot-scope="props">
<template #body="props">
<p v-html="props.text"></p>
<p v-html="confirmationTextLabel"></p>
<form ref="form" :action="deleteProjectUrl" method="post">
......
......@@ -35,7 +35,7 @@
}
@include media-breakpoint-down(xs) {
.alert-details-create-issue-button {
.alert-details-issue-button {
width: 100%;
}
}
......
......@@ -4,7 +4,7 @@ class Projects::AlertManagementController < Projects::ApplicationController
before_action :authorize_read_alert_management_alert!
before_action do
push_frontend_feature_flag(:alert_list_status_filtering_enabled)
push_frontend_feature_flag(:create_issue_from_alert_enabled)
push_frontend_feature_flag(:alert_management_create_alert_issue)
push_frontend_feature_flag(:alert_assignee, project)
end
......
......@@ -14,6 +14,7 @@ class Projects::PipelinesController < Projects::ApplicationController
push_frontend_feature_flag(:junit_pipeline_view, project)
push_frontend_feature_flag(:filter_pipelines_search, default_enabled: true)
push_frontend_feature_flag(:dag_pipeline_tab)
push_frontend_feature_flag(:pipelines_security_report_summary, project)
end
before_action :ensure_pipeline, only: [:show]
......
......@@ -53,7 +53,7 @@ class Projects::RefsController < Projects::ApplicationController
format.json do
logs, next_offset = tree_summary.fetch_logs
response.headers["More-Logs-Offset"] = next_offset if next_offset
response.headers["More-Logs-Offset"] = next_offset.to_s if next_offset
render json: logs
end
......
......@@ -15,7 +15,7 @@ module Projects::AlertManagementHelper
{
'alert-id' => alert_id,
'project-path' => project.full_path,
'new-issue-path' => new_project_issue_path(project)
'project-issues-path' => project_issues_path(project)
}
end
end
......@@ -5,6 +5,7 @@ module Ci
include AfterCommitQueue
include ObjectStorage::BackgroundMove
include UpdateProjectStatistics
include UsageStatistics
include Sortable
extend Gitlab::Ci::Model
......
---
title: Fix leaky constant issue in commit entity spec
merge_request: 32039
author: Rajendra Kadam
type: fixed
---
title: Remove usage of spam constants in spec
merge_request: 31959
author: Rajendra Kadam
type: fixed
---
title: Add usage data metrics for terraform reports
merge_request: 31281
author:
type: added
---
title: Update deprecated slot syntax in ./app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue
merge_request: 31994
author: Gilang Gumilar
type: other
# frozen_string_literal: true
class AddIndexToCiJobArtifactsForTerraformReports < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
INDEX_NAME = 'index_ci_job_artifacts_for_terraform_reports'
disable_ddl_transaction!
def up
add_concurrent_index :ci_job_artifacts, [:project_id, :id], where: 'file_type = 18', name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :ci_job_artifacts, INDEX_NAME
end
end
# frozen_string_literal: true
class UpdateIndexOnNameTypeEqCiBuildToCiBuilds < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
OLD_INDEX_NAME = 'index_ci_builds_on_name_and_security_type_eq_ci_build'
NEW_INDEX_NAME = 'index_security_ci_builds_on_name_and_id'
OLD_CLAUSE = "((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text,
('dast'::character varying)::text,
('dependency_scanning'::character varying)::text,
('license_management'::character varying)::text,
('sast'::character varying)::text,
('license_scanning'::character varying)::text])) AND ((type)::text = 'Ci::Build'::text)"
NEW_CLAUSE = "((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text,
('dast'::character varying)::text,
('dependency_scanning'::character varying)::text,
('license_management'::character varying)::text,
('sast'::character varying)::text,
('secret_detection'::character varying)::text,
('license_scanning'::character varying)::text])) AND ((type)::text = 'Ci::Build'::text)"
def up
add_concurrent_index :ci_builds, [:name, :id], name: NEW_INDEX_NAME, where: NEW_CLAUSE
remove_concurrent_index_by_name :ci_builds, OLD_INDEX_NAME
end
def down
add_concurrent_index :ci_builds, [:name, :id], name: OLD_INDEX_NAME, where: OLD_CLAUSE
remove_concurrent_index_by_name :ci_builds, NEW_INDEX_NAME
end
end
......@@ -9235,8 +9235,6 @@ CREATE INDEX index_ci_builds_on_commit_id_and_type_and_name_and_ref ON public.ci
CREATE INDEX index_ci_builds_on_commit_id_and_type_and_ref ON public.ci_builds USING btree (commit_id, type, ref);
CREATE INDEX index_ci_builds_on_name_and_security_type_eq_ci_build ON public.ci_builds USING btree (name, id) WHERE (((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('sast'::character varying)::text, ('license_scanning'::character varying)::text])) AND ((type)::text = 'Ci::Build'::text));
CREATE INDEX index_ci_builds_on_project_id_and_id ON public.ci_builds USING btree (project_id, id);
CREATE INDEX index_ci_builds_on_project_id_and_name_and_ref ON public.ci_builds USING btree (project_id, name, ref) WHERE (((type)::text = 'Ci::Build'::text) AND ((status)::text = 'success'::text) AND ((retried = false) OR (retried IS NULL)));
......@@ -9281,6 +9279,8 @@ CREATE UNIQUE INDEX index_ci_instance_variables_on_key ON public.ci_instance_var
CREATE INDEX index_ci_job_artifacts_file_store_is_null ON public.ci_job_artifacts USING btree (id) WHERE (file_store IS NULL);
CREATE INDEX index_ci_job_artifacts_for_terraform_reports ON public.ci_job_artifacts USING btree (project_id, id) WHERE (file_type = 18);
CREATE INDEX index_ci_job_artifacts_on_expire_at_and_job_id ON public.ci_job_artifacts USING btree (expire_at, job_id);
CREATE INDEX index_ci_job_artifacts_on_file_store ON public.ci_job_artifacts USING btree (file_store);
......@@ -10607,6 +10607,8 @@ CREATE UNIQUE INDEX index_scim_identities_on_user_id_and_group_id ON public.scim
CREATE UNIQUE INDEX index_scim_oauth_access_tokens_on_group_id_and_token_encrypted ON public.scim_oauth_access_tokens USING btree (group_id, token_encrypted);
CREATE INDEX index_security_ci_builds_on_name_and_id ON public.ci_builds USING btree (name, id) WHERE (((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('sast'::character varying)::text, ('secret_detection'::character varying)::text, ('license_scanning'::character varying)::text])) AND ((type)::text = 'Ci::Build'::text));
CREATE INDEX index_self_managed_prometheus_alert_events_on_environment_id ON public.self_managed_prometheus_alert_events USING btree (environment_id);
CREATE INDEX index_sent_notifications_on_noteable_type_noteable_id ON public.sent_notifications USING btree (noteable_id) WHERE ((noteable_type)::text = 'Issue'::text);
......@@ -13899,7 +13901,9 @@ COPY "schema_migrations" (version) FROM STDIN;
20200511208012
20200511220023
20200512085150
20200512160004
20200512164334
20200512195442
20200513160930
20200513171959
20200513224143
......
......@@ -118,7 +118,8 @@ job log using a regular expression. In the pipelines settings, search for the
![Pipelines settings test coverage](img/pipelines_settings_test_coverage.png)
Leave blank if you want to disable it or enter a Ruby regular expression. You
can use <https://rubular.com> to test your regex.
can use <https://rubular.com> to test your regex. The regex returns the **last**
match found in the output.
If the pipeline succeeds, the coverage is shown in the merge request widget and
in the jobs table.
......
......@@ -70,12 +70,11 @@ registry.
The project using the `Secure-Binaries.gitlab-ci.yml` template should now host all the required
images and resources needed to run GitLab Security features.
The next step is to tell the offline instance to use these resources instead of the default ones on
`gitlab.com`. This can be done by setting the right environment variables:
`SAST_ANALYZER_IMAGE_PREFIX` for SAST analyzers, `DS_ANALYZER_IMAGE_PREFIX` for Dependency Scanning,
and so on.
Next, you must tell the offline instance to use these resources instead of the default ones on
GitLab.com. To do so, set the environment variable `SECURE_ANALYZERS_PREFIX` with the URL of the
project [container registry](../../user/packages/container_registry/index.md).
You can set these variables in the project's `.gitlab-ci.yml` files by using the bundle directly, or
You can set this variable in the projects' `.gitlab-ci.yml`, or
in the GitLab UI at the project or group level. See the [GitLab CI/CD environment variables page](../../ci/variables/README.md#custom-environment-variables)
for more information.
......
......@@ -313,19 +313,20 @@ Some analyzers can be customized with environment variables.
| Environment variable | Analyzer | Description |
|-----------------------------|----------|-------------|
| `SCAN_KUBERNETES_MANIFESTS` | Kubesec | Set to `"true"` to scan Kubernetes manifests. |
| `ANT_HOME` | SpotBugs | The `ANT_HOME` environment variable. |
| `ANT_PATH` | SpotBugs | Path to the `ant` executable. |
| `GRADLE_PATH` | SpotBugs | Path to the `gradle` executable. |
| `JAVA_OPTS` | SpotBugs | Additional arguments for the `java` executable. |
| `JAVA_PATH` | SpotBugs | Path to the `java` executable. |
| `SAST_JAVA_VERSION` | SpotBugs | Which Java version to use. Supported versions are `8` and `11`. Defaults to `8`. |
| `MAVEN_CLI_OPTS` | SpotBugs | Additional arguments for the `mvn` or `mvnw` executable. |
| `MAVEN_PATH` | SpotBugs | Path to the `mvn` executable. |
| `MAVEN_REPO_PATH` | SpotBugs | Path to the Maven local repository (shortcut for the `maven.repo.local` property). |
| `SBT_PATH` | SpotBugs | Path to the `sbt` executable. |
| `FAIL_NEVER` | SpotBugs | Set to `1` to ignore compilation failure. |
| `SAST_GOSEC_CONFIG` | Gosec | Path to configuration for Gosec (optional). |
| `SCAN_KUBERNETES_MANIFESTS` | Kubesec | Set to `"true"` to scan Kubernetes manifests. |
| `ANT_HOME` | SpotBugs | The `ANT_HOME` environment variable. |
| `ANT_PATH` | SpotBugs | Path to the `ant` executable. |
| `GRADLE_PATH` | SpotBugs | Path to the `gradle` executable. |
| `JAVA_OPTS` | SpotBugs | Additional arguments for the `java` executable. |
| `JAVA_PATH` | SpotBugs | Path to the `java` executable. |
| `SAST_JAVA_VERSION` | SpotBugs | Which Java version to use. Supported versions are `8` and `11`. Defaults to `8`. |
| `MAVEN_CLI_OPTS` | SpotBugs | Additional arguments for the `mvn` or `mvnw` executable. |
| `MAVEN_PATH` | SpotBugs | Path to the `mvn` executable. |
| `MAVEN_REPO_PATH` | SpotBugs | Path to the Maven local repository (shortcut for the `maven.repo.local` property). |
| `SBT_PATH` | SpotBugs | Path to the `sbt` executable. |
| `FAIL_NEVER` | SpotBugs | Set to `1` to ignore compilation failure. |
| `SAST_GOSEC_CONFIG` | Gosec | Path to configuration for Gosec (optional). |
| `PHPCS_SECURITY_AUDIT_PHP_EXTENSIONS` | phpcs-security-audit | Comma separated list of additional PHP Extensions. |
#### Custom environment variables
......
......@@ -25,7 +25,7 @@ module Gitlab
if Gitlab::RuggedInstrumentation.active?
Gitlab::RuggedInstrumentation.increment_query_count
Gitlab::RuggedInstrumentation.query_time += duration
Gitlab::RuggedInstrumentation.add_query_time(duration)
Gitlab::RuggedInstrumentation.add_call_details(
feature: method_name,
......
......@@ -201,7 +201,8 @@ module Gitlab
request_hash = request.is_a?(Google::Protobuf::MessageExts) ? request.to_h : {}
# Keep track, separately, for the performance bar
self.query_time += duration
self.add_query_time(duration)
if Gitlab::PerformanceBar.enabled_for_request?
add_call_details(feature: "#{service}##{rpc}", duration: duration, request: request_hash, rpc: rpc,
backtrace: Gitlab::BacktraceCleaner.clean_backtrace(caller))
......@@ -209,12 +210,15 @@ module Gitlab
end
def self.query_time
query_time = SafeRequestStore[:gitaly_query_time] ||= 0
query_time = Gitlab::SafeRequestStore[:gitaly_query_time] || 0
query_time.round(Gitlab::InstrumentationHelper::DURATION_PRECISION)
end
def self.query_time=(duration)
SafeRequestStore[:gitaly_query_time] = duration
def self.add_query_time(duration)
return unless Gitlab::SafeRequestStore.active?
Gitlab::SafeRequestStore[:gitaly_query_time] ||= 0
Gitlab::SafeRequestStore[:gitaly_query_time] += duration
end
def self.current_transaction_labels
......
......@@ -46,8 +46,8 @@ module Gitlab
end
def self.add_duration(duration)
total_time = query_time + duration
::RequestStore[REDIS_CALL_DURATION] = total_time
::RequestStore[REDIS_CALL_DURATION] ||= 0
::RequestStore[REDIS_CALL_DURATION] += duration
end
def self.add_call_details(duration, args)
......
......@@ -3,12 +3,13 @@
module Gitlab
module RuggedInstrumentation
def self.query_time
query_time = SafeRequestStore[:rugged_query_time] ||= 0
query_time = SafeRequestStore[:rugged_query_time] || 0
query_time.round(Gitlab::InstrumentationHelper::DURATION_PRECISION)
end
def self.query_time=(duration)
SafeRequestStore[:rugged_query_time] = duration
def self.add_query_time(duration)
SafeRequestStore[:rugged_query_time] ||= 0
SafeRequestStore[:rugged_query_time] += duration
end
def self.query_time_ms
......
......@@ -131,11 +131,13 @@ module Gitlab
projects_with_error_tracking_enabled: count(::ErrorTracking::ProjectErrorTrackingSetting.where(enabled: true)),
projects_with_alerts_service_enabled: count(AlertsService.active),
projects_with_prometheus_alerts: distinct_count(PrometheusAlert, :project_id),
projects_with_terraform_reports: distinct_count(::Ci::JobArtifact.terraform_reports, :project_id),
protected_branches: count(ProtectedBranch),
releases: count(Release),
remote_mirrors: count(RemoteMirror),
snippets: count(Snippet),
suggestions: count(Suggestion),
terraform_reports: count(::Ci::JobArtifact.terraform_reports),
todos: count(Todo),
uploads: count(Upload),
web_hooks: count(WebHook),
......
......@@ -40,12 +40,6 @@ module Peek
super.merge(request: pretty_request || {})
end
def setup_subscribers
subscribe 'start_processing.action_controller' do
::Gitlab::GitalyClient.query_time = 0
end
end
end
end
end
......@@ -219,6 +219,16 @@ msgid_plural "%d unresolved threads"
msgstr[0] ""
msgstr[1] ""
msgid "%d url scanned"
msgid_plural "%d urls scanned"
msgstr[0] ""
msgstr[1] ""
msgid "%d vulnerability"
msgid_plural "%d vulnerabilities"
msgstr[0] ""
msgstr[1] ""
msgid "%d vulnerability dismissed"
msgid_plural "%d vulnerabilities dismissed"
msgstr[0] ""
......@@ -1847,6 +1857,9 @@ msgstr ""
msgid "AlertManagement|Unknown"
msgstr ""
msgid "AlertManagement|View issue"
msgstr ""
msgid "AlertService|%{linkStart}Learn more%{linkEnd} about configuring this endpoint to receive alerts."
msgstr ""
......
......@@ -29,10 +29,12 @@ module QA
view_pipelines
Page::Project::Pipeline::Show.perform do |parent_pipeline|
parent_pipeline.click_linked_job(project.name)
expect(parent_pipeline).to have_passed
parent_pipeline.retry_on_exception(sleep_interval: 1.0) do
parent_pipeline.click_linked_job(project.name)
end
expect(parent_pipeline).to have_job("child_job")
expect(parent_pipeline).to have_passed
end
end
......@@ -41,10 +43,12 @@ module QA
view_pipelines
Page::Project::Pipeline::Show.perform do |parent_pipeline|
parent_pipeline.click_linked_job(project.name)
expect(parent_pipeline).to have_failed
parent_pipeline.retry_on_exception(sleep_interval: 1.0) do
parent_pipeline.click_linked_job(project.name)
end
expect(parent_pipeline).to have_job("child_job")
expect(parent_pipeline).to have_failed
end
end
......
......@@ -29,10 +29,12 @@ module QA
view_pipelines
Page::Project::Pipeline::Show.perform do |parent_pipeline|
parent_pipeline.click_linked_job(project.name)
expect(parent_pipeline).to have_passed
parent_pipeline.retry_on_exception(reload: true, sleep_interval: 1.0) do
parent_pipeline.click_linked_job(project.name)
end
expect(parent_pipeline).to have_job("child_job")
expect(parent_pipeline).to have_passed
end
end
......@@ -41,10 +43,12 @@ module QA
view_pipelines
Page::Project::Pipeline::Show.perform do |parent_pipeline|
parent_pipeline.click_linked_job(project.name)
expect(parent_pipeline).to have_passed
parent_pipeline.retry_on_exception(reload: true, sleep_interval: 1.0) do
parent_pipeline.click_linked_job(project.name)
end
expect(parent_pipeline).to have_job("child_job")
expect(parent_pipeline).to have_passed
end
end
......
......@@ -73,7 +73,7 @@ describe Projects::RefsController do
cache_key = "projects/#{project.id}/logs/#{project.commit.id}/#{path}/25"
expect(Rails.cache.fetch(cache_key)).to eq(['logs', 50])
expect(response.headers['More-Logs-Offset']).to eq(50)
expect(response.headers['More-Logs-Offset']).to eq("50")
end
end
end
......
......@@ -33,6 +33,9 @@ FactoryBot.define do
issues = create_list(:issue, 4, project: projects[0])
create_list(:prometheus_alert, 2, project: projects[0])
create(:prometheus_alert, project: projects[1])
create(:merge_request, :simple, :with_terraform_reports, source_project: projects[0])
create(:merge_request, :rebased, :with_terraform_reports, source_project: projects[0])
create(:merge_request, :simple, :with_terraform_reports, source_project: projects[1])
create(:zoom_meeting, project: projects[0], issue: projects[0].issues[0], issue_status: :added)
create_list(:zoom_meeting, 2, project: projects[0], issue: projects[0].issues[1], issue_status: :removed)
create(:zoom_meeting, project: projects[0], issue: projects[0].issues[2], issue_status: :added)
......
......@@ -164,6 +164,8 @@ describe 'New issue', :js do
end
context 'when the SpamVerdictService allows' do
include_context 'includes Spam constants'
before do
allow_next_instance_of(Spam::SpamVerdictService) do |verdict_service|
allow(verdict_service).to receive(:execute).and_return(ALLOW)
......
......@@ -2,7 +2,9 @@ import { mount, shallowMount } from '@vue/test-utils';
import { GlAlert, GlLoadingIcon, GlDropdownItem, GlTable } from '@gitlab/ui';
import AlertDetails from '~/alert_management/components/alert_details.vue';
import updateAlertStatus from '~/alert_management/graphql/mutations/update_alert_status.graphql';
import createIssueQuery from '~/alert_management/graphql/mutations/create_issue_from_alert.graphql';
import createFlash from '~/flash';
import { joinPaths } from '~/lib/utils/url_utility';
import mockAlerts from '../mocks/alerts.json';
......@@ -11,13 +13,15 @@ jest.mock('~/flash');
describe('AlertDetails', () => {
let wrapper;
const newIssuePath = 'root/alerts/-/issues/new';
const projectPath = 'root/alerts';
const projectIssuesPath = 'root/alerts/-/issues';
const findStatusDropdownItem = () => wrapper.find(GlDropdownItem);
const findDetailsTable = () => wrapper.find(GlTable);
function mountComponent({
data,
createIssueFromAlertEnabled = false,
alertManagementCreateAlertIssue = false,
loading = false,
mountMethod = shallowMount,
stubs = {},
......@@ -25,14 +29,14 @@ describe('AlertDetails', () => {
wrapper = mountMethod(AlertDetails, {
propsData: {
alertId: 'alertId',
projectPath: 'projectPath',
newIssuePath,
projectPath,
projectIssuesPath,
},
data() {
return { alert: { ...mockAlert }, ...data };
},
provide: {
glFeatures: { createIssueFromAlertEnabled },
glFeatures: { alertManagementCreateAlertIssue },
},
mocks: {
$apollo: {
......@@ -50,11 +54,15 @@ describe('AlertDetails', () => {
afterEach(() => {
if (wrapper) {
wrapper.destroy();
if (wrapper) {
wrapper.destroy();
}
}
});
const findCreatedIssueBtn = () => wrapper.find('[data-testid="createIssueBtn"]');
const findCreateIssueBtn = () => wrapper.find('[data-testid="createIssueBtn"]');
const findViewIssueBtn = () => wrapper.find('[data-testid="viewIssueBtn"]');
const findIssueCreationAlert = () => wrapper.find('[data-testid="issueCreationError"]');
describe('Alert details', () => {
describe('when alert is null', () => {
......@@ -118,17 +126,68 @@ describe('AlertDetails', () => {
describe('Create issue from alert', () => {
describe('createIssueFromAlertEnabled feature flag enabled', () => {
it('should display a button that links to new issue page', () => {
mountComponent({ createIssueFromAlertEnabled: true });
expect(findCreatedIssueBtn().exists()).toBe(true);
expect(findCreatedIssueBtn().attributes('href')).toBe(newIssuePath);
it('should display "View issue" button that links the issue page when issue exists', () => {
const issueIid = '3';
mountComponent({
alertManagementCreateAlertIssue: true,
data: { alert: { ...mockAlert, issueIid } },
});
expect(findViewIssueBtn().exists()).toBe(true);
expect(findViewIssueBtn().attributes('href')).toBe(
joinPaths(projectIssuesPath, issueIid),
);
expect(findCreateIssueBtn().exists()).toBe(false);
});
it('should display "Create issue" button when issue doesn\'t exist yet', () => {
const issueIid = null;
mountComponent({
mountMethod: mount,
alertManagementCreateAlertIssue: true,
data: { alert: { ...mockAlert, issueIid } },
});
expect(findViewIssueBtn().exists()).toBe(false);
expect(findCreateIssueBtn().exists()).toBe(true);
});
it('calls `$apollo.mutate` with `createIssueQuery`', () => {
const issueIid = '10';
jest
.spyOn(wrapper.vm.$apollo, 'mutate')
.mockResolvedValue({ data: { createAlertIssue: { issue: { iid: issueIid } } } });
findCreateIssueBtn().trigger('click');
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: createIssueQuery,
variables: {
iid: mockAlert.iid,
projectPath,
},
});
});
it('shows error alert when issue creation fails ', () => {
const errorMsg = 'Something went wrong';
mountComponent({
mountMethod: mount,
alertManagementCreateAlertIssue: true,
data: { alert: { ...mockAlert, alertIid: 1 } },
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue(errorMsg);
findCreateIssueBtn().trigger('click');
setImmediate(() => {
expect(findIssueCreationAlert().text()).toBe(errorMsg);
});
});
});
describe('createIssueFromAlertEnabled feature flag disabled', () => {
it('should display a button that links to a new issue page', () => {
mountComponent({ createIssueFromAlertEnabled: false });
expect(findCreatedIssueBtn().exists()).toBe(false);
it('should not display a View or Create issue button', () => {
mountComponent({ alertManagementCreateAlertIssue: false });
expect(findCreateIssueBtn().exists()).toBe(false);
expect(findViewIssueBtn().exists()).toBe(false);
});
});
});
......@@ -223,7 +282,7 @@ describe('AlertDetails', () => {
variables: {
iid: 'alertId',
status: 'TRIGGERED',
projectPath: 'projectPath',
projectPath,
},
});
});
......
......@@ -4,6 +4,7 @@
"title": "SyntaxError: Invalid or unexpected token",
"severity": "CRITICAL",
"eventCount": 7,
"createdAt": "2020-04-17T23:18:14.996Z",
"startedAt": "2020-04-17T23:18:14.996Z",
"endedAt": "2020-04-17T23:18:14.996Z",
"status": "TRIGGERED"
......
......@@ -20,7 +20,10 @@ exports[`Contributors charts should render charts when loading completed and the
height="264"
includelegendavgmax="true"
legendaveragetext="Avg"
legendcurrenttext="Current"
legendlayout="inline"
legendmaxtext="Max"
legendmintext="Min"
option="[object Object]"
thresholds=""
width="0"
......@@ -48,7 +51,10 @@ exports[`Contributors charts should render charts when loading completed and the
height="216"
includelegendavgmax="true"
legendaveragetext="Avg"
legendcurrenttext="Current"
legendlayout="inline"
legendmaxtext="Max"
legendmintext="Min"
option="[object Object]"
thresholds=""
width="0"
......
......@@ -14,7 +14,10 @@ exports[`PipelinesAreaChart matches the snapshot 1`] = `
data="[object Object],[object Object]"
height="300"
legendaveragetext="Avg"
legendcurrenttext="Current"
legendlayout="inline"
legendmaxtext="Max"
legendmintext="Min"
option="[object Object]"
thresholds=""
width="0"
......
......@@ -69,13 +69,13 @@ describe Projects::AlertManagementHelper do
describe '#alert_management_detail_data' do
let(:alert_id) { 1 }
let(:new_issue_path) { new_project_issue_path(project) }
let(:issues_path) { project_issues_path(project) }
it 'returns detail page configuration' do
expect(helper.alert_management_detail_data(project, alert_id)).to eq(
'alert-id' => alert_id,
'project-path' => project_path,
'new-issue-path' => new_issue_path
'project-issues-path' => issues_path
)
end
end
......
......@@ -21,10 +21,10 @@ describe Gitlab::GitalyClient do
describe '.query_time', :request_store do
it 'increments query times' do
subject.query_time += 0.451
subject.query_time += 0.322
subject.add_query_time(0.4510004)
subject.add_query_time(0.3220004)
expect(subject.query_time).to eq(0.773)
expect(subject.query_time).to eq(0.773001)
end
end
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Instrumentation::Redis do
describe '.add_duration', :request_store do
it 'does not lose precision while adding' do
precision = 1.0 / (10**::Gitlab::InstrumentationHelper::DURATION_PRECISION)
2.times { described_class.add_duration(0.4 * precision) }
# 2 * 0.4 should be 0.8 and get rounded to 1
expect(described_class.query_time).to eq(1 * precision)
end
end
end
......@@ -7,10 +7,10 @@ describe Gitlab::RuggedInstrumentation, :request_store do
describe '.query_time' do
it 'increments query times' do
subject.query_time += 0.451
subject.query_time += 0.322
subject.add_query_time(0.4510004)
subject.add_query_time(0.3220004)
expect(subject.query_time).to be_within(0.001).of(0.773)
expect(subject.query_time).to eq(0.773001)
expect(subject.query_time_ms).to eq(773.0)
end
end
......
......@@ -13,7 +13,7 @@ describe Gitlab::SidekiqMiddleware do
def perform(_arg)
Gitlab::SafeRequestStore['gitaly_call_actual'] = 1
Gitlab::GitalyClient.query_time = 5
Gitlab::SafeRequestStore[:gitaly_query_time] = 5
end
end
end
......
......@@ -57,6 +57,8 @@ describe Gitlab::UsageData, :aggregate_failures do
expect(count_data[:projects_with_error_tracking_enabled]).to eq(1)
expect(count_data[:projects_with_alerts_service_enabled]).to eq(1)
expect(count_data[:projects_with_prometheus_alerts]).to eq(2)
expect(count_data[:projects_with_terraform_reports]).to eq(2)
expect(count_data[:terraform_reports]).to eq(3)
expect(count_data[:issues_created_from_gitlab_error_tracking_ui]).to eq(1)
expect(count_data[:issues_with_associated_zoom_link]).to eq(2)
expect(count_data[:issues_using_zoom_quick_actions]).to eq(3)
......
......@@ -16,7 +16,7 @@ describe Peek::Views::Rugged, :request_store do
end
it 'returns aggregated results' do
::Gitlab::RuggedInstrumentation.query_time += 1.234
::Gitlab::RuggedInstrumentation.add_query_time(1.234)
::Gitlab::RuggedInstrumentation.increment_query_count
::Gitlab::RuggedInstrumentation.increment_query_count
......
......@@ -3,7 +3,7 @@
require 'spec_helper'
describe CommitEntity do
SIGNATURE_HTML = 'TEST'.freeze
let(:signature_html) { 'TEST' }
let(:entity) do
described_class.new(commit, request: request)
......@@ -16,7 +16,7 @@ describe CommitEntity do
before do
render = double('render')
allow(render).to receive(:call).and_return(SIGNATURE_HTML)
allow(render).to receive(:call).and_return(signature_html)
allow(request).to receive(:project).and_return(project)
allow(request).to receive(:render).and_return(render)
......@@ -83,7 +83,7 @@ describe CommitEntity do
it 'exposes "signature_html"' do
expect(request.render).to receive(:call)
expect(subject.fetch(:signature_html)).to be SIGNATURE_HTML
expect(subject.fetch(:signature_html)).to be signature_html
end
end
......
......@@ -117,12 +117,14 @@ module UsageDataHelpers
projects_with_expiration_policy_enabled_with_cadence_set_to_14d
projects_with_expiration_policy_enabled_with_cadence_set_to_1month
projects_with_expiration_policy_enabled_with_cadence_set_to_3month
projects_with_terraform_reports
pages_domains
protected_branches
releases
remote_mirrors
snippets
suggestions
terraform_reports
todos
uploads
web_hooks
......
# frozen_string_literal: true
shared_context 'includes Spam constants' do
REQUIRE_RECAPTCHA = Spam::SpamConstants::REQUIRE_RECAPTCHA
DISALLOW = Spam::SpamConstants::DISALLOW
ALLOW = Spam::SpamConstants::ALLOW
before do
stub_const('REQUIRE_RECAPTCHA', Spam::SpamConstants::REQUIRE_RECAPTCHA)
stub_const('DISALLOW', Spam::SpamConstants::DISALLOW)
stub_const('ALLOW', Spam::SpamConstants::ALLOW)
end
end
......@@ -787,10 +787,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.127.0.tgz#1f7ffdffe44d6a82b372535f93d78f3a895d1960"
integrity sha512-Uv52DqkG2KwCB0VRlXUEHFZxJ/7Ql0t1YTdzICpXmDjltuUBrysFcdmWPVO6PgXQxk2ahryNsUjSOziMYTeSiw==
"@gitlab/ui@14.10.0":
version "14.10.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-14.10.0.tgz#39c04d62c914fcefe96c7ec32fdf31b1f98f1119"
integrity sha512-k9w6z3/QBeUas++cH5BaozjxY4fVu+AggjGoh9QMKN5hpiGTiTPx5aQJIlOv8UX/kpUmgc4pHSWAbw30YVGGFw==
"@gitlab/ui@^14.14.2":
version "14.14.2"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-14.14.2.tgz#7cc81d90d5b5394345d6781ff02e974e24b97387"
integrity sha512-Fq7fGjhofnN64xckTuuuX4EE23ZXcndwCfFBFrCTCbDfrDSa0l0xkmkrvYCSrNNTp6CyL5Ec/LWgGcnGCPWaFw==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"
......@@ -9099,16 +9099,7 @@ postcss-value-parser@^4.0.0:
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.0.tgz#99a983d365f7b2ad8d0f9b8c3094926eab4b936d"
integrity sha512-ESPktioptiSUchCKgggAkzdmkgzKfmp0EU8jXH+5kbIUB+unr0Y4CY9SRMvibuvYUBjNh1ACLbxqYNpdTQOteQ==
postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.27, postcss@^7.0.7:
version "7.0.27"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.27.tgz#cc67cdc6b0daa375105b7c424a85567345fc54d9"
integrity sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==
dependencies:
chalk "^2.4.2"
source-map "^0.6.1"
supports-color "^6.1.0"
postcss@^7.0.5, postcss@^7.0.6:
postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.27, postcss@^7.0.5, postcss@^7.0.6, postcss@^7.0.7:
version "7.0.30"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.30.tgz#cc9378beffe46a02cbc4506a0477d05fcea9a8e2"
integrity sha512-nu/0m+NtIzoubO+xdAlwZl/u5S5vi/y6BCsoL8D+8IxsD3XvBS8X4YEADNIVXKVuQvduiucnRv+vPIqj56EGMQ==
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册