Commit b47bbab7 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents e6d53f3f 08501ae2
dfdc9b7725eb710dab8ae9970e98cc5118e65c49
092d4e489d7de1dcc38d57a4c667e85df8b8377f
......@@ -2,7 +2,7 @@
import { GlTable, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import Tracking from '~/tracking';
import { trackAlertIntergrationsViewsOptions } from '../constants';
import { trackAlertIntegrationsViewsOptions } from '../constants';
export const i18n = {
title: s__('AlertsIntegrations|Current integrations'),
......@@ -64,7 +64,7 @@ export default {
},
methods: {
trackPageViews() {
const { category, action } = trackAlertIntergrationsViewsOptions;
const { category, action } = trackAlertIntegrationsViewsOptions;
Tracking.event(category, action);
},
},
......
<script>
import {
GlButton,
GlCollapse,
GlForm,
GlFormGroup,
GlFormSelect,
GlFormInput,
GlLink,
GlSprintf,
} from '@gitlab/ui';
import { s__ } from '~/locale';
import { integrationTypes } from '../constants';
export default {
i18n: {
integrationsInfo: s__(
'AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}',
),
integrationFormSteps: {
step1: s__('AlertSettings|1. Select integration type'),
step2: s__('AlertSettings|2. Name integration'),
},
},
components: {
GlButton,
GlCollapse,
GlForm,
GlFormGroup,
GlFormInput,
GlFormSelect,
GlLink,
GlSprintf,
},
data() {
return {
selectedIntegration: integrationTypes[0].value,
options: integrationTypes,
formVisible: false,
form: {
name: '',
},
};
},
methods: {
onIntegrationTypeSelect() {
if (this.selectedIntegration === integrationTypes[0].value) {
this.formVisible = false;
} else {
this.formVisible = true;
}
},
onSubmit() {
// TODO Add GraphQL method
},
onReset() {
this.form.name = '';
},
},
};
</script>
<template>
<gl-form class="gl-mt-6" @submit.prevent="onSubmit" @reset.prevent="onReset">
<h5 class="gl-font-lg gl-my-5">{{ s__('AlertSettings|Add new integrations') }}</h5>
<gl-form-group
id="integration-type"
:label="$options.i18n.integrationFormSteps.step1"
label-for="integration-type"
>
<gl-form-select
id="integration-type"
v-model="selectedIntegration"
:options="options"
@change="onIntegrationTypeSelect"
/>
<span class="gl-text-gray-500">
<gl-sprintf :message="$options.i18n.integrationsInfo">
<template #link="{ content }">
<gl-link
class="gl-display-inline-block"
href="https://gitlab.com/groups/gitlab-org/-/epics/4390"
target="_blank"
>{{ content }}</gl-link
>
</template>
</gl-sprintf>
</span>
</gl-form-group>
<gl-collapse v-model="formVisible" class="gl-mt-3">
<gl-form-group
id="name-integration"
:label="$options.i18n.integrationFormSteps.step2"
label-for="name-integration"
>
<gl-form-input
id="name-integration"
v-model="form.name"
type="text"
:placeholder="s__('AlertSettings|Enter integration name')"
/>
</gl-form-group>
<div class="gl-display-flex gl-justify-content-end">
<gl-button type="reset" class="gl-mr-3 js-no-auto-disable">{{ __('Cancel') }}</gl-button>
<gl-button
type="submit"
category="secondary"
variant="success"
class="gl-mr-1 js-no-auto-disable"
>{{ __('Save and test payload') }}</gl-button
>
<gl-button type="submit" variant="success" class="js-no-auto-disable">{{
s__('AlertSettings|Save integration')
}}</gl-button>
</div>
</gl-collapse>
</gl-form>
</template>
<script>
import { s__ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import IntegrationsList from './alerts_integrations_list.vue';
import SettingsFormOld from './alerts_settings_form_old.vue';
import SettingsFormNew from './alerts_settings_form_new.vue';
export default {
components: {
IntegrationsList,
SettingsFormOld,
SettingsFormNew,
},
mixins: [glFeatureFlagsMixin()],
inject: {
generic: {
default: {},
},
prometheus: {
default: {},
},
},
computed: {
integrations() {
return [
{
name: s__('AlertSettings|HTTP endpoint'),
type: s__('AlertsIntegrations|HTTP endpoint'),
activated: this.generic.activated,
},
{
name: s__('AlertSettings|External Prometheus'),
type: s__('AlertsIntegrations|Prometheus'),
activated: this.prometheus.activated,
},
];
},
},
};
</script>
<template>
<div>
<integrations-list :integrations="integrations" />
<settings-form-new v-if="glFeatures.httpIntegrationsList" />
<settings-form-old v-else />
</div>
</template>
......@@ -17,11 +17,10 @@ export const i18n = {
changesSaved: s__('AlertSettings|Your integration was successfully updated.'),
prometheusInfo: s__('AlertSettings|Add URL and auth key to your Prometheus config file'),
integrationsInfo: s__(
'AlertSettings|Learn more about our improvements for %{linkStart}integrations%{linkEnd}',
'AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}',
),
resetKey: s__('AlertSettings|Reset key'),
copyToClipboard: s__('AlertSettings|Copy'),
integrationsLabel: s__('AlertSettings|Add new integrations'),
apiBaseUrlLabel: s__('AlertSettings|API URL'),
authKeyLabel: s__('AlertSettings|Authorization key'),
urlLabel: s__('AlertSettings|Webhook URL'),
......@@ -40,7 +39,8 @@ export const i18n = {
integration: s__('AlertSettings|Integration'),
};
export const serviceOptions = [
export const integrationTypes = [
{ value: '', text: s__('AlertSettings|Select integration type') },
{ value: 'generic', text: s__('AlertSettings|HTTP Endpoint') },
{ value: 'prometheus', text: s__('AlertSettings|External Prometheus') },
{ value: 'opsgenie', text: s__('AlertSettings|Opsgenie') },
......@@ -56,9 +56,9 @@ export const sectionHash = 'js-alert-management-settings';
/* eslint-disable @gitlab/require-i18n-strings */
/**
* Tracks snowplow event when user views alerts intergration list
* Tracks snowplow event when user views alerts integration list
*/
export const trackAlertIntergrationsViewsOptions = {
category: 'Alert Intergrations',
export const trackAlertIntegrationsViewsOptions = {
category: 'Alert Integrations',
action: 'view_alert_integrations_list',
};
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import AlertSettingsForm from './components/alerts_settings_form.vue';
import AlertSettingsWrapper from './components/alerts_settings_wrapper.vue';
export default el => {
if (!el) {
......@@ -26,16 +26,11 @@ export default el => {
opsgenieMvcTargetUrl,
} = el.dataset;
const genericActivated = parseBoolean(activatedStr);
const prometheusIsActivated = parseBoolean(prometheusActivated);
const opsgenieMvcActivated = parseBoolean(opsgenieMvcEnabled);
const opsgenieMvcIsAvailable = parseBoolean(opsgenieMvcAvailable);
return new Vue({
el,
provide: {
prometheus: {
activated: prometheusIsActivated,
activated: parseBoolean(prometheusActivated),
prometheusUrl,
authorizationKey: prometheusAuthorizationKey,
prometheusFormPath,
......@@ -45,23 +40,23 @@ export default el => {
generic: {
alertsSetupUrl,
alertsUsageUrl,
activated: genericActivated,
activated: parseBoolean(activatedStr),
formPath,
authorizationKey,
url,
},
opsgenie: {
formPath: opsgenieMvcFormPath,
activated: opsgenieMvcActivated,
activated: parseBoolean(opsgenieMvcEnabled),
opsgenieMvcTargetUrl,
opsgenieMvcIsAvailable,
opsgenieMvcIsAvailable: parseBoolean(opsgenieMvcAvailable),
},
},
components: {
AlertSettingsForm,
AlertSettingsWrapper,
},
render(createElement) {
return createElement('alert-settings-form');
return createElement('alert-settings-wrapper');
},
});
};
<script>
/* eslint-disable vue/no-v-html */
import { GlLoadingIcon, GlBadge } from '@gitlab/ui';
import { GlLoadingIcon, GlBadge, GlTooltipDirective } from '@gitlab/ui';
import { visitUrl } from '../../lib/utils/url_utility';
import tooltip from '../../vue_shared/directives/tooltip';
import identicon from '../../vue_shared/components/identicon.vue';
import eventHub from '../event_hub';
import { VISIBILITY_TYPE_ICON, GROUP_VISIBILITY_TYPE } from '../constants';
......@@ -17,7 +16,7 @@ import { showLearnGitLabGroupItemPopover } from '~/onboarding_issues';
export default {
directives: {
tooltip,
GlTooltip: GlTooltipDirective,
},
components: {
GlBadge,
......@@ -127,11 +126,10 @@ export default {
<div class="group-text flex-grow-1 flex-shrink-1">
<div class="d-flex align-items-center flex-wrap title namespace-title gl-mr-3">
<a
v-tooltip
v-gl-tooltip.bottom
:href="group.relativePath"
:title="group.fullName"
class="no-expand gl-mt-3 gl-mr-3 gl-text-gray-900!"
data-placement="bottom"
>{{
// ending bracket must be by closing tag to prevent
// link hover text-decoration from over-extending
......
......@@ -6,6 +6,10 @@ module Projects
before_action :authorize_admin_operations!
before_action :authorize_read_prometheus_alerts!, only: [:reset_alerting_token]
before_action do
push_frontend_feature_flag(:http_integrations_list, @project)
end
respond_to :json, only: [:reset_alerting_token, :reset_pagerduty_token]
helper_method :error_tracking_setting
......
# frozen_string_literal: true
module Resolvers
module AlertManagement
class IntegrationsResolver < BaseResolver
alias_method :project, :synchronized_object
def resolve(**args)
return [] unless Feature.enabled?(:multiple_http_integrations, project)
http_integrations + prometheus_integrations
end
private
def prometheus_integrations
return [] unless Ability.allowed?(current_user, :admin_project, project)
Array(project.prometheus_service)
end
def http_integrations
return [] unless Ability.allowed?(current_user, :admin_operations, project)
::AlertManagement::HttpIntegrationsFinder.new(project, {}).execute
end
end
end
end
# frozen_string_literal: true
module Types
module AlertManagement
class HttpIntegrationType < BaseObject
graphql_name 'AlertManagementHttpIntegration'
description 'An endpoint and credentials used to accept alerts for a project'
implements(Types::AlertManagement::IntegrationType)
authorize :admin_operations
def type
:http
end
def api_url
nil
end
end
end
end
# frozen_string_literal: true
module Types
module AlertManagement
module IntegrationType
include Types::BaseInterface
graphql_name 'AlertManagementIntegration'
field :id,
GraphQL::ID_TYPE,
null: false,
description: 'ID of the integration'
field :type,
AlertManagement::IntegrationTypeEnum,
null: false,
description: 'Type of integration'
field :name,
GraphQL::STRING_TYPE,
null: true,
description: 'Name of the integration'
field :active,
GraphQL::BOOLEAN_TYPE,
null: true,
description: 'Whether the endpoint is currently accepting alerts'
field :token,
GraphQL::STRING_TYPE,
null: true,
description: 'Token used to authenticate alert notification requests'
field :url,
GraphQL::STRING_TYPE,
null: true,
description: 'Endpoint which accepts alert notifications'
field :api_url,
GraphQL::STRING_TYPE,
null: true,
description: 'URL at which Prometheus metrics can be queried to populate the metrics dashboard'
definition_methods do
def resolve_type(object, context)
if object.is_a?(::PrometheusService)
Types::AlertManagement::PrometheusIntegrationType
else
Types::AlertManagement::HttpIntegrationType
end
end
end
orphan_types Types::AlertManagement::PrometheusIntegrationType,
Types::AlertManagement::HttpIntegrationType
end
end
end
# frozen_string_literal: true
module Types
module AlertManagement
class IntegrationTypeEnum < BaseEnum
graphql_name 'AlertManagementIntegrationType'
description 'Values of types of integrations'
value 'PROMETHEUS', 'Prometheus integration', value: :prometheus
value 'HTTP', 'Integration with any monitoring tool', value: :http
end
end
end
# frozen_string_literal: true
module Types
module AlertManagement
class PrometheusIntegrationType < BaseObject
include ::Gitlab::Routing
graphql_name 'AlertManagementPrometheusIntegration'
description 'An endpoint and credentials used to accept Prometheus alerts for a project'
implements(Types::AlertManagement::IntegrationType)
authorize :admin_project
alias_method :prometheus_service, :object
def name
prometheus_service.title
end
def type
:prometheus
end
def token
prometheus_service.project&.alerting_setting&.token
end
def url
prometheus_service.project && notify_project_prometheus_alerts_url(prometheus_service.project, format: :json)
end
def active
prometheus_service.manual_configuration?
end
end
end
end
......@@ -267,6 +267,12 @@ module Types
description: 'Counts of alerts by status for the project',
resolver: Resolvers::AlertManagement::AlertStatusCountsResolver
field :alert_management_integrations,
Types::AlertManagement::IntegrationType.connection_type,
null: true,
description: 'Integrations which can receive alerts for the project',
resolver: Resolvers::AlertManagement::IntegrationsResolver
field :releases,
Types::ReleaseType.connection_type,
null: true,
......
......@@ -2,6 +2,7 @@
module AlertManagement
class HttpIntegration < ApplicationRecord
include ::Gitlab::Routing
LEGACY_IDENTIFIER = 'legacy'
DEFAULT_NAME_SLUG = 'http-endpoint'
......@@ -31,9 +32,9 @@ module AlertManagement
scope :ordered_by_id, -> { order(:id) }
def url
return ::Gitlab::Routing.url_helpers.project_alerts_notify_url(project, format: :json) if legacy?
return project_alerts_notify_url(project, format: :json) if legacy?
::Gitlab::Routing.url_helpers.project_alert_http_integration_url(project, name_slug, endpoint_identifier, format: :json)
project_alert_http_integration_url(project, name_slug, endpoint_identifier, format: :json)
end
private
......
# frozen_string_literal: true
module AlertManagement
class HttpIntegrationPolicy < ::BasePolicy
delegate { @subject.project }
end
end
# frozen_string_literal: true
class PrometheusServicePolicy < ::BasePolicy
delegate { @subject.project }
end
---
title: Disallow some project routes in robots.txt
merge_request: 46218
author:
type: changed
---
name: security_on_demand_scans_feature_flag
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32994
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/237796
group: group::dynamic analysis
name: http_integrations_list
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45993
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/255502
type: development
default_enabled: true
group: group::health
default_enabled: false
......@@ -590,6 +590,173 @@ type AlertManagementAlertStatusCountsType {
triggered: Int
}
"""
An endpoint and credentials used to accept alerts for a project
"""
type AlertManagementHttpIntegration implements AlertManagementIntegration {
"""
Whether the endpoint is currently accepting alerts
"""
active: Boolean
"""
URL at which Prometheus metrics can be queried to populate the metrics dashboard
"""
apiUrl: String
"""
ID of the integration
"""
id: ID!
"""
Name of the integration
"""
name: String
"""
Token used to authenticate alert notification requests
"""
token: String
"""
Type of integration
"""
type: AlertManagementIntegrationType!
"""
Endpoint which accepts alert notifications
"""
url: String
}
interface AlertManagementIntegration {
"""
Whether the endpoint is currently accepting alerts
"""
active: Boolean
"""
URL at which Prometheus metrics can be queried to populate the metrics dashboard
"""
apiUrl: String
"""
ID of the integration
"""
id: ID!
"""
Name of the integration
"""
name: String
"""
Token used to authenticate alert notification requests
"""
token: String
"""
Type of integration
"""
type: AlertManagementIntegrationType!
"""
Endpoint which accepts alert notifications
"""
url: String
}
"""
The connection type for AlertManagementIntegration.
"""
type AlertManagementIntegrationConnection {
"""
A list of edges.
"""
edges: [AlertManagementIntegrationEdge]
"""
A list of nodes.
"""
nodes: [AlertManagementIntegration]
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type AlertManagementIntegrationEdge {
"""
A cursor for use in pagination.
"""
cursor: String!
"""
The item at the end of the edge.
"""
node: AlertManagementIntegration
}
"""
Values of types of integrations
"""
enum AlertManagementIntegrationType {
"""
Integration with any monitoring tool
"""
HTTP
"""
Prometheus integration
"""
PROMETHEUS
}
"""
An endpoint and credentials used to accept Prometheus alerts for a project
"""
type AlertManagementPrometheusIntegration implements AlertManagementIntegration {
"""
Whether the endpoint is currently accepting alerts
"""
active: Boolean
"""
URL at which Prometheus metrics can be queried to populate the metrics dashboard
"""
apiUrl: String
"""
ID of the integration
"""
id: ID!
"""
Name of the integration
"""
name: String
"""
Token used to authenticate alert notification requests
"""
token: String
"""
Type of integration
"""
type: AlertManagementIntegrationType!
"""
Endpoint which accepts alert notifications
"""
url: String
}
"""
Alert severity values
"""
......@@ -13747,6 +13914,31 @@ type Project {
statuses: [AlertManagementStatus!]
): AlertManagementAlertConnection
"""
Integrations which can receive alerts for the project
"""
alertManagementIntegrations(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
): AlertManagementIntegrationConnection
"""
If `only_allow_merge_if_pipeline_succeeds` is true, indicates if merge
requests of the project can also be merged with skipped jobs
......
......@@ -106,6 +106,34 @@ Represents total number of alerts for the represented categories.
| `resolved` | Int | Number of alerts with status RESOLVED for the project |
| `triggered` | Int | Number of alerts with status TRIGGERED for the project |
### AlertManagementHttpIntegration
An endpoint and credentials used to accept alerts for a project.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `active` | Boolean | Whether the endpoint is currently accepting alerts |
| `apiUrl` | String | URL at which Prometheus metrics can be queried to populate the metrics dashboard |
| `id` | ID! | ID of the integration |
| `name` | String | Name of the integration |
| `token` | String | Token used to authenticate alert notification requests |
| `type` | AlertManagementIntegrationType! | Type of integration |
| `url` | String | Endpoint which accepts alert notifications |
### AlertManagementPrometheusIntegration
An endpoint and credentials used to accept Prometheus alerts for a project.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `active` | Boolean | Whether the endpoint is currently accepting alerts |
| `apiUrl` | String | URL at which Prometheus metrics can be queried to populate the metrics dashboard |
| `id` | ID! | ID of the integration |
| `name` | String | Name of the integration |
| `token` | String | Token used to authenticate alert notification requests |
| `type` | AlertManagementIntegrationType! | Type of integration |
| `url` | String | Endpoint which accepts alert notifications |
### AlertSetAssigneesPayload
Autogenerated return type of AlertSetAssignees.
......@@ -3241,6 +3269,15 @@ Values for sorting alerts.
| `updated_asc` **{warning-solid}** | **Deprecated:** Use UPDATED_ASC. Deprecated in 13.5 |
| `updated_desc` **{warning-solid}** | **Deprecated:** Use UPDATED_DESC. Deprecated in 13.5 |
### AlertManagementIntegrationType
Values of types of integrations.
| Value | Description |
| ----- | ----------- |
| `HTTP` | Integration with any monitoring tool |
| `PROMETHEUS` | Prometheus integration |
### AlertManagementSeverity
Alert severity values.
......
......@@ -714,10 +714,6 @@ To delete a scanner profile:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218465) in GitLab 13.2.
> - [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/218465) in GitLab 13.3.
> - It's deployed behind a feature flag, enabled by default.
> - It's enabled on GitLab.com.
> - It's able to be enabled or disabled per-project.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-on-demand-scans).
An on-demand DAST scan runs outside the DevOps life cycle. Changes in your repository don't trigger
the scan. You must start it manually.
......@@ -748,35 +744,6 @@ To run an on-demand DAST scan, you need:
The on-demand DAST scan runs and the project's dashboard shows the results.
### Enable or disable On-demand Scans
The On-demand DAST Scans feature is enabled by default. You can disable on-demand scans
instance-wide, or disable it for specific projects if you prefer.
To run on-demand DAST scans, an administrator must enable the
`security_on_demand_scans_feature_flag` feature flag.
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
can disable or enable the feature flags.
To disable On-demand DAST Scans:
```ruby
# Instance-wide
Feature.disable(:security_on_demand_scans_feature_flag)
# or by project
Feature.disable(:security_on_demand_scans_feature_flag, Project.find(<project id>))
```
To enable On-demand DAST Scans:
```ruby
# Instance-wide
Feature.enable(:security_on_demand_scans_feature_flag)
# or by project
Feature.enable(:security_on_demand_scans_feature_flag, Project.find(<project ID>))
```
## Reports
The DAST tool outputs a report file in JSON format by default. However, this tool can also generate reports in
......
......@@ -17,8 +17,6 @@ module EE
null: true,
description: 'The DAST scanner profiles associated with the project',
resolve: -> (project, _args, _ctx) do
return DastScannerProfile.none unless ::Feature.enabled?(:security_on_demand_scans_feature_flag, project, default_enabled: true)
DastScannerProfilesFinder.new(project_ids: [project.id]).execute
end
......@@ -153,6 +151,7 @@ module EE
results = ::Ci::DailyBuildGroupReportResult
.by_projects(project_ids)
.with_coverage
.with_default_branch
.latest
.summaries_per_project
......
......@@ -101,7 +101,6 @@ module EE
with_scope :subject
condition(:on_demand_scans_enabled) do
::Feature.enabled?(:security_on_demand_scans_feature_flag, project, default_enabled: true) &&
@subject.feature_available?(:security_on_demand_scans)
end
......
......@@ -135,14 +135,6 @@ RSpec.describe Mutations::DastOnDemandScans::Create do
end
end
context 'when on demand scan feature is not enabled' do
it 'raises an exception' do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when on demand scan licensed feature is not available' do
it 'raises an exception' do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -84,16 +84,6 @@ RSpec.describe Mutations::DastScannerProfiles::Create do
end
end
context 'when security_on_demand_scans_feature_flag is disabled' do
before do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
end
it 'raises an exception' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when on demand scan licensed feature is not available' do
it 'raises an exception' do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -54,14 +54,6 @@ RSpec.describe Mutations::DastScannerProfiles::Delete do
end
end
context 'when security_on_demand_scans_feature_flag is disabled' do
it 'raises an exception' do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when on demand scan licensed feature is not available' do
it 'raises an exception' do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -109,14 +109,6 @@ RSpec.describe Mutations::DastScannerProfiles::Update do
end
end
context 'when on demand scan feature is not enabled' do
it 'raises an exception' do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when on demand scan licensed feature is not available' do
it 'raises an exception' do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -90,14 +90,6 @@ RSpec.describe Mutations::DastSiteProfiles::Create do
end
end
context 'when on demand scan feature is not enabled' do
it 'raises an exception' do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when on demand scan licensed feature is not available' do
it 'raises an exception' do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -97,14 +97,6 @@ RSpec.describe Mutations::DastSiteProfiles::Delete do
end
end
context 'when on demand scan feature is not enabled' do
it 'raises an exception' do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when on demand scan licensed feature is not available' do
it 'raises an exception' do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -97,14 +97,6 @@ RSpec.describe Mutations::DastSiteProfiles::Update do
end
end
context 'when on demand scan feature is not enabled' do
it 'raises an exception' do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when on demand scan licensed feature is not available' do
it 'raises an exception' do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -87,14 +87,6 @@ RSpec.describe Mutations::DastSiteTokens::Create do
end
end
context 'when on demand scan feature is not enabled' do
it 'raises an exception' do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when on demand scan site validations feature is not enabled' do
it 'raises an exception' do
stub_feature_flags(security_on_demand_scans_site_validation: false)
......
......@@ -71,14 +71,6 @@ RSpec.describe Mutations::DastSiteValidations::Create do
expect(subject[:status]).to eq(dast_site_validation.state)
end
context 'when on demand scan feature is not enabled' do
it 'raises an exception' do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when on demand scan site validations feature is not enabled' do
it 'raises an exception' do
stub_feature_flags(security_on_demand_scans_site_validation: false)
......
......@@ -34,10 +34,6 @@ RSpec.describe Mutations::Pipelines::RunDastScan do
end
context 'when on demand scan feature is enabled' do
before do
stub_feature_flags(security_on_demand_scans_feature_flag: true)
end
context 'when the project does not exist' do
let(:project_path) { SecureRandom.hex }
......
......@@ -33,14 +33,6 @@ RSpec.describe DastSiteProfilePolicy do
it { is_expected.to be_allowed(:create_on_demand_dast_scan) }
context 'when on demand scan feature flag is disabled' do
before do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
end
it { is_expected.to be_disallowed(:create_on_demand_dast_scan) }
end
context 'when on demand scan licensed feature is not available' do
before do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -33,14 +33,6 @@ RSpec.describe DastSiteValidationPolicy do
it { is_expected.to be_allowed(:create_on_demand_dast_scan) }
context 'when on demand scan feature flag is disabled' do
before do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
end
it { is_expected.to be_disallowed(:create_on_demand_dast_scan) }
end
context 'when on demand scan licensed feature is not available' do
before do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -37,10 +37,6 @@ RSpec.describe 'Running a DAST Scan' do
end
context 'when on demand scan feature is enabled' do
before do
stub_feature_flags(security_on_demand_scans_feature_flag: true)
end
context 'when the user does not have permission to run a dast scan' do
it_behaves_like 'a mutation that returns top-level errors',
errors: ['The resource that you are attempting to access does not ' \
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'getting Alert Management Integrations' do
include ::Gitlab::Routing.url_helpers
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
let_it_be(:current_user) { create(:user) }
let_it_be(:prometheus_service) { create(:prometheus_service, project: project) }
let_it_be(:project_alerting_setting) { create(:project_alerting_setting, project: project) }
let_it_be(:active_http_integration) { create(:alert_management_http_integration, project: project) }
let_it_be(:inactive_http_integration) { create(:alert_management_http_integration, :inactive, project: project) }
let_it_be(:other_project_http_integration) { create(:alert_management_http_integration) }
let(:fields) do
<<~QUERY
nodes {
#{all_graphql_fields_for('AlertManagementIntegration')}
}
QUERY
end
let(:query) do
graphql_query_for(
'project',
{ 'fullPath' => project.full_path },
query_graphql_field('alertManagementIntegrations', {}, fields)
)
end
before do
stub_licensed_features(multiple_alert_http_integrations: true)
end
context 'with integrations' do
let(:integrations) { graphql_data.dig('project', 'alertManagementIntegrations', 'nodes') }
context 'without project permissions' do
let(:user) { create(:user) }
before do
post_graphql(query, current_user: current_user)
end
it_behaves_like 'a working graphql query'
specify { expect(integrations).to be_nil }
end
context 'with project permissions' do
before do
project.add_maintainer(current_user)
post_graphql(query, current_user: current_user)
end
it_behaves_like 'a working graphql query'
specify { expect(integrations.size).to eq(3) }
it 'returns the correct properties of the integrations' do
expect(integrations).to include(
{
'id' => GitlabSchema.id_from_object(active_http_integration).to_s,
'type' => 'HTTP',
'name' => active_http_integration.name,
'active' => active_http_integration.active,
'token' => active_http_integration.token,
'url' => active_http_integration.url,
'apiUrl' => nil
},
{
'id' => GitlabSchema.id_from_object(inactive_http_integration).to_s,
'type' => 'HTTP',
'name' => inactive_http_integration.name,
'active' => inactive_http_integration.active,
'token' => inactive_http_integration.token,
'url' => inactive_http_integration.url,
'apiUrl' => nil
},
{
'id' => GitlabSchema.id_from_object(prometheus_service).to_s,
'type' => 'PROMETHEUS',
'name' => 'Prometheus',
'active' => prometheus_service.manual_configuration?,
'token' => project_alerting_setting.token,
'url' => "http://localhost/#{project.full_path}/prometheus/alerts/notify.json",
'apiUrl' => prometheus_service.api_url
}
)
end
end
end
end
......@@ -37,14 +37,26 @@ RSpec.describe 'Getting code coverage summary in a project' do
end
context 'when project has coverage' do
let!(:daily_build_group_report_result) { create(:ci_daily_build_group_report_result, project: project) }
context 'for the default branch' do
let!(:daily_build_group_report_result) { create(:ci_daily_build_group_report_result, project: project) }
it 'contains code coverage summary data', :aggregates_failures do
post_graphql(query, current_user: current_user)
it 'contains code coverage summary data', :aggregates_failures do
post_graphql(query, current_user: current_user)
expect(code_coverage_summary_graphql_data.dig('averageCoverage')).to eq(77.0)
expect(code_coverage_summary_graphql_data.dig('coverageCount')).to eq(1)
expect(code_coverage_summary_graphql_data.dig('lastUpdatedAt')).to eq(daily_build_group_report_result.date.to_s)
end
end
context 'not for the default branch' do
let!(:daily_build_group_report_result) { create(:ci_daily_build_group_report_result, :on_feature_branch, project: project) }
it 'returns nil' do
post_graphql(query, current_user: current_user)
expect(code_coverage_summary_graphql_data.dig('averageCoverage')).to eq(77.0)
expect(code_coverage_summary_graphql_data.dig('coverageCount')).to eq(1)
expect(code_coverage_summary_graphql_data.dig('lastUpdatedAt')).to eq(daily_build_group_report_result.date.to_s)
expect(code_coverage_summary_graphql_data).to be_nil
end
end
end
......
......@@ -93,17 +93,5 @@ RSpec.describe 'Query.project(fullPath).dastScannerProfiles' do
it { is_expected.to eq(dast_scanner_profile.to_global_id.to_s) }
end
context 'when on demand scan feature flag is disabled' do
before do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
end
describe 'dast scanner profiles' do
subject { response_data.dig('project', 'dastScannerProfiles', 'nodes') }
it { is_expected.to be_empty }
end
end
end
end
......@@ -83,14 +83,6 @@ RSpec.describe 'Query.project(fullPath).dastSiteProfile' do
end
end
context 'when on demand scan feature flag is disabled' do
it 'returns a null dast_site_profile' do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
expect(dast_site_profile_response).to be_nil
end
end
context 'when on demand scan licensed feature is not available' do
it 'returns a null dast_site_profile' do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -106,14 +106,6 @@ RSpec.describe 'Query.project(fullPath).dastSiteProfiles' do
end
end
context 'when on demand scan feature flag is disabled' do
it 'returns an empty edges array' do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
expect(dast_site_profiles_response['nodes']).to be_empty
end
end
context 'when on demand scan licensed feature is not available' do
it 'returns an empty edges array' do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -9,7 +9,6 @@ RSpec.describe Projects::OnDemandScansController, type: :request do
describe 'GET #index' do
context 'feature available' do
before do
stub_feature_flags(security_on_demand_scans_feature_flag: true)
stub_licensed_features(security_on_demand_scans: true)
end
......@@ -49,16 +48,7 @@ RSpec.describe Projects::OnDemandScansController, type: :request do
login_as(user)
end
it "sees a 404 error if the feature flag is disabled" do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
stub_licensed_features(security_on_demand_scans: true)
get project_on_demand_scans_path(project)
expect(response).to have_gitlab_http_status(:not_found)
end
it "sees a 404 error if the license doesn't support the feature" do
stub_feature_flags(security_on_demand_scans_feature_flag: true)
stub_licensed_features(security_on_demand_scans: false)
get project_on_demand_scans_path(project)
......
......@@ -9,7 +9,6 @@ RSpec.describe Projects::Security::DastProfilesController, type: :request do
describe 'GET #index' do
context 'feature available' do
before do
stub_feature_flags(security_on_demand_scans_feature_flag: true)
stub_licensed_features(security_on_demand_scans: true)
end
......@@ -49,19 +48,8 @@ RSpec.describe Projects::Security::DastProfilesController, type: :request do
login_as(user)
end
context 'feature flag is disabled' do
it 'sees a 404 error' do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
stub_licensed_features(security_on_demand_scans: true)
get project_security_configuration_dast_profiles_path(project)
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'license doesnt\'t support the feature' do
it 'sees a 404 error' do
stub_feature_flags(security_on_demand_scans_feature_flag: true)
stub_licensed_features(security_on_demand_scans: false)
get project_security_configuration_dast_profiles_path(project)
......
......@@ -9,7 +9,6 @@ RSpec.describe Projects::Security::DastScannerProfilesController, type: :request
shared_context 'on-demand scans feature available' do
before do
stub_feature_flags(security_on_demand_scans_feature_flag: true)
stub_licensed_features(security_on_demand_scans: true)
end
end
......@@ -54,23 +53,13 @@ RSpec.describe Projects::Security::DastScannerProfilesController, type: :request
end
context 'feature not available' do
using RSpec::Parameterized::TableSyntax
include_context 'user authorized'
where(:feature_flag_enabled, :license_support) do
false | true
true | false
end
it 'sees a 404 error' do
stub_licensed_features(security_on_demand_scans: false)
get path
with_them do
it 'sees a 404 error' do
stub_feature_flags(security_on_demand_scans_feature_flag: feature_flag_enabled)
stub_licensed_features(security_on_demand_scans: license_support)
get path
expect(response).to have_gitlab_http_status(:not_found)
end
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
......
......@@ -8,7 +8,6 @@ RSpec.describe Projects::Security::DastSiteProfilesController, type: :request do
let(:dast_site_profile) { create(:dast_site_profile, project: project) }
def with_feature_available
stub_feature_flags(security_on_demand_scans_feature_flag: true)
stub_licensed_features(security_on_demand_scans: true)
end
......@@ -55,19 +54,8 @@ RSpec.describe Projects::Security::DastSiteProfilesController, type: :request do
with_user_authorized
end
context 'feature flag is disabled' do
it 'sees a 404 error' do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
stub_licensed_features(security_on_demand_scans: true)
get path
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'license doesnt\'t support the feature' do
it 'sees a 404 error' do
stub_feature_flags(security_on_demand_scans_feature_flag: true)
stub_licensed_features(security_on_demand_scans: false)
get path
......
......@@ -171,20 +171,6 @@ RSpec.describe Ci::RunDastScanService do
end
end
context 'when on demand scan feature is disabled' do
before do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
end
it 'returns an error status' do
expect(status).to eq(:error)
end
it 'populates message' do
expect(message).to eq('Insufficient permissions')
end
end
context 'when on demand scan licensed feature is not available' do
before do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -106,20 +106,6 @@ RSpec.describe DastScannerProfiles::CreateService do
end
end
context 'when on demand scan feature is disabled' do
before do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
end
it 'returns an error status' do
expect(status).to eq(:error)
end
it 'populates message' do
expect(message).to eq('Insufficient permissions')
end
end
context 'when on demand scan licensed feature is not available' do
before do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -64,20 +64,6 @@ RSpec.describe DastScannerProfiles::DestroyService do
end
end
context 'when on demand scan feature is disabled' do
before do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
end
it 'returns an error status' do
expect(status).to eq(:error)
end
it 'populates message' do
expect(message).to eq('You are not authorized to update this scanner profile')
end
end
context 'when on demand scan licensed feature is not available' do
before do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -151,20 +151,6 @@ RSpec.describe DastScannerProfiles::UpdateService do
end
end
context 'when on demand scan feature is disabled' do
before do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
end
it 'returns an error status' do
expect(status).to eq(:error)
end
it 'populates message' do
expect(message).to eq('You are not authorized to update this scanner profile')
end
end
context 'when on demand scan licensed feature is not available' do
before do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -77,20 +77,6 @@ RSpec.describe DastSiteProfiles::CreateService do
end
end
context 'when on demand scan feature is disabled' do
before do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
end
it 'returns an error status' do
expect(status).to eq(:error)
end
it 'populates message' do
expect(message).to eq('Insufficient permissions')
end
end
context 'when on demand scan licensed feature is not available' do
before do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -86,20 +86,6 @@ RSpec.describe DastSiteProfiles::UpdateService do
end
end
context 'when on demand scan feature is disabled' do
before do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
end
it 'returns an error status' do
expect(status).to eq(:error)
end
it 'populates message' do
expect(message).to eq('Insufficient permissions')
end
end
context 'when on demand scan licensed feature is not available' do
before do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -59,16 +59,6 @@ RSpec.describe DastSites::FindOrCreateService do
end
end
context 'when on demand scan feature is disabled' do
it 'raises an exception' do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
expect { subject }.to raise_error(DastSites::FindOrCreateService::PermissionsError) do |err|
expect(err.message).to include('Insufficient permissions')
end
end
end
context 'when on demand scan licensed feature is not available' do
it 'raises an exception' do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -27,14 +27,6 @@ RSpec.shared_examples 'an on-demand scan mutation when user can run an on-demand
expect(mutation_response["errors"]).to be_empty
end
context 'when on demand scan feature is disabled' do
before do
stub_feature_flags(security_on_demand_scans_feature_flag: false)
end
it_behaves_like 'a mutation that returns a top-level access error'
end
context 'when on demand scan licensed feature is not available' do
before do
stub_licensed_features(security_on_demand_scans: false)
......
......@@ -2486,6 +2486,12 @@ msgstr ""
msgid "AlertService|You must provide this URL and authorization key to authorize an external service to send alerts to GitLab. You can provide this URL and key to multiple services. After configuring an external service, alerts from your service will display on the GitLab %{linkStart}Alerts%{linkEnd} page."
msgstr ""
msgid "AlertSettings|1. Select integration type"
msgstr ""
msgid "AlertSettings|2. Name integration"
msgstr ""
msgid "AlertSettings|API URL"
msgstr ""
......@@ -2510,6 +2516,9 @@ msgstr ""
msgid "AlertSettings|Copy"
msgstr ""
msgid "AlertSettings|Enter integration name"
msgstr ""
msgid "AlertSettings|Enter test alert JSON...."
msgstr ""
......@@ -2525,7 +2534,10 @@ msgstr ""
msgid "AlertSettings|Integration"
msgstr ""
msgid "AlertSettings|Learn more about our improvements for %{linkStart}integrations%{linkEnd}"
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
msgid "AlertSettings|Learn more about our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
msgid "AlertSettings|Opsgenie"
......@@ -2540,6 +2552,12 @@ msgstr ""
msgid "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint."
msgstr ""
msgid "AlertSettings|Save integration"
msgstr ""
msgid "AlertSettings|Select integration type"
msgstr ""
msgid "AlertSettings|Test alert payload"
msgstr ""
......@@ -23040,6 +23058,9 @@ msgstr ""
msgid "Save Push Rules"
msgstr ""
msgid "Save and test payload"
msgstr ""
msgid "Save anyway"
msgstr ""
......
......@@ -67,3 +67,16 @@ Disallow: /*/protected_branches
Disallow: /*/uploads/
Disallow: /*/project_members
Disallow: /*/settings
Disallow: /*/-/import
Disallow: /*/-/environments
Disallow: /*/-/jobs
Disallow: /*/-/requirements_management
Disallow: /*/-/pipelines
Disallow: /*/-/pipeline_schedules
Disallow: /*/-/dependencies
Disallow: /*/-/licenses
Disallow: /*/-/metrics
Disallow: /*/-/incidents
Disallow: /*/-/value_stream_analytics
Disallow: /*/-/analytics
Disallow: /*/insights
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Alert integrations settings form', :js do
let_it_be(:project) { create(:project) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:developer) { create(:user) }
before_all do
project.add_maintainer(maintainer)
project.add_developer(developer)
end
before do
sign_in(maintainer)
end
describe 'when viewing alert integrations as a maintainer' do
context 'with feature flag enabled' do
before do
visit project_settings_operations_path(project, anchor: 'js-alert-management-settings')
wait_for_requests
end
it 'shows the alerts setting form title' do
page.within('#js-alert-management-settings') do
expect(find('h3')).to have_content('Alerts')
end
end
it 'shows the new alerts setting form' do
expect(page).to have_content('1. Select integration type')
end
end
context 'with feature flag disabled' do
before do
stub_feature_flags(http_integrations_list: false)
visit project_settings_operations_path(project, anchor: 'js-alert-management-settings')
wait_for_requests
end
it 'shows the old alerts setting form' do
expect(page).to have_content('Webhook URL')
end
end
end
describe 'when viewing alert integrations as a developer' do
before do
sign_in(developer)
visit project_settings_operations_path(project, anchor: 'js-alert-management-settings')
wait_for_requests
end
it 'shows the old alerts setting form' do
expect(page).not_to have_selector('.incident-management-list')
expect(page).not_to have_selector('#js-alert-management-settings')
end
end
end
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AlertsSettingsForm with default values renders the initial template 1`] = `
"<div>
<integrations-list-stub integrations=\\"[object Object],[object Object]\\"></integrations-list-stub>
<gl-form-stub>
<h5 class=\\"gl-font-lg gl-my-5\\">Add new integrations</h5>
<!---->
<div data-testid=\\"alert-settings-description\\">
<p>
<gl-sprintf-stub message=\\"You must provide this URL and authorization key to authorize an external service to send alerts to GitLab. You can provide this URL and key to multiple services. After configuring an external service, alerts from your service will display on the GitLab %{linkStart}Alerts%{linkEnd} page.\\"></gl-sprintf-stub>
</p>
<p>
<gl-sprintf-stub message=\\"Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint.\\"></gl-sprintf-stub>
</p>
</div>
<gl-form-group-stub label-for=\\"integration-type\\" label=\\"Integration\\">
<gl-form-select-stub id=\\"integration-type\\" options=\\"[object Object],[object Object],[object Object]\\" data-testid=\\"alert-settings-select\\" value=\\"generic\\"></gl-form-select-stub> <span class=\\"gl-text-gray-500\\"><gl-sprintf-stub message=\\"Learn more about our improvements for %{linkStart}integrations%{linkEnd}\\"></gl-sprintf-stub></span>
</gl-form-group-stub>
<gl-form-group-stub label=\\"Active\\" label-for=\\"activated\\">
<toggle-button-stub id=\\"activated\\"></toggle-button-stub>
</gl-form-group-stub>
<!---->
<gl-form-group-stub label=\\"Webhook URL\\" label-for=\\"url\\">
<gl-form-input-group-stub value=\\"/alerts/notify.json\\" predefinedoptions=\\"[object Object]\\" id=\\"url\\" readonly=\\"\\"></gl-form-input-group-stub> <span class=\\"gl-text-gray-500\\">
</span>
</gl-form-group-stub>
<gl-form-group-stub label=\\"Authorization key\\" label-for=\\"authorization-key\\">
<gl-form-input-group-stub value=\\"abcedfg123\\" predefinedoptions=\\"[object Object]\\" id=\\"authorization-key\\" readonly=\\"\\" class=\\"gl-mb-2\\"></gl-form-input-group-stub>
<gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\" class=\\"gl-mt-3\\" role=\\"button\\" tabindex=\\"0\\">Reset key</gl-button-stub>
<gl-modal-stub modalid=\\"authKeyModal\\" titletag=\\"h4\\" modalclass=\\"\\" size=\\"md\\" title=\\"Reset key\\" ok-title=\\"Reset key\\" ok-variant=\\"danger\\">
Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.
</gl-modal-stub>
</gl-form-group-stub>
<gl-form-group-stub label=\\"Alert test payload\\" label-for=\\"alert-json\\">
<gl-form-textarea-stub noresize=\\"true\\" id=\\"alert-json\\" disabled=\\"true\\" state=\\"true\\" placeholder=\\"Enter test alert JSON....\\" rows=\\"6\\" max-rows=\\"10\\"></gl-form-textarea-stub>
</gl-form-group-stub>
<gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\">Test alert payload</gl-button-stub>
<div class=\\"footer-block row-content-block gl-display-flex gl-justify-content-space-between\\">
<gl-button-stub category=\\"primary\\" variant=\\"success\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\">
Save changes
</gl-button-stub>
<gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\">
Cancel
</gl-button-stub>
</div>
</gl-form-stub>
</div>"
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AlertsSettingsFormNew with default values renders the initial template 1`] = `
"<gl-form-stub class=\\"gl-mt-6\\">
<h5 class=\\"gl-font-lg gl-my-5\\">Add new integrations</h5>
<gl-form-group-stub id=\\"integration-type\\" label=\\"1. Select integration type\\" label-for=\\"integration-type\\">
<gl-form-select-stub id=\\"integration-type\\" options=\\"[object Object],[object Object],[object Object],[object Object]\\" value=\\"\\"></gl-form-select-stub> <span class=\\"gl-text-gray-500\\"><gl-sprintf-stub message=\\"Learn more about our upcoming %{linkStart}integrations%{linkEnd}\\"></gl-sprintf-stub></span>
</gl-form-group-stub>
<b-collapse-stub tag=\\"div\\" class=\\"gl-mt-3\\">
<gl-form-group-stub id=\\"name-integration\\" label=\\"2. Name integration\\" label-for=\\"name-integration\\">
<b-form-input-stub id=\\"name-integration\\" value=\\"\\" placeholder=\\"Enter integration name\\" debounce=\\"0\\" type=\\"text\\" class=\\"gl-form-input\\"></b-form-input-stub>
</gl-form-group-stub>
<div class=\\"gl-display-flex gl-justify-content-end\\">
<gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" type=\\"reset\\" class=\\"gl-mr-3 js-no-auto-disable\\">Cancel</gl-button-stub>
<gl-button-stub category=\\"secondary\\" variant=\\"success\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" type=\\"submit\\" class=\\"gl-mr-1 js-no-auto-disable\\">Save and test payload</gl-button-stub>
<gl-button-stub category=\\"primary\\" variant=\\"success\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" type=\\"submit\\" class=\\"js-no-auto-disable\\">Save integration</gl-button-stub>
</div>
</b-collapse-stub>
</gl-form-stub>"
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AlertsSettingsForm with default values renders the initial template 1`] = `
"<gl-form-stub>
<h5 class=\\"gl-font-lg gl-my-5\\"></h5>
<!---->
<div data-testid=\\"alert-settings-description\\">
<p>
<gl-sprintf-stub message=\\"You must provide this URL and authorization key to authorize an external service to send alerts to GitLab. You can provide this URL and key to multiple services. After configuring an external service, alerts from your service will display on the GitLab %{linkStart}Alerts%{linkEnd} page.\\"></gl-sprintf-stub>
</p>
<p>
<gl-sprintf-stub message=\\"Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint.\\"></gl-sprintf-stub>
</p>
</div>
<gl-form-group-stub label-for=\\"integration-type\\" label=\\"Integration\\">
<gl-form-select-stub id=\\"integration-type\\" options=\\"[object Object],[object Object],[object Object],[object Object]\\" data-testid=\\"alert-settings-select\\" value=\\"generic\\"></gl-form-select-stub> <span class=\\"gl-text-gray-500\\"><gl-sprintf-stub message=\\"Learn more about our our upcoming %{linkStart}integrations%{linkEnd}\\"></gl-sprintf-stub></span>
</gl-form-group-stub>
<gl-form-group-stub label=\\"Active\\" label-for=\\"activated\\">
<toggle-button-stub id=\\"activated\\"></toggle-button-stub>
</gl-form-group-stub>
<!---->
<gl-form-group-stub label=\\"Webhook URL\\" label-for=\\"url\\">
<gl-form-input-group-stub value=\\"/alerts/notify.json\\" predefinedoptions=\\"[object Object]\\" id=\\"url\\" readonly=\\"\\"></gl-form-input-group-stub> <span class=\\"gl-text-gray-500\\">
</span>
</gl-form-group-stub>
<gl-form-group-stub label=\\"Authorization key\\" label-for=\\"authorization-key\\">
<gl-form-input-group-stub value=\\"abcedfg123\\" predefinedoptions=\\"[object Object]\\" id=\\"authorization-key\\" readonly=\\"\\" class=\\"gl-mb-2\\"></gl-form-input-group-stub>
<gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\" class=\\"gl-mt-3\\" role=\\"button\\" tabindex=\\"0\\">Reset key</gl-button-stub>
<gl-modal-stub modalid=\\"authKeyModal\\" titletag=\\"h4\\" modalclass=\\"\\" size=\\"md\\" title=\\"Reset key\\" ok-title=\\"Reset key\\" ok-variant=\\"danger\\">
Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.
</gl-modal-stub>
</gl-form-group-stub>
<gl-form-group-stub label=\\"Alert test payload\\" label-for=\\"alert-json\\">
<gl-form-textarea-stub noresize=\\"true\\" id=\\"alert-json\\" disabled=\\"true\\" state=\\"true\\" placeholder=\\"Enter test alert JSON....\\" rows=\\"6\\" max-rows=\\"10\\"></gl-form-textarea-stub>
</gl-form-group-stub>
<gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\">Test alert payload</gl-button-stub>
<div class=\\"footer-block row-content-block gl-display-flex gl-justify-content-space-between\\">
<gl-button-stub category=\\"primary\\" variant=\\"success\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\">
Save changes
</gl-button-stub>
<gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\">
Cancel
</gl-button-stub>
</div>
</gl-form-stub>"
`;
......@@ -4,7 +4,7 @@ import Tracking from '~/tracking';
import AlertIntegrationsList, {
i18n,
} from '~/alerts_settings/components/alerts_integrations_list.vue';
import { trackAlertIntergrationsViewsOptions } from '~/alerts_settings/constants';
import { trackAlertIntegrationsViewsOptions } from '~/alerts_settings/constants';
const mockIntegrations = [
{
......@@ -82,7 +82,7 @@ describe('AlertIntegrationsList', () => {
});
it('should track alert list page views', () => {
const { category, action } = trackAlertIntergrationsViewsOptions;
const { category, action } = trackAlertIntegrationsViewsOptions;
expect(Tracking.event).toHaveBeenCalledWith(category, action);
});
});
......
import { shallowMount } from '@vue/test-utils';
import { GlForm, GlFormSelect, GlCollapse, GlFormInput } from '@gitlab/ui';
import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_form_new.vue';
import { defaultAlertSettingsConfig } from './util';
jest.mock('~/alerts_settings/services');
describe('AlertsSettingsFormNew', () => {
let wrapper;
const createComponent = ({ methods } = {}, data) => {
wrapper = shallowMount(AlertsSettingsForm, {
data() {
return { ...data };
},
provide: {
...defaultAlertSettingsConfig,
},
methods,
stubs: { GlCollapse, GlFormInput },
});
};
const findForm = () => wrapper.find(GlForm);
const findSelect = () => wrapper.find(GlFormSelect);
const findFormSteps = () => wrapper.find(GlCollapse);
const findFormName = () => wrapper.find(GlFormInput);
afterEach(() => {
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
});
describe('with default values', () => {
beforeEach(() => {
createComponent();
});
it('renders the initial template', () => {
expect(wrapper.html()).toMatchSnapshot();
});
it('render the initial form with only an integration type dropdown', () => {
expect(findForm().exists()).toBe(true);
expect(findSelect().exists()).toBe(true);
expect(findFormSteps().attributes('visible')).toBeUndefined();
});
it('shows the rest of the form when the dropdown is used', async () => {
findSelect().vm.$emit('change', 'prometheus');
await wrapper.vm.$nextTick();
expect(findFormName().isVisible()).toBe(true);
});
});
});
import { shallowMount } from '@vue/test-utils';
import { GlModal, GlAlert } from '@gitlab/ui';
import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_form.vue';
import IntegrationsList from '~/alerts_settings/components/alerts_integrations_list.vue';
import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_form_old.vue';
import ToggleButton from '~/vue_shared/components/toggle_button.vue';
import { i18n } from '~/alerts_settings/constants';
import service from '~/alerts_settings/services';
import { defaultAlertSettingsConfig } from './util';
jest.mock('~/alerts_settings/services');
const PROMETHEUS_URL = '/prometheus/alerts/notify.json';
const GENERIC_URL = '/alerts/notify.json';
const KEY = 'abcedfg123';
const INVALID_URL = 'http://invalid';
const ACTIVATED = false;
describe('AlertsSettingsForm', () => {
let wrapper;
......@@ -23,26 +17,7 @@ describe('AlertsSettingsForm', () => {
return { ...data };
},
provide: {
generic: {
authorizationKey: KEY,
formPath: INVALID_URL,
url: GENERIC_URL,
alertsSetupUrl: INVALID_URL,
alertsUsageUrl: INVALID_URL,
activated: ACTIVATED,
},
prometheus: {
authorizationKey: KEY,
prometheusFormPath: INVALID_URL,
prometheusUrl: PROMETHEUS_URL,
activated: ACTIVATED,
},
opsgenie: {
opsgenieMvcIsAvailable: true,
formPath: INVALID_URL,
activated: ACTIVATED,
opsgenieMvcTargetUrl: GENERIC_URL,
},
...defaultAlertSettingsConfig,
},
methods,
});
......@@ -63,7 +38,10 @@ describe('AlertsSettingsForm', () => {
});
afterEach(() => {
wrapper.destroy();
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
});
describe('with default values', () => {
......@@ -76,11 +54,6 @@ describe('AlertsSettingsForm', () => {
});
});
it('renders alerts integrations list', () => {
createComponent();
expect(wrapper.find(IntegrationsList).exists()).toBe(true);
});
describe('reset key', () => {
it('triggers resetKey method', () => {
const resetKey = jest.fn();
......@@ -140,7 +113,7 @@ describe('AlertsSettingsForm', () => {
createComponent(
{},
{
selectedEndpoint: 'prometheus',
selectedIntegration: 'prometheus',
},
);
});
......@@ -154,7 +127,9 @@ describe('AlertsSettingsForm', () => {
});
it('shows the correct default API URL', () => {
expect(findUrl().attributes('value')).toBe(PROMETHEUS_URL);
expect(findUrl().attributes('value')).toBe(
defaultAlertSettingsConfig.prometheus.prometheusUrl,
);
});
});
......@@ -163,7 +138,7 @@ describe('AlertsSettingsForm', () => {
createComponent(
{},
{
selectedEndpoint: 'opsgenie',
selectedIntegration: 'opsgenie',
},
);
});
......
import { shallowMount } from '@vue/test-utils';
import AlertsSettingsWrapper from '~/alerts_settings/components/alerts_settings_wrapper.vue';
import AlertsSettingsFormOld from '~/alerts_settings/components/alerts_settings_form_old.vue';
import AlertsSettingsFormNew from '~/alerts_settings/components/alerts_settings_form_new.vue';
import IntegrationsList from '~/alerts_settings/components/alerts_integrations_list.vue';
import { defaultAlertSettingsConfig } from './util';
jest.mock('~/alerts_settings/services');
describe('AlertsSettingsFormWrapper', () => {
let wrapper;
const createComponent = (data = {}, provide = {}) => {
wrapper = shallowMount(AlertsSettingsWrapper, {
data() {
return { ...data };
},
provide: {
...defaultAlertSettingsConfig,
glFeatures: { httpIntegrationsList: false },
...provide,
},
});
};
afterEach(() => {
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
});
describe('with default values', () => {
it('renders alerts integrations list and old form by default', () => {
createComponent();
expect(wrapper.find(IntegrationsList).exists()).toBe(true);
expect(wrapper.find(AlertsSettingsFormOld).exists()).toBe(true);
expect(wrapper.find(AlertsSettingsFormNew).exists()).toBe(false);
});
it('renders alerts integrations list and new form when httpIntegrationsList feature flag is enabled', () => {
createComponent({}, { glFeatures: { httpIntegrationsList: true } });
expect(wrapper.find(IntegrationsList).exists()).toBe(true);
expect(wrapper.find(AlertsSettingsFormOld).exists()).toBe(false);
expect(wrapper.find(AlertsSettingsFormNew).exists()).toBe(true);
});
});
});
const PROMETHEUS_URL = '/prometheus/alerts/notify.json';
const GENERIC_URL = '/alerts/notify.json';
const KEY = 'abcedfg123';
const INVALID_URL = 'http://invalid';
const ACTIVATED = false;
export const defaultAlertSettingsConfig = {
generic: {
authorizationKey: KEY,
formPath: INVALID_URL,
url: GENERIC_URL,
alertsSetupUrl: INVALID_URL,
alertsUsageUrl: INVALID_URL,
activated: ACTIVATED,
},
prometheus: {
authorizationKey: KEY,
prometheusFormPath: INVALID_URL,
prometheusUrl: PROMETHEUS_URL,
activated: ACTIVATED,
},
opsgenie: {
opsgenieMvcIsAvailable: true,
formPath: INVALID_URL,
activated: ACTIVATED,
opsgenieMvcTargetUrl: GENERIC_URL,
},
};
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Resolvers::AlertManagement::IntegrationsResolver do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:prometheus_integration) { create(:prometheus_service, project: project) }
let_it_be(:active_http_integration) { create(:alert_management_http_integration, project: project) }
let_it_be(:inactive_http_integration) { create(:alert_management_http_integration, :inactive, project: project) }
let_it_be(:other_proj_integration) { create(:alert_management_http_integration) }
subject { sync(resolve_http_integrations) }
context 'user does not have permission' do
it { is_expected.to be_empty }
end
context 'user has permission' do
before do
project.add_maintainer(current_user)
end
it { is_expected.to contain_exactly(active_http_integration, prometheus_integration) }
context 'feature flag is not enabled' do
before do
stub_feature_flags(multiple_http_integrations: false)
end
it { is_expected.to be_empty }
end
end
private
def resolve_http_integrations(args = {}, context = { current_user: current_user })
resolve(described_class, obj: project, ctx: context)
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['AlertManagementHttpIntegration'] do
specify { expect(described_class.graphql_name).to eq('AlertManagementHttpIntegration') }
specify { expect(described_class).to require_graphql_authorizations(:admin_operations) }
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['AlertManagementIntegrationType'] do
specify { expect(described_class.graphql_name).to eq('AlertManagementIntegrationType') }
describe 'statuses' do
using RSpec::Parameterized::TableSyntax
where(:name, :value) do
'PROMETHEUS' | :prometheus
'HTTP' | :http
end
with_them do
it 'exposes a type with the correct value' do
expect(described_class.values[name].value).to eq(value)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['AlertManagementIntegration'] do
specify { expect(described_class.graphql_name).to eq('AlertManagementIntegration') }
it 'exposes the expected fields' do
expected_fields = %i[
id
type
name
active
token
url
api_url
]
expect(described_class).to have_graphql_fields(*expected_fields)
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['AlertManagementPrometheusIntegration'] do
include GraphqlHelpers
specify { expect(described_class.graphql_name).to eq('AlertManagementPrometheusIntegration') }
specify { expect(described_class).to require_graphql_authorizations(:admin_project) }
describe 'resolvers' do
shared_examples_for 'has field with value' do |field_name|
it 'correctly renders the field' do
expect(resolve_field(field_name, integration)).to eq(value)
end
end
let_it_be_with_reload(:integration) { create(:prometheus_service) }
it_behaves_like 'has field with value', 'name' do
let(:value) { integration.title }
end
it_behaves_like 'has field with value', 'type' do
let(:value) { :prometheus }
end
it_behaves_like 'has field with value', 'token' do
let(:value) { nil }
end
it_behaves_like 'has field with value', 'url' do
let(:value) { "http://localhost/#{integration.project.full_path}/prometheus/alerts/notify.json" }
end
it_behaves_like 'has field with value', 'active' do
let(:value) { integration.manual_configuration? }
end
context 'with alerting setting' do
let_it_be(:alerting_setting) { create(:project_alerting_setting, project: integration.project) }
it_behaves_like 'has field with value', 'token' do
let(:value) { alerting_setting.token }
end
end
context 'without project' do
let_it_be(:integration) { create(:prometheus_service, project: nil, group: create(:group)) }
it_behaves_like 'has field with value', 'token' do
let(:value) { nil }
end
it_behaves_like 'has field with value', 'url' do
let(:value) { nil }
end
end
end
end
......@@ -27,7 +27,8 @@ RSpec.describe GitlabSchema.types['Project'] do
environment boards jira_import_status jira_imports services releases release
alert_management_alerts alert_management_alert alert_management_alert_status_counts
container_expiration_policy service_desk_enabled service_desk_address
issue_status_counts terraform_states
issue_status_counts terraform_states alert_management_integrations
]
expect(described_class).to include_graphql_fields(*expected_fields)
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe AlertManagement::HttpIntegrationPolicy, :models do
let(:integration) { create(:alert_management_http_integration) }
let(:project) { integration.project }
let(:user) { create(:user) }
subject(:policy) { described_class.new(user, integration) }
describe 'rules' do
it { is_expected.to be_disallowed :admin_operations }
context 'when maintainer' do
before do
project.add_maintainer(user)
end
it { is_expected.to be_allowed :admin_operations }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe PrometheusServicePolicy, :models do
let(:integration) { create(:prometheus_service) }
let(:project) { integration.project }
let(:user) { create(:user) }
subject(:policy) { described_class.new(user, integration) }
describe 'rules' do
it { is_expected.to be_disallowed :admin_project }
context 'when maintainer' do
before do
project.add_maintainer(user)
end
it { is_expected.to be_allowed :admin_project }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'getting Alert Management Integrations' do
include ::Gitlab::Routing
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
let_it_be(:current_user) { create(:user) }
let_it_be(:prometheus_service) { create(:prometheus_service, project: project) }
let_it_be(:project_alerting_setting) { create(:project_alerting_setting, project: project) }
let_it_be(:active_http_integration) { create(:alert_management_http_integration, project: project) }
let_it_be(:inactive_http_integration) { create(:alert_management_http_integration, :inactive, project: project) }
let_it_be(:other_project_http_integration) { create(:alert_management_http_integration) }
let(:fields) do
<<~QUERY
nodes {
#{all_graphql_fields_for('AlertManagementIntegration')}
}
QUERY
end
let(:query) do
graphql_query_for(
'project',
{ 'fullPath' => project.full_path },
query_graphql_field('alertManagementIntegrations', {}, fields)
)
end
context 'with integrations' do
let(:integrations) { graphql_data.dig('project', 'alertManagementIntegrations', 'nodes') }
context 'without project permissions' do
let(:user) { create(:user) }
before do
post_graphql(query, current_user: current_user)
end
it_behaves_like 'a working graphql query'
it { expect(integrations).to be_nil }
end
context 'with project permissions' do
before do
project.add_maintainer(current_user)
post_graphql(query, current_user: current_user)
end
let(:http_integration) { integrations.first }
let(:prometheus_integration) { integrations.second }
it_behaves_like 'a working graphql query'
it { expect(integrations.size).to eq(2) }
it 'returns the correct properties of the integrations' do
expect(http_integration).to include(
'id' => GitlabSchema.id_from_object(active_http_integration).to_s,
'type' => 'HTTP',
'name' => active_http_integration.name,
'active' => active_http_integration.active,
'token' => active_http_integration.token,
'url' => active_http_integration.url,
'apiUrl' => nil
)
expect(prometheus_integration).to include(
'id' => GitlabSchema.id_from_object(prometheus_service).to_s,
'type' => 'PROMETHEUS',
'name' => 'Prometheus',
'active' => prometheus_service.manual_configuration?,
'token' => project_alerting_setting.token,
'url' => "http://localhost/#{project.full_path}/prometheus/alerts/notify.json",
'apiUrl' => prometheus_service.api_url
)
end
end
end
end
......@@ -66,7 +66,20 @@ RSpec.describe 'Robots.txt Requests', :aggregate_failures do
'/foo/bar/uploads/foo',
'/foo/bar/project_members',
'/foo/bar/settings',
'/namespace/subnamespace/design.gitlab.com/settings'
'/namespace/subnamespace/design.gitlab.com/settings',
'/foo/bar/-/import',
'/foo/bar/-/environments',
'/foo/bar/-/jobs',
'/foo/bar/-/requirements_management',
'/foo/bar/-/pipelines',
'/foo/bar/-/pipeline_schedules',
'/foo/bar/-/dependencies',
'/foo/bar/-/licenses',
'/foo/bar/-/metrics',
'/foo/bar/-/incidents',
'/foo/bar/-/value_stream_analytics',
'/foo/bar/-/analytics',
'/foo/bar/insights'
]
requests.each do |request|
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment