Commit 06a36608 authored by zmartins's avatar zmartins

Extract time range information to be reused

Monitoring and Threat Monitoring will be
both using the same components in
regards to time range filter
parent 5eeb2ecc
......@@ -13,7 +13,7 @@ import {
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
import LogControlButtons from './log_control_buttons.vue';
import { timeRanges, defaultTimeRange } from '~/monitoring/constants';
import { timeRanges, defaultTimeRange } from '~/vue_shared/constants';
import { timeRangeFromUrl } from '~/monitoring/utils';
import { formatDate } from '../utils';
......
import { timeRanges, defaultTimeRange } from '~/monitoring/constants';
import { timeRanges, defaultTimeRange } from '~/vue_shared/constants';
import { convertToFixedRange } from '~/lib/utils/datetime_range';
export default () => ({
......
......@@ -31,7 +31,8 @@ import DashboardsDropdown from './dashboards_dropdown.vue';
import TrackEventDirective from '~/vue_shared/directives/track_event';
import { getAddMetricTrackingOptions, timeRangeToUrl, timeRangeFromUrl } from '../utils';
import { defaultTimeRange, timeRanges, metricStates } from '../constants';
import { metricStates } from '../constants';
import { defaultTimeRange, timeRanges } from '~/vue_shared/constants';
export default {
components: {
......
......@@ -3,7 +3,8 @@ import { mapActions, mapState, mapGetters } from 'vuex';
import PanelType from 'ee_else_ce/monitoring/components/panel_type.vue';
import { convertToFixedRange } from '~/lib/utils/datetime_range';
import { timeRangeFromUrl, removeTimeRangeParams } from '../utils';
import { sidebarAnimationDuration, defaultTimeRange } from '../constants';
import { sidebarAnimationDuration } from '../constants';
import { defaultTimeRange } from '~/vue_shared/constants';
let sidebarMutationObserver;
......
import { __ } from '~/locale';
export const PROMETHEUS_TIMEOUT = 120000; // TWO_MINUTES
/**
......@@ -89,37 +87,3 @@ export const dateFormats = {
timeOfDay: 'h:MM TT',
default: 'dd mmm yyyy, h:MMTT',
};
export const timeRanges = [
{
label: __('30 minutes'),
duration: { seconds: 60 * 30 },
},
{
label: __('3 hours'),
duration: { seconds: 60 * 60 * 3 },
},
{
label: __('8 hours'),
duration: { seconds: 60 * 60 * 8 },
default: true,
},
{
label: __('1 day'),
duration: { seconds: 60 * 60 * 24 * 1 },
},
{
label: __('3 days'),
duration: { seconds: 60 * 60 * 24 * 3 },
},
{
label: __('1 week'),
duration: { seconds: 60 * 60 * 24 * 7 * 1 },
},
{
label: __('1 month'),
duration: { seconds: 60 * 60 * 24 * 30 },
},
];
export const defaultTimeRange = timeRanges.find(tr => tr.default);
......@@ -43,6 +43,11 @@ export default {
required: false,
default: () => defaultTimeRanges,
},
customEnabled: {
type: Boolean,
required: false,
default: true,
},
},
data() {
return {
......@@ -166,6 +171,7 @@ export default {
>
<div class="d-flex justify-content-between gl-p-2">
<gl-form-group
v-if="customEnabled"
:label="__('Custom range')"
label-for="custom-from-time"
label-class="gl-pb-1"
......
import { __ } from '~/locale';
const INTERVALS = {
minute: 'minute',
hour: 'hour',
day: 'day',
};
export const timeRanges = [
{
label: __('30 minutes'),
duration: { seconds: 60 * 30 },
name: 'thirtyMinutes',
interval: INTERVALS.minute,
},
{
label: __('3 hours'),
duration: { seconds: 60 * 60 * 3 },
name: 'threeHours',
interval: INTERVALS.hour,
},
{
label: __('8 hours'),
duration: { seconds: 60 * 60 * 8 },
name: 'eightHours',
default: true,
interval: INTERVALS.hour,
},
{
label: __('1 day'),
duration: { seconds: 60 * 60 * 24 * 1 },
name: 'oneDay',
interval: INTERVALS.hour,
},
{
label: __('3 days'),
duration: { seconds: 60 * 60 * 24 * 3 },
name: 'threeDays',
interval: INTERVALS.hour,
},
{
label: __('1 week'),
duration: { seconds: 60 * 60 * 24 * 7 * 1 },
name: 'oneWeek',
interval: INTERVALS.day,
},
{
label: __('1 month'),
duration: { seconds: 60 * 60 * 24 * 30 },
name: 'oneMonth',
interval: INTERVALS.day,
},
];
export const defaultTimeRange = timeRanges.find(tr => tr.default);
export const getTimeWindow = timeWindowName => timeRanges.find(tr => tr.name === timeWindowName);
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
import { GlFormGroup, GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { TIME_WINDOWS } from '../constants';
import { timeRanges, defaultTimeRange } from '~/vue_shared/constants';
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
export default {
name: 'ThreatMonitoringFilters',
......@@ -9,6 +10,13 @@ export default {
GlFormGroup,
GlDropdown,
GlDropdownItem,
DateTimePicker,
},
data() {
return {
selectedTimeRange: defaultTimeRange,
timeRanges,
};
},
computed: {
...mapState('threatMonitoring', [
......@@ -17,7 +25,7 @@ export default {
'isLoadingEnvironments',
'isLoadingWafStatistics',
]),
...mapGetters('threatMonitoring', ['currentEnvironmentName', 'currentTimeWindowName']),
...mapGetters('threatMonitoring', ['currentEnvironmentName']),
isDisabled() {
return (
this.isLoadingEnvironments || this.isLoadingWafStatistics || this.environments.length === 0
......@@ -26,10 +34,12 @@ export default {
},
methods: {
...mapActions('threatMonitoring', ['setCurrentEnvironmentId', 'setCurrentTimeWindow']),
onDateTimePickerInput(timeRange) {
this.selectedTimeRange = timeRange;
this.setCurrentTimeWindow(timeRange);
},
},
environmentFilterId: 'threat-monitoring-environment-filter',
showLastFilterId: 'threat-monitoring-show-last-filter',
timeWindows: TIME_WINDOWS,
};
</script>
......@@ -63,25 +73,17 @@ export default {
<gl-form-group
:label="s__('ThreatMonitoring|Show last')"
label-size="sm"
:label-for="$options.showLastFilterId"
class="col-sm-6 col-md-4 col-lg-3 col-xl-2"
label-for="threat-monitoring-time-window-dropdown"
class="col-sm-6 col-md-6 col-lg-4"
>
<gl-dropdown
:id="$options.showLastFilterId"
ref="showLastDropdown"
class="mb-0 d-flex"
toggle-class="d-flex justify-content-between"
:text="currentTimeWindowName"
<date-time-picker
ref="dateTimePicker"
:custom-enabled="false"
:value="selectedTimeRange"
:options="timeRanges"
:disabled="isDisabled"
>
<gl-dropdown-item
v-for="(timeWindowConfig, timeWindow) in $options.timeWindows"
:key="timeWindow"
ref="showLastDropdownItem"
@click="setCurrentTimeWindow(timeWindow)"
>{{ timeWindowConfig.name }}</gl-dropdown-item
>
</gl-dropdown>
@input="onDateTimePickerInput"
/>
</gl-form-group>
</div>
</div>
......
import { __ } from '~/locale';
/* eslint-disable import/prefer-default-export */
export const INVALID_CURRENT_ENVIRONMENT_NAME = '';
const INTERVALS = {
minute: 'minute',
hour: 'hour',
day: 'day',
};
export const TIME_WINDOWS = {
thirtyMinutes: {
name: __('30 minutes'),
durationInMilliseconds: 30 * 60 * 1000,
interval: INTERVALS.minute,
},
oneHour: {
name: __('1 hour'),
durationInMilliseconds: 60 * 60 * 1000,
interval: INTERVALS.minute,
},
twentyFourHours: {
name: __('24 hours'),
durationInMilliseconds: 24 * 60 * 60 * 1000,
interval: INTERVALS.hour,
},
sevenDays: {
name: __('7 days'),
durationInMilliseconds: 7 * 24 * 60 * 60 * 1000,
interval: INTERVALS.day,
},
thirtyDays: {
name: __('30 days'),
durationInMilliseconds: 30 * 24 * 60 * 60 * 1000,
interval: INTERVALS.day,
},
};
export const DEFAULT_TIME_WINDOW = 'thirtyDays';
......@@ -64,7 +64,7 @@ export const setCurrentEnvironmentId = ({ commit, dispatch }, environmentId) =>
};
export const setCurrentTimeWindow = ({ commit, dispatch }, timeWindow) => {
commit(types.SET_CURRENT_TIME_WINDOW, timeWindow);
commit(types.SET_CURRENT_TIME_WINDOW, timeWindow.name);
dispatch(`threatMonitoringWaf/fetchStatistics`, null, { root: true });
if (window.gon.features?.networkPolicyUi) {
......
/* eslint-disable import/prefer-default-export */
import { INVALID_CURRENT_ENVIRONMENT_NAME } from '../../../constants';
import { getTimeWindowConfig } from '../../utils';
export const currentEnvironmentName = ({ currentEnvironmentId, environments }) => {
const environment = environments.find(({ id }) => id === currentEnvironmentId);
return environment ? environment.name : INVALID_CURRENT_ENVIRONMENT_NAME;
};
export const currentTimeWindowName = ({ currentTimeWindow }) =>
getTimeWindowConfig(currentTimeWindow).name;
import { DEFAULT_TIME_WINDOW } from '../../../constants';
import { defaultTimeRange } from '~/vue_shared/constants';
export default () => ({
environmentsEndpoint: '',
......@@ -6,5 +6,5 @@ export default () => ({
isLoadingEnvironments: false,
errorLoadingEnvironments: false,
currentEnvironmentId: -1,
currentTimeWindow: DEFAULT_TIME_WINDOW,
currentTimeWindow: defaultTimeRange.name,
});
import { TIME_WINDOWS, DEFAULT_TIME_WINDOW } from 'ee/threat_monitoring/constants';
import { getTimeWindow, defaultTimeRange } from '~/vue_shared/constants';
import { pick } from 'lodash';
export const getTimeWindowConfig = timeWindow =>
TIME_WINDOWS[timeWindow] || TIME_WINDOWS[DEFAULT_TIME_WINDOW];
export const getTimeWindowConfig = timeWindow => {
const timeWindowObj = pick(getTimeWindow(timeWindow) || defaultTimeRange, 'duration', 'interval');
return {
durationInMilliseconds: timeWindowObj.duration.seconds * 1000,
interval: timeWindowObj.interval,
};
};
/**
* Get the from/to/interval query parameters for the given time window.
* @param {string} timeWindow - The time window (keyof TIME_WINDOWS)
* @param {string} timeWindow - The time window name (from the array of objects timeRanges)
* @param {number} to - Milliseconds past the epoch corresponding to the
* returned `to` parameter
* @returns {Object} Query parameters `from` and `to` are ISO 8601 dates and
......
---
title: Extract time range information to be reused
merge_request: 26431
author:
type: changed
import { shallowMount } from '@vue/test-utils';
import createStore from 'ee/threat_monitoring/store';
import ThreatMonitoringFilters from 'ee/threat_monitoring/components/threat_monitoring_filters.vue';
import { INVALID_CURRENT_ENVIRONMENT_NAME, TIME_WINDOWS } from 'ee/threat_monitoring/constants';
import { INVALID_CURRENT_ENVIRONMENT_NAME } from 'ee/threat_monitoring/constants';
import { mockEnvironmentsResponse } from '../mock_data';
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
import { timeRanges, defaultTimeRange } from '~/vue_shared/constants';
const mockEnvironments = mockEnvironmentsResponse.environments;
......@@ -23,8 +25,7 @@ describe('ThreatMonitoringFilters component', () => {
const findEnvironmentsDropdown = () => wrapper.find({ ref: 'environmentsDropdown' });
const findEnvironmentsDropdownItems = () => wrapper.findAll({ ref: 'environmentsDropdownItem' });
const findShowLastDropdown = () => wrapper.find({ ref: 'showLastDropdown' });
const findShowLastDropdownItems = () => wrapper.findAll({ ref: 'showLastDropdownItem' });
const findShowLastDropdown = () => wrapper.find(DateTimePicker);
afterEach(() => {
wrapper.destroy();
......@@ -92,22 +93,15 @@ describe('ThreatMonitoringFilters component', () => {
});
it('has text set to the current time window name', () => {
const currentTimeWindowName = store.getters['threatMonitoring/currentTimeWindowName'];
expect(findShowLastDropdown().attributes().text).toBe(currentTimeWindowName);
expect(findShowLastDropdown().vm.value.label).toBe(defaultTimeRange.label);
});
it('has dropdown items for each time window', () => {
const dropdownItems = findShowLastDropdownItems();
Object.entries(TIME_WINDOWS).forEach(([timeWindow, config], i) => {
const dropdownItem = dropdownItems.at(i);
expect(dropdownItem.text()).toBe(config.name);
dropdownItem.vm.$emit('click');
expect(store.dispatch).toHaveBeenCalledWith(
'threatMonitoring/setCurrentTimeWindow',
timeWindow,
);
const dropdownOptions = findShowLastDropdown().props('options');
Object.entries(timeRanges).forEach(([index, timeWindow]) => {
const dropdownOption = dropdownOptions[index];
expect(dropdownOption.interval).toBe(timeWindow.interval);
expect(dropdownOption.duration.seconds).toBe(timeWindow.duration.seconds);
});
});
});
......
......@@ -235,14 +235,14 @@ describe('Threat Monitoring actions', () => {
});
describe('setCurrentTimeWindow', () => {
const timeWindow = 'foo';
const timeWindow = { name: 'foo' };
it('commits the SET_CURRENT_TIME_WINDOW mutation and dispatches WAF fetch action', () =>
testAction(
actions.setCurrentTimeWindow,
timeWindow,
state,
[{ type: types.SET_CURRENT_TIME_WINDOW, payload: timeWindow }],
[{ type: types.SET_CURRENT_TIME_WINDOW, payload: timeWindow.name }],
[{ type: 'threatMonitoringWaf/fetchStatistics', payload: null }],
));
......@@ -254,7 +254,7 @@ describe('Threat Monitoring actions', () => {
actions.setCurrentTimeWindow,
timeWindow,
state,
[{ type: types.SET_CURRENT_TIME_WINDOW, payload: timeWindow }],
[{ type: types.SET_CURRENT_TIME_WINDOW, payload: timeWindow.name }],
[
{ type: 'threatMonitoringWaf/fetchStatistics', payload: null },
{ type: 'threatMonitoringNetworkPolicy/fetchStatistics', payload: null },
......
import createState from 'ee/threat_monitoring/store/modules/threat_monitoring/state';
import * as getters from 'ee/threat_monitoring/store/modules/threat_monitoring/getters';
import { INVALID_CURRENT_ENVIRONMENT_NAME, TIME_WINDOWS } from 'ee/threat_monitoring/constants';
import { INVALID_CURRENT_ENVIRONMENT_NAME } from 'ee/threat_monitoring/constants';
describe('threatMonitoring module getters', () => {
let state;
......@@ -26,18 +26,4 @@ describe('threatMonitoring module getters', () => {
});
});
});
describe('currentTimeWindowName', () => {
it('gives the correct name for a valid time window', () => {
Object.keys(TIME_WINDOWS).forEach(timeWindow => {
state.currentTimeWindow = timeWindow;
expect(getters.currentTimeWindowName(state)).toBe(TIME_WINDOWS[timeWindow].name);
});
});
it('gives the default name for an invalid time window', () => {
state.currentTimeWindowName = 'foo';
expect(getters.currentTimeWindowName(state)).toBe('30 days');
});
});
});
......@@ -14,7 +14,7 @@ jest.mock('~/flash', () => jest.fn());
const statisticsEndpoint = 'statisticsEndpoint';
const timeRange = {
from: '2019-01-01T00:00:00.000Z',
from: '2019-01-30T16:00:00.000Z',
to: '2019-01-31T00:00:00.000Z',
};
......@@ -101,7 +101,7 @@ describe('threatMonitoringStatistics actions', () => {
.onGet(statisticsEndpoint, {
params: {
environment_id: currentEnvironmentId,
interval: 'day',
interval: 'hour',
...timeRange,
},
})
......
import { getTimeWindowConfig, getTimeWindowParams } from 'ee/threat_monitoring/store/utils';
import { DEFAULT_TIME_WINDOW, TIME_WINDOWS } from 'ee/threat_monitoring/constants';
import { defaultTimeRange, timeRanges } from '~/vue_shared/constants';
describe('threatMonitoring module utils', () => {
describe('getTimeWindowConfig', () => {
it('gives the correct config for a valid time window', () => {
Object.entries(TIME_WINDOWS).forEach(([timeWindow, expectedConfig]) => {
expect(getTimeWindowConfig(timeWindow)).toBe(expectedConfig);
Object.entries(timeRanges).forEach(([, timeWindow]) => {
const timeWindowConfig = getTimeWindowConfig(timeWindow.name);
expect(timeWindowConfig.interval).toBe(timeWindow.interval);
expect(timeWindowConfig.durationInMilliseconds).toBe(timeWindow.duration.seconds * 1000);
});
});
it('gives the default name for an invalid time window', () => {
expect(getTimeWindowConfig('foo')).toBe(TIME_WINDOWS[DEFAULT_TIME_WINDOW]);
const timeWindowConfig = getTimeWindowConfig('foo');
expect(timeWindowConfig.interval).toBe(defaultTimeRange.interval);
expect(timeWindowConfig.durationInMilliseconds).toBe(
defaultTimeRange.duration.seconds * 1000,
);
});
});
......@@ -18,17 +24,19 @@ describe('threatMonitoring module utils', () => {
const mockTimestamp = new Date(2020, 0, 1, 10).getTime();
it.each`
timeWindow | expectedFrom | interval
${'thirtyMinutes'} | ${'2020-01-01T09:30:00.000Z'} | ${'minute'}
${'oneHour'} | ${'2020-01-01T09:00:00.000Z'} | ${'minute'}
${'twentyFourHours'} | ${'2019-12-31T10:00:00.000Z'} | ${'hour'}
${'sevenDays'} | ${'2019-12-25T10:00:00.000Z'} | ${'day'}
${'thirtyDays'} | ${'2019-12-02T10:00:00.000Z'} | ${'day'}
${'foo'} | ${'2019-12-02T10:00:00.000Z'} | ${'day'}
timeWindowName | expectedFrom | interval
${'thirtyMinutes'} | ${'2020-01-01T09:30:00.000Z'} | ${'minute'}
${'threeHours'} | ${'2020-01-01T07:00:00.000Z'} | ${'hour'}
${'eightHours'} | ${'2020-01-01T02:00:00.000Z'} | ${'hour'}
${'oneDay'} | ${'2019-12-31T10:00:00.000Z'} | ${'hour'}
${'threeDays'} | ${'2019-12-29T10:00:00.000Z'} | ${'hour'}
${'oneWeek'} | ${'2019-12-25T10:00:00.000Z'} | ${'day'}
${'oneMonth'} | ${'2019-12-02T10:00:00.000Z'} | ${'day'}
${'foo'} | ${'2020-01-01T02:00:00.000Z'} | ${'hour'}
`(
'returns the expected params given "$timeWindow"',
({ timeWindow, expectedFrom, interval }) => {
expect(getTimeWindowParams(timeWindow, mockTimestamp)).toEqual({
'returns the expected params given "$timeWindowName"',
({ timeWindowName, expectedFrom, interval }) => {
expect(getTimeWindowParams(timeWindowName, mockTimestamp)).toEqual({
from: expectedFrom,
to: '2020-01-01T10:00:00.000Z',
interval,
......
......@@ -708,9 +708,6 @@ msgstr ""
msgid "20-29 contributions"
msgstr ""
msgid "24 hours"
msgstr ""
msgid "2FA"
msgstr ""
......@@ -723,9 +720,6 @@ msgstr ""
msgid "3 hours"
msgstr ""
msgid "30 days"
msgstr ""
msgid "30 minutes"
msgstr ""
......@@ -747,9 +741,6 @@ msgstr ""
msgid "404|Please contact your GitLab administrator if you think this is a mistake."
msgstr ""
msgid "7 days"
msgstr ""
msgid "8 hours"
msgstr ""
......
......@@ -13,7 +13,7 @@ import {
fetchMoreLogsPrepend,
} from '~/logs/stores/actions';
import { defaultTimeRange } from '~/monitoring/constants';
import { defaultTimeRange } from '~/vue_shared/constants';
import axios from '~/lib/utils/axios_utils';
import flash from '~/flash';
......
......@@ -78,6 +78,7 @@ exports[`Dashboard template matches the default snapshot 1`] = `
label-size="sm"
>
<date-time-picker-stub
customenabled="true"
options="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]"
value="[object Object]"
/>
......
......@@ -7,7 +7,7 @@ import { mockProjectDir } from '../mock_data';
import Dashboard from '~/monitoring/components/dashboard.vue';
import { createStore } from '~/monitoring/stores';
import { defaultTimeRange } from '~/monitoring/constants';
import { defaultTimeRange } from '~/vue_shared/constants';
import { propsData } from '../init_utils';
jest.mock('~/flash');
......
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