Commit b48820cf authored by David O'Regan's avatar David O'Regan Committed by Olena Horal-Koretska

Add multi HTTP support

Bootstrap the base for the
multi HTTP alert form
and hide it with a feature flag
parent 3dcc63fa
<script>
import { GlTable, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { GlTable, GlIcon, GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import Tracking from '~/tracking';
import { trackAlertIntegrationsViewsOptions } from '../constants';
......@@ -27,6 +27,7 @@ export default {
components: {
GlTable,
GlIcon,
GlLoadingIcon,
},
directives: {
GlTooltip: GlTooltipDirective,
......@@ -37,10 +38,15 @@ export default {
required: false,
default: () => [],
},
loading: {
type: Boolean,
required: false,
default: false,
},
},
fields: [
{
key: 'activated',
key: 'active',
label: __('Status'),
},
{
......@@ -78,12 +84,13 @@ export default {
:empty-text="$options.i18n.emptyState"
:items="integrations"
:fields="$options.fields"
:busy="loading"
stacked="md"
:tbody-tr-class="tbodyTrClass"
show-empty
>
<template #cell(activated)="{ item }">
<span v-if="item.activated" data-testid="integration-activated-status">
<template #cell(active)="{ item }">
<span v-if="item.active" data-testid="integration-activated-status">
<gl-icon
v-gl-tooltip
name="check-circle-filled"
......@@ -104,6 +111,10 @@ export default {
{{ $options.i18n.status.disabled.name }}
</span>
</template>
<template #table-busy>
<gl-loading-icon size="lg" color="dark" class="mt-3" />
</template>
</gl-table>
</div>
</template>
<script>
import { s__ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { fetchPolicies } from '~/lib/graphql';
import getIntegrationsQuery from '../graphql/queries/get_integrations.query.graphql';
import IntegrationsList from './alerts_integrations_list.vue';
import SettingsFormOld from './alerts_settings_form_old.vue';
import SettingsFormNew from './alerts_settings_form_new.vue';
......@@ -19,19 +21,52 @@ export default {
prometheus: {
default: {},
},
projectPath: {
default: '',
},
},
apollo: {
integrations: {
fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
query: getIntegrationsQuery,
variables() {
return {
projectPath: this.projectPath,
};
},
update(data) {
const { alertManagementIntegrations: { nodes: list = [] } = {} } = data.project || {};
return {
list,
};
},
error() {
this.errored = true;
},
},
},
data() {
return {
errored: false,
integrations: {},
};
},
computed: {
integrations() {
loading() {
return this.$apollo.queries.integrations.loading;
},
intergrationsOptionsOld() {
return [
{
name: s__('AlertSettings|HTTP endpoint'),
type: s__('AlertsIntegrations|HTTP endpoint'),
activated: this.generic.activated,
active: this.generic.activated,
},
{
name: s__('AlertSettings|External Prometheus'),
type: s__('AlertsIntegrations|Prometheus'),
activated: this.prometheus.activated,
active: this.prometheus.activated,
},
];
},
......@@ -41,7 +76,10 @@ export default {
<template>
<div>
<integrations-list :integrations="integrations" />
<integrations-list
:integrations="glFeatures.httpIntegrationsList ? integrations.list : intergrationsOptionsOld"
:loading="loading"
/>
<settings-form-new v-if="glFeatures.httpIntegrationsList" />
<settings-form-old v-else />
</div>
......
fragment IntegrationItem on AlertManagementIntegration {
id
type
active
name
url
token
apiUrl
}
#import "../fragments/integration_item.fragment.graphql"
query getIntegrations($projectPath: ID!) {
project(fullPath: $projectPath) {
alertManagementIntegrations {
nodes {
...IntegrationItem
}
}
}
}
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import { parseBoolean } from '~/lib/utils/common_utils';
import AlertSettingsWrapper from './components/alerts_settings_wrapper.vue';
Vue.use(VueApollo);
export default el => {
if (!el) {
return null;
......@@ -24,8 +28,22 @@ export default el => {
opsgenieMvcFormPath,
opsgenieMvcEnabled,
opsgenieMvcTargetUrl,
projectPath,
} = el.dataset;
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(
{},
{
cacheConfig: {},
},
),
});
apolloProvider.clients.defaultClient.cache.writeData({
data: {},
});
return new Vue({
el,
provide: {
......@@ -51,7 +69,9 @@ export default el => {
opsgenieMvcTargetUrl,
opsgenieMvcIsAvailable: parseBoolean(opsgenieMvcAvailable),
},
projectPath,
},
apolloProvider,
components: {
AlertSettingsWrapper,
},
......
......@@ -8,13 +8,12 @@
@include gl-text-gray-500;
tbody {
tr:not(.b-table-busy-slot) {
// TODO replace with gitlab/ui utilities: https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/1791
tr:not(.b-table-busy-slot):not(.b-table-empty-row) {
&:hover {
border-top-style: double;
@include gl-border-t-double;
td {
border-bottom-style: initial;
@include gl-border-b-initial;
}
}
}
......@@ -22,7 +21,7 @@
tr {
&:focus {
outline: none;
@include gl-outline-none;
}
td,
......@@ -118,22 +117,22 @@
}
.gl-tabs-nav {
border-bottom-width: 0;
@include gl-border-b-0;
.gl-tab-nav-item {
color: $gray-500;
@include gl-text-gray-500;
> .gl-tab-counter-badge {
color: inherit;
@include gl-reset-color;
@include gl-font-sm;
background-color: $gray-50;
@include gl-bg-gray-50;
}
}
}
@include media-breakpoint-down(xs) {
.list-header {
flex-direction: column-reverse;
@include gl-flex-direction-column-reverse;
}
.create-incident-button {
......
......@@ -29,7 +29,8 @@ module OperationsHelper
'url' => alerts_service.url,
'alerts_setup_url' => help_page_path('operations/incident_management/alert_integrations.md', anchor: 'generic-http-endpoint'),
'alerts_usage_url' => project_alert_management_index_path(@project),
'disabled' => disabled.to_s
'disabled' => disabled.to_s,
'project_path' => project_path(@project)
}
end
......
......@@ -8,12 +8,12 @@ import { trackAlertIntegrationsViewsOptions } from '~/alerts_settings/constants'
const mockIntegrations = [
{
activated: true,
active: true,
name: 'Integration 1',
type: 'HTTP endpoint',
},
{
activated: false,
active: false,
name: 'Integration 2',
type: 'HTTP endpoint',
},
......
......@@ -3,8 +3,6 @@ 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;
......
import { shallowMount } from '@vue/test-utils';
import { mount } from '@vue/test-utils';
import { GlLoadingIcon } from '@gitlab/ui';
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';
import mockIntegrations from './mocks/integrations.json';
jest.mock('~/alerts_settings/services');
describe('AlertsSettingsFormWrapper', () => {
describe('AlertsSettingsWrapper', () => {
let wrapper;
const createComponent = (data = {}, provide = {}) => {
wrapper = shallowMount(AlertsSettingsWrapper, {
const findLoader = () => wrapper.find(IntegrationsList).find(GlLoadingIcon);
const findIntegrations = () => wrapper.find(IntegrationsList).findAll('table tbody tr');
const createComponent = ({ data = {}, provide = {}, loading = false } = {}) => {
wrapper = mount(AlertsSettingsWrapper, {
data() {
return { ...data };
},
......@@ -20,6 +23,16 @@ describe('AlertsSettingsFormWrapper', () => {
glFeatures: { httpIntegrationsList: false },
...provide,
},
mocks: {
$apollo: {
query: jest.fn(),
queries: {
integrations: {
loading,
},
},
},
},
});
};
......@@ -30,19 +43,41 @@ describe('AlertsSettingsFormWrapper', () => {
}
});
describe('with default values', () => {
it('renders alerts integrations list and old form by default', () => {
describe('with httpIntegrationsList feature flag disabled', () => {
it('renders data driven 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 } });
describe('with httpIntegrationsList feature flag enabled', () => {
it('renders the GraphQL alerts integrations list and new form', () => {
createComponent({ provide: { glFeatures: { httpIntegrationsList: true } } });
expect(wrapper.find(IntegrationsList).exists()).toBe(true);
expect(wrapper.find(AlertsSettingsFormOld).exists()).toBe(false);
expect(wrapper.find(AlertsSettingsFormNew).exists()).toBe(true);
});
it('uses a loading state inside the IntegrationsList table', () => {
createComponent({
data: { integrations: {} },
provide: { glFeatures: { httpIntegrationsList: true } },
loading: true,
});
expect(wrapper.find(IntegrationsList).exists()).toBe(true);
expect(findLoader().exists()).toBe(true);
});
it('renders the IntegrationsList table using the API data', () => {
createComponent({
data: { integrations: { list: mockIntegrations } },
provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
expect(findLoader().exists()).toBe(false);
expect(findIntegrations()).toHaveLength(mockIntegrations.length);
});
});
});
[
{
"id": "gid://gitlab/AlertManagement::HttpIntegration/7",
"type": "HTTP",
"active": true,
"name": "test",
"url": "http://192.168.1.152:3000/root/autodevops/alerts/notify/test/eddd36969b2d3d6a.json",
"token": "7eb24af194116411ec8d66b58c6b0d2e",
"apiUrl": null
},
{
"id": "gid://gitlab/AlertManagement::HttpIntegration/6",
"type": "HTTP",
"active": false,
"name": "test",
"url": "http://192.168.1.152:3000/root/autodevops/alerts/notify/test/abce123.json",
"token": "8639e0ce06c731b00ee3e8dcdfd14fe0",
"apiUrl": null
},
{
"id": "gid://gitlab/AlertManagement::HttpIntegration/5",
"type": "HTTP",
"active": false,
"name": "test",
"url": "http://192.168.1.152:3000/root/autodevops/alerts/notify/test/bcd64c85f918a2e2.json",
"token": "5c8101533d970a55d5c105f8abff2192",
"apiUrl": null
},
{
"id": "gid://gitlab/PrometheusService/12",
"type": "PROMETHEUS",
"active": true,
"name": "Prometheus",
"url": "http://192.168.1.152:3000/root/autodevops/prometheus/alerts/notify.json",
"token": "0b18c37caa8fe980799b349916fe5ddf",
"apiUrl": "https://another-url-2.com"
}
]
......@@ -43,7 +43,8 @@ RSpec.describe OperationsHelper do
'prometheus_api_url' => nil,
'prometheus_activated' => 'false',
'prometheus_url' => notify_project_prometheus_alerts_url(project, format: :json),
'disabled' => 'false'
'disabled' => 'false',
'project_path' => project_path(project)
)
end
end
......
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