Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-05-21 00:08:06 +00:00
parent 1902e25626
commit 5abd2b70c8
50 changed files with 369 additions and 117 deletions

View File

@ -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'

View File

@ -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"

View File

@ -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,
},
});
},

View File

@ -6,4 +6,5 @@ fragment AlertListItem on AlertManagementAlert {
startedAt
endedAt
eventCount
issueIid
}

View File

@ -0,0 +1,8 @@
mutation ($projectPath: ID!, $iid: String!) {
createAlertIssue(input: { iid: $iid, projectPath: $projectPath }) {
errors
issue {
iid
}
}
}

View File

@ -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">

View File

@ -35,7 +35,7 @@
}
@include media-breakpoint-down(xs) {
.alert-details-create-issue-button {
.alert-details-issue-button {
width: 100%;
}
}

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -5,6 +5,7 @@ module Ci
include AfterCommitQueue
include ObjectStorage::BackgroundMove
include UpdateProjectStatistics
include UsageStatistics
include Sortable
extend Gitlab::Ci::Model

View File

@ -0,0 +1,5 @@
---
title: Fix leaky constant issue in commit entity spec
merge_request: 32039
author: Rajendra Kadam
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Remove usage of spam constants in spec
merge_request: 31959
author: Rajendra Kadam
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Add usage data metrics for terraform reports
merge_request: 31281
author:
type: added

View File

@ -0,0 +1,5 @@
---
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

View File

@ -0,0 +1,18 @@
# 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

View File

@ -0,0 +1,36 @@
# 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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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),

View File

@ -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

View File

@ -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 ""

View File

@ -41,7 +41,7 @@
"@babel/preset-env": "^7.8.4",
"@gitlab/at.js": "1.5.5",
"@gitlab/svgs": "1.127.0",
"@gitlab/ui": "14.10.0",
"@gitlab/ui": "^14.14.2",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.3",
"@sentry/browser": "^5.10.2",

View File

@ -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_job("child_job")
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")
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_job("child_job")
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")
end
end

View File

@ -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_job("child_job")
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")
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_job("child_job")
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")
end
end

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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,
},
});
});

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,15 @@
# 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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,7 +1,9 @@
# 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

View File

@ -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==