Commit e79443c7 authored by Mike Greiling's avatar Mike Greiling

Merge branch 'extract_timerange_information_to_be_shared' into 'master'

Extract time range information to be reused

See merge request gitlab-org/gitlab!26431
parents b2a51453 06a36608
......@@ -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,
......
......@@ -711,9 +711,6 @@ msgstr ""
msgid "20-29 contributions"
msgstr ""
msgid "24 hours"
msgstr ""
msgid "2FA"
msgstr ""
......@@ -726,9 +723,6 @@ msgstr ""
msgid "3 hours"
msgstr ""
msgid "30 days"
msgstr ""
msgid "30 minutes"
msgstr ""
......@@ -750,9 +744,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