diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index 705044565a0..26e2c2568c1 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -11,7 +11,7 @@ import { GlModalDirective, GlTooltipDirective, } from '@gitlab/ui'; -import { __, s__ } from '~/locale'; +import { s__ } from '~/locale'; import createFlash from '~/flash'; import Icon from '~/vue_shared/components/icon.vue'; import { getParameterValues, mergeUrlParams, redirectTo } from '~/lib/utils/url_utility'; @@ -167,6 +167,7 @@ export default { formIsValid: null, selectedTimeWindow: {}, isRearrangingPanels: false, + hasValidDates: true, }; }, computed: { @@ -185,7 +186,9 @@ export default { 'additionalPanelTypesEnabled', ]), firstDashboard() { - return this.allDashboards[0] || {}; + return this.environmentsEndpoint.length > 0 && this.allDashboards.length > 0 + ? this.allDashboards[0] + : {}; }, selectedDashboard() { return this.allDashboards.find(d => d.path === this.currentDashboard) || this.firstDashboard; @@ -199,9 +202,6 @@ export default { addingMetricsAvailable() { return IS_EE && this.canAddMetrics && !this.showEmptyState; }, - alertWidgetAvailable() { - return IS_EE && this.prometheusAlertsAvailable && this.alertsEndpoint; - }, hasHeaderButtons() { return ( this.addingMetricsAvailable || @@ -237,8 +237,10 @@ export default { this.selectedTimeWindow = range; if (!isValidDate(start) || !isValidDate(end)) { + this.hasValidDates = false; this.showInvalidDateError(); } else { + this.hasValidDates = true; this.fetchData(range); } } @@ -248,44 +250,13 @@ export default { 'fetchData', 'setGettingStartedEmptyState', 'setEndpoints', - 'setDashboardEnabled', 'setPanelGroupMetrics', ]), chartsWithData(charts) { - if (!this.useDashboardEndpoint) { - return charts; - } return charts.filter(chart => chart.metrics.some(metric => this.metricsWithData.includes(metric.metric_id)), ); }, - csvText(graphData) { - const chartData = graphData.queries[0].result[0].values; - const yLabel = graphData.y_label; - const header = `timestamp,${yLabel}\r\n`; // eslint-disable-line @gitlab/i18n/no-non-i18n-strings - return chartData.reduce((csv, data) => { - const row = data.join(','); - return `${csv}${row}\r\n`; - }, header); - }, - downloadCsv(graphData) { - const data = new Blob([this.csvText(graphData)], { type: 'text/plain' }); - return window.URL.createObjectURL(data); - }, - // TODO: BEGIN, Duplicated code with panel_type until feature flag is removed - // Issue number: https://gitlab.com/gitlab-org/gitlab-foss/issues/63845 - getGraphAlerts(queries) { - if (!this.allAlerts) return {}; - const metricIdsForChart = queries.map(q => q.metricId); - return _.pick(this.allAlerts, alert => metricIdsForChart.includes(alert.metricId)); - }, - getGraphAlertValues(queries) { - return Object.values(this.getGraphAlerts(queries)); - }, - showToast() { - this.$toast.show(__('Link copied')); - }, - // TODO: END updateMetrics(key, metrics) { this.setPanelGroupMetrics({ metrics, @@ -298,6 +269,11 @@ export default { key, }); }, + removeGraph(metrics, graphIndex) { + // At present graphs will not be removed, they should removed using the vuex store + // See https://gitlab.com/gitlab-org/gitlab/issues/27835 + metrics.splice(graphIndex, 1); + }, showInvalidDateError() { createFlash(s__('Metrics|Link contains an invalid time window.')); }, @@ -387,7 +363,7 @@ export default { - - + + diff --git a/app/assets/javascripts/monitoring/components/embed.vue b/app/assets/javascripts/monitoring/components/embed.vue index 80e9b7a9c57..f75839c7c6b 100644 --- a/app/assets/javascripts/monitoring/components/embed.vue +++ b/app/assets/javascripts/monitoring/components/embed.vue @@ -78,9 +78,6 @@ export default { }, sidebarAnimationDuration); }, setInitialState() { - this.setFeatureFlags({ - prometheusEndpointEnabled: true, - }); this.setEndpoints({ dashboardEndpoint: removeParams(['start', 'end'], this.dashboardUrl), }); diff --git a/app/assets/javascripts/monitoring/monitoring_bundle.js b/app/assets/javascripts/monitoring/monitoring_bundle.js index 6aa1fb5e9c6..a14145d480b 100644 --- a/app/assets/javascripts/monitoring/monitoring_bundle.js +++ b/app/assets/javascripts/monitoring/monitoring_bundle.js @@ -11,13 +11,6 @@ export default (props = {}) => { const el = document.getElementById('prometheus-graphs'); if (el && el.dataset) { - if (gon.features) { - store.dispatch('monitoringDashboard/setFeatureFlags', { - prometheusEndpointEnabled: gon.features.environmentMetricsUsePrometheusEndpoint, - additionalPanelTypesEnabled: gon.features.environmentMetricsAdditionalPanelTypes, - }); - } - const [currentDashboard] = getParameterValues('dashboard'); // eslint-disable-next-line no-new diff --git a/app/assets/javascripts/monitoring/stores/actions.js b/app/assets/javascripts/monitoring/stores/actions.js index c1fe7aa7e71..a670becf91a 100644 --- a/app/assets/javascripts/monitoring/stores/actions.js +++ b/app/assets/javascripts/monitoring/stores/actions.js @@ -35,14 +35,6 @@ export const setEndpoints = ({ commit }, endpoints) => { commit(types.SET_ENDPOINTS, endpoints); }; -export const setFeatureFlags = ( - { commit }, - { prometheusEndpointEnabled, additionalPanelTypesEnabled }, -) => { - commit(types.SET_DASHBOARD_ENABLED, prometheusEndpointEnabled); - commit(types.SET_ADDITIONAL_PANEL_TYPES_ENABLED, additionalPanelTypesEnabled); -}; - export const setShowErrorBanner = ({ commit }, enabled) => { commit(types.SET_SHOW_ERROR_BANNER, enabled); }; @@ -79,29 +71,7 @@ export const fetchData = ({ dispatch }, params) => { dispatch('fetchEnvironmentsData'); }; -export const fetchMetricsData = ({ state, dispatch }, params) => { - if (state.useDashboardEndpoint) { - return dispatch('fetchDashboard', params); - } - - dispatch('requestMetricsData'); - - return backOffRequest(() => axios.get(state.metricsEndpoint, { params })) - .then(resp => resp.data) - .then(response => { - if (!response || !response.data || !response.success) { - dispatch('receiveMetricsDataFailure', null); - createFlash(s__('Metrics|Unexpected metrics data response from prometheus endpoint')); - } - dispatch('receiveMetricsDataSuccess', response.data); - }) - .catch(error => { - dispatch('receiveMetricsDataFailure', error); - if (state.setShowErrorBanner) { - createFlash(s__('Metrics|There was an error while retrieving metrics')); - } - }); -}; +export const fetchMetricsData = ({ dispatch }, params) => dispatch('fetchDashboard', params); export const fetchDashboard = ({ state, dispatch }, params) => { dispatch('requestMetricsDashboard'); diff --git a/app/assets/javascripts/monitoring/stores/mutation_types.js b/app/assets/javascripts/monitoring/stores/mutation_types.js index 4d05fa7beb9..fa15a2ba800 100644 --- a/app/assets/javascripts/monitoring/stores/mutation_types.js +++ b/app/assets/javascripts/monitoring/stores/mutation_types.js @@ -9,8 +9,6 @@ export const RECEIVE_ENVIRONMENTS_DATA_SUCCESS = 'RECEIVE_ENVIRONMENTS_DATA_SUCC export const RECEIVE_ENVIRONMENTS_DATA_FAILURE = 'RECEIVE_ENVIRONMENTS_DATA_FAILURE'; export const SET_QUERY_RESULT = 'SET_QUERY_RESULT'; export const SET_TIME_WINDOW = 'SET_TIME_WINDOW'; -export const SET_DASHBOARD_ENABLED = 'SET_DASHBOARD_ENABLED'; -export const SET_ADDITIONAL_PANEL_TYPES_ENABLED = 'SET_ADDITIONAL_PANEL_TYPES_ENABLED'; export const SET_ALL_DASHBOARDS = 'SET_ALL_DASHBOARDS'; export const SET_ENDPOINTS = 'SET_ENDPOINTS'; export const SET_GETTING_STARTED_EMPTY_STATE = 'SET_GETTING_STARTED_EMPTY_STATE'; diff --git a/app/assets/javascripts/monitoring/stores/mutations.js b/app/assets/javascripts/monitoring/stores/mutations.js index 9e38314f8af..26d4c56ca78 100644 --- a/app/assets/javascripts/monitoring/stores/mutations.js +++ b/app/assets/javascripts/monitoring/stores/mutations.js @@ -25,12 +25,10 @@ export default { // for backwards compatibility, and to limit Vue template changes: // for each group alias panels to metrics // for each panel alias metrics to queries - if (state.useDashboardEndpoint) { - metrics = panels.map(panel => ({ - ...panel, - queries: panel.metrics, - })); - } + metrics = panels.map(panel => ({ + ...panel, + queries: panel.metrics, + })); return { ...group, @@ -42,8 +40,6 @@ export default { if (!state.dashboard.panel_groups.length) { state.emptyState = 'noData'; - } else { - state.showEmptyState = false; } }, [types.RECEIVE_METRICS_DATA_FAILURE](state, error) { @@ -90,9 +86,6 @@ export default { state.currentDashboard = endpoints.currentDashboard; state.projectPath = endpoints.projectPath; }, - [types.SET_DASHBOARD_ENABLED](state, enabled) { - state.useDashboardEndpoint = enabled; - }, [types.SET_GETTING_STARTED_EMPTY_STATE](state) { state.emptyState = 'gettingStarted'; }, @@ -103,9 +96,6 @@ export default { [types.SET_ALL_DASHBOARDS](state, dashboards) { state.allDashboards = dashboards; }, - [types.SET_ADDITIONAL_PANEL_TYPES_ENABLED](state, enabled) { - state.additionalPanelTypesEnabled = enabled; - }, [types.SET_SHOW_ERROR_BANNER](state, enabled) { state.showErrorBanner = enabled; }, diff --git a/app/assets/javascripts/monitoring/stores/state.js b/app/assets/javascripts/monitoring/stores/state.js index 5d7c46ca0c5..87e94311176 100644 --- a/app/assets/javascripts/monitoring/stores/state.js +++ b/app/assets/javascripts/monitoring/stores/state.js @@ -7,8 +7,6 @@ export default () => ({ environmentsEndpoint: null, deploymentsEndpoint: null, dashboardEndpoint: invalidUrl, - useDashboardEndpoint: false, - additionalPanelTypesEnabled: false, emptyState: 'gettingStarted', showEmptyState: true, showErrorBanner: true, diff --git a/app/assets/javascripts/pipelines/components/graph/action_component.vue b/app/assets/javascripts/pipelines/components/graph/action_component.vue index 3c85bb61ce8..fd59a580961 100644 --- a/app/assets/javascripts/pipelines/components/graph/action_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/action_component.vue @@ -88,7 +88,7 @@ export default { :title="tooltipText" :class="cssClass" :disabled="isDisabled" - class="js-ci-action btn btn-blank btn-transparent ci-action-icon-container ci-action-icon-wrapper" + class="js-ci-action btn btn-blank btn-transparent ci-action-icon-container ci-action-icon-wrapper d-flex align-items-center justify-content-center" @click="onClickAction" > diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 132476f832c..faf669829e1 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -871,7 +871,7 @@ button.mini-pipeline-graph-dropdown-toggle { height: $ci-action-dropdown-svg-size; fill: $gl-text-color-secondary; position: relative; - top: 1px; + top: auto; vertical-align: initial; } } diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index e0da3d9b6c9..4562296cea0 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -13,8 +13,6 @@ class Projects::EnvironmentsController < Projects::ApplicationController before_action :verify_api_request!, only: :terminal_websocket_authorize before_action :expire_etag_cache, only: [:index] before_action only: [:metrics, :additional_metrics, :metrics_dashboard] do - push_frontend_feature_flag(:environment_metrics_use_prometheus_endpoint, default_enabled: true) - push_frontend_feature_flag(:environment_metrics_additional_panel_types) push_frontend_feature_flag(:prometheus_computed_alerts) end diff --git a/app/models/grafana_integration.rb b/app/models/grafana_integration.rb index 620bb4231a0..ed4c279965a 100644 --- a/app/models/grafana_integration.rb +++ b/app/models/grafana_integration.rb @@ -16,6 +16,8 @@ class GrafanaIntegration < ApplicationRecord validates :enabled, inclusion: { in: [true, false] } + scope :enabled, -> { where(enabled: true) } + def client return unless enabled? diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md index ce35a5e746b..7d575e9b0b1 100644 --- a/doc/development/documentation/index.md +++ b/doc/development/documentation/index.md @@ -16,7 +16,7 @@ In addition to this page, the following resources can help you craft and contrib ## Source files and rendered web locations -Documentation for GitLab, GitLab Runner, Omnibus GitLab and Charts are published to . Documentation for GitLab is also published within the application at `/help` on the domain of the GitLab instance. +Documentation for GitLab, GitLab Runner, Omnibus GitLab and Charts is published to . Documentation for GitLab is also published within the application at `/help` on the domain of the GitLab instance. At `/help`, only help for your current edition and version is included. Help for other versions is available at . The source of the documentation exists within the codebase of each GitLab application in the following repository locations: diff --git a/doc/install/installation.md b/doc/install/installation.md index cc91f14045f..2a26a61544a 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -588,7 +588,7 @@ You can specify a different Git repository by providing it as an extra parameter sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse,https://example.com/gitlab-workhorse.git]" RAILS_ENV=production ``` -### Install GitLab-Elasticsearch-indexer` +### Install GitLab-Elasticsearch-indexer GitLab-Elasticsearch-Indexer uses [GNU Make](https://www.gnu.org/software/make/). The following command-line will install GitLab-Elasticsearch-Indexer in `/home/git/gitlab-elasticsearch-indexer` diff --git a/doc/user/group/index.md b/doc/user/group/index.md index 934fbb28a6c..5f45a462f94 100644 --- a/doc/user/group/index.md +++ b/doc/user/group/index.md @@ -123,7 +123,7 @@ For more details on creating groups, watch the video [GitLab Namespaces (users, ## Add users to a group A benefit of putting multiple projects in one group is that you can -give a user to access to all projects in the group with one action. +give a user access to all projects in the group with one action. Add members to a group by navigating to the group's dashboard and clicking **Members**. @@ -135,14 +135,14 @@ Consider a group with two projects: - On the **Group Members** page, you can now add a new user to the group. - Now, because this user is a **Developer** member of the group, they automatically - gets **Developer** access to **all projects** within that group. + get **Developer** access to **all projects** within that group. To increase the access level of an existing user for a specific project, add them again as a new member to the project with the desired permission level. ## Request access to a group -As a group owner, you can enable or disable the ability for non members to request access to +As a group owner, you can enable or disable the ability for non-members to request access to your group. Go to the group settings, and click **Allow users to request access**. As a user, you can request to be a member of a group, if that setting is enabled. Go to the group for which you'd like to be a member, and click the **Request Access** button on the right @@ -356,7 +356,7 @@ Restriction currently applies to: - [From GitLab 12.3](https://gitlab.com/gitlab-org/gitlab/issues/12874), API access. - [From GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/issues/32113), Git actions via SSH. -To avoid accidental lock-out, admins and group owners are are able to access +To avoid accidental lock-out, admins and group owners are able to access the group regardless of the IP restriction. #### Allowed domain restriction **(PREMIUM)** @@ -385,7 +385,7 @@ Some domains cannot be restricted. These are the most popular public email domai To enable this feature: 1. Navigate to the group's **Settings > General** page. -1. Expand the **Permissions, LFS, 2FA** section, and enter domain name into **Restrict membership by email** field. +1. Expand the **Permissions, LFS, 2FA** section, and enter the domain name into **Restrict membership by email** field. 1. Click **Save changes**. This will enable the domain-checking for all new users added to the group from this moment on. @@ -445,7 +445,7 @@ To enable this feature: > [Introduced](https://gitlab.com/gitlab-org/gitlab/merge_requests/13294) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.0. -A group owner can check the aggregated storage usage for all the project in a group, sub-groups included, in the **Storage** tab of the **Usage Quotas** page available to the group page settings list. +A group owner can check the aggregated storage usage for all the projects in a group, sub-groups included, in the **Storage** tab of the **Usage Quotas** page available to the group page settings list. ![Group storage usage quota](img/group_storage_usage_quota.png) diff --git a/doc/user/markdown.md b/doc/user/markdown.md index 43974a51205..3bd0dcafc19 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -405,6 +405,7 @@ GFM will recognize the following: | label by ID | `~123` | `namespace/project~123` | `project~123` | | one-word label by name | `~bug` | `namespace/project~bug` | `project~bug` | | multi-word label by name | `~"feature request"` | `namespace/project~"feature request"` | `project~"feature request"` | +| scoped label by name | `~"priority::high"` | `namespace/project~"priority::high"` | `project~"priority::high"` | | project milestone by ID | `%123` | `namespace/project%123` | `project%123` | | one-word milestone by name | `%v1.23` | `namespace/project%v1.23` | `project%v1.23` | | multi-word milestone by name | `%"release candidate"` | `namespace/project%"release candidate"` | `project%"release candidate"` | diff --git a/doc/user/search/img/issue_search_filter_v12_5.png b/doc/user/search/img/issue_search_filter_v12_5.png new file mode 100644 index 00000000000..1e2dd3d98a3 Binary files /dev/null and b/doc/user/search/img/issue_search_filter_v12_5.png differ diff --git a/doc/user/search/index.md b/doc/user/search/index.md index 33f920440ac..bc31052b758 100644 --- a/doc/user/search/index.md +++ b/doc/user/search/index.md @@ -32,13 +32,14 @@ on the search field on the top-right of your screen: If you want to search for issues present in a specific project, navigate to a project's **Issues** tab, and click on the field **Search or filter results...**. It will display a dropdown menu, from which you can add filters per author, assignee, milestone, -label, weight, and 'my-reaction' (based on your emoji votes). When done, press **Enter** on your keyboard to filter the issues. +release, label, weight, confidentiality, and "my-reaction" (based on your emoji votes). +When done, press **Enter** on your keyboard to filter the issues. -![filter issues in a project](img/issue_search_filter.png) +![filter issues in a project](img/issue_search_filter_v12_5.png) The same process is valid for merge requests. Navigate to your project's **Merge Requests** tab, and click **Search or filter results...**. Merge requests can be filtered by author, assignee, -milestone, and label. +approver, milestone, release, label, "my-reaction", "work in progess" status, and target branch. ### Filtering by **None** / **Any** diff --git a/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml index 9cc3b4cade6..a888f146664 100644 --- a/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml @@ -1,8 +1,8 @@ -.auto-deploy: +.dast-auto-deploy: image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v0.4.0" dast_environment_deploy: - extends: .auto-deploy + extends: .dast-auto-deploy stage: review script: - auto-deploy check_kube_domain @@ -31,7 +31,7 @@ dast_environment_deploy: - $DAST_WEBSITE # we don't need to create a review app if a URL is already given stop_dast_environment: - extends: .auto-deploy + extends: .dast-auto-deploy stage: cleanup variables: GIT_STRATEGY: none diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb index 10a86d13aa7..5ad4f324a23 100644 --- a/lib/gitlab/usage_data.rb +++ b/lib/gitlab/usage_data.rb @@ -78,6 +78,7 @@ module Gitlab clusters_applications_knative: count(::Clusters::Applications::Knative.available), clusters_applications_elastic_stack: count(::Clusters::Applications::ElasticStack.available), in_review_folder: count(::Environment.in_review_folder), + grafana_integrated_projects: count(GrafanaIntegration.enabled), groups: count(Group), issues: count(Issue), issues_with_associated_zoom_link: count(ZoomMeeting.added_to_issue), diff --git a/locale/gitlab.pot b/locale/gitlab.pot index b70e1d3e40e..5365c1bab23 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -10810,9 +10810,6 @@ msgstr "" msgid "Metrics|Unexpected deployment data response from prometheus endpoint" msgstr "" -msgid "Metrics|Unexpected metrics data response from prometheus endpoint" -msgstr "" - msgid "Metrics|Unit label" msgstr "" diff --git a/spec/frontend/monitoring/charts/time_series_spec.js b/spec/frontend/monitoring/charts/time_series_spec.js index 03d63ef7c3e..554535418fe 100644 --- a/spec/frontend/monitoring/charts/time_series_spec.js +++ b/spec/frontend/monitoring/charts/time_series_spec.js @@ -1,22 +1,23 @@ import { shallowMount } from '@vue/test-utils'; +import { setTestTimeout } from 'helpers/timeout'; import { createStore } from '~/monitoring/stores'; import { GlLink } from '@gitlab/ui'; import { GlAreaChart, GlLineChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts'; import { shallowWrapperContainsSlotText } from 'helpers/vue_test_utils_helper'; import TimeSeries from '~/monitoring/components/charts/time_series.vue'; import * as types from '~/monitoring/stores/mutation_types'; -import { TEST_HOST } from 'spec/test_constants'; -import MonitoringMock, { +import { deploymentData, - mockProjectPath, -} from '../../../javascripts/monitoring/mock_data'; + metricsGroupsAPIResponse, + mockedQueryResultPayload, + mockProjectDir, + mockHost, +} from '../mock_data'; + import * as iconUtils from '~/lib/utils/icon_utils'; const mockSvgPathContent = 'mockSvgPathContent'; -const mockSha = 'mockSha'; const mockWidgets = 'mockWidgets'; -const projectPath = `${TEST_HOST}${mockProjectPath}`; -const commitUrl = `${projectPath}/commit/${mockSha}`; jest.mock('~/lib/utils/icon_utils', () => ({ getSvgIconPathContent: jest.fn().mockImplementation( @@ -33,32 +34,44 @@ describe('Time series component', () => { let store; beforeEach(() => { + setTestTimeout(1000); + store = createStore(); - store.commit(`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, MonitoringMock.data); + + store.commit( + `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, + metricsGroupsAPIResponse, + ); + store.commit(`monitoringDashboard/${types.RECEIVE_DEPLOYMENTS_DATA_SUCCESS}`, deploymentData); - [, mockGraphData] = store.state.monitoringDashboard.dashboard.panel_groups[0].metrics; + + // Mock data contains 2 panels, pick the first one + store.commit(`monitoringDashboard/${types.SET_QUERY_RESULT}`, mockedQueryResultPayload); + + [mockGraphData] = store.state.monitoringDashboard.dashboard.panel_groups[0].metrics; makeTimeSeriesChart = (graphData, type) => shallowMount(TimeSeries, { - attachToDocument: true, propsData: { graphData: { ...graphData, type }, deploymentData: store.state.monitoringDashboard.deploymentData, - projectPath, + projectPath: `${mockHost}${mockProjectDir}`, }, slots: { default: mockWidgets, }, sync: false, store, + attachToDocument: true, }); }); describe('general functions', () => { let timeSeriesChart; - beforeEach(() => { + beforeEach(done => { timeSeriesChart = makeTimeSeriesChart(mockGraphData, 'area-chart'); + timeSeriesChart.vm.$nextTick(done); }); it('renders chart title', () => { @@ -83,18 +96,24 @@ describe('Time series component', () => { describe('methods', () => { describe('formatTooltipText', () => { - const mockDate = deploymentData[0].created_at; - const mockCommitUrl = deploymentData[0].commitUrl; - const generateSeriesData = type => ({ - seriesData: [ - { - seriesName: timeSeriesChart.vm.chartData[0].name, - componentSubType: type, - value: [mockDate, 5.55555], - dataIndex: 0, - }, - ], - value: mockDate, + let mockDate; + let mockCommitUrl; + let generateSeriesData; + + beforeEach(() => { + mockDate = deploymentData[0].created_at; + mockCommitUrl = deploymentData[0].commitUrl; + generateSeriesData = type => ({ + seriesData: [ + { + seriesName: timeSeriesChart.vm.chartData[0].name, + componentSubType: type, + value: [mockDate, 5.55555], + dataIndex: 0, + }, + ], + value: mockDate, + }); }); describe('when series is of line type', () => { @@ -104,11 +123,11 @@ describe('Time series component', () => { }); it('formats tooltip title', () => { - expect(timeSeriesChart.vm.tooltip.title).toBe('31 May 2017, 9:23PM'); + expect(timeSeriesChart.vm.tooltip.title).toBe('16 Jul 2019, 10:14AM'); }); it('formats tooltip content', () => { - const name = 'Core Usage'; + const name = 'Pod average'; const value = '5.556'; const dataIndex = 0; const seriesLabel = timeSeriesChart.find(GlChartSeriesLabel); @@ -129,13 +148,13 @@ describe('Time series component', () => { }); }); - describe('when series is of scatter type', () => { + describe('when series is of scatter type, for deployments', () => { beforeEach(() => { timeSeriesChart.vm.formatTooltipText(generateSeriesData('scatter')); }); it('formats tooltip title', () => { - expect(timeSeriesChart.vm.tooltip.title).toBe('31 May 2017, 9:23PM'); + expect(timeSeriesChart.vm.tooltip.title).toBe('16 Jul 2019, 10:14AM'); }); it('formats tooltip sha', () => { @@ -274,9 +293,9 @@ describe('Time series component', () => { describe('scatterSeries', () => { it('utilizes deployment data', () => { expect(timeSeriesChart.vm.scatterSeries.data).toEqual([ - ['2017-05-31T21:23:37.881Z', 0], - ['2017-05-30T20:08:04.629Z', 0], - ['2017-05-30T17:42:38.409Z', 0], + ['2019-07-16T10:14:25.589Z', 0], + ['2019-07-16T11:14:25.589Z', 0], + ['2019-07-16T12:14:25.589Z', 0], ]); expect(timeSeriesChart.vm.scatterSeries.symbolSize).toBe(14); @@ -285,7 +304,7 @@ describe('Time series component', () => { describe('yAxisLabel', () => { it('constructs a label for the chart y-axis', () => { - expect(timeSeriesChart.vm.yAxisLabel).toBe('CPU'); + expect(timeSeriesChart.vm.yAxisLabel).toBe('Memory Used per Pod'); }); }); }); @@ -318,6 +337,10 @@ describe('Time series component', () => { timeSeriesAreaChart.vm.$nextTick(done); }); + afterEach(() => { + timeSeriesAreaChart.destroy(); + }); + it('is a Vue instance', () => { expect(glChart.exists()).toBe(true); expect(glChart.isVueInstance()).toBe(true); @@ -343,6 +366,9 @@ describe('Time series component', () => { }); describe('when tooltip is showing deployment data', () => { + const mockSha = 'mockSha'; + const commitUrl = `${mockProjectDir}/commit/${mockSha}`; + beforeEach(done => { timeSeriesAreaChart.vm.tooltip.isDeployment = true; timeSeriesAreaChart.vm.$nextTick(done); diff --git a/spec/frontend/monitoring/mock_data.js b/spec/frontend/monitoring/mock_data.js index 39b4343d0ce..74e2d079d9b 100644 --- a/spec/frontend/monitoring/mock_data.js +++ b/spec/frontend/monitoring/mock_data.js @@ -1,3 +1,4 @@ +export const mockHost = 'http://test.host'; export const mockProjectDir = '/frontend-fixtures/environments-project'; export const anomalyDeploymentData = [ @@ -159,3 +160,171 @@ export const anomalyMockGraphData = { }, ], }; + +export const deploymentData = [ + { + id: 111, + iid: 3, + sha: 'f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187', + commitUrl: + 'http://test.host/frontend-fixtures/environments-project/commit/f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187', + ref: { + name: 'master', + }, + created_at: '2019-07-16T10:14:25.589Z', + tag: false, + tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false', + 'last?': true, + }, + { + id: 110, + iid: 2, + sha: 'f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187', + commitUrl: + 'http://test.host/frontend-fixtures/environments-project/commit/f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187', + ref: { + name: 'master', + }, + created_at: '2019-07-16T11:14:25.589Z', + tag: false, + tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false', + 'last?': false, + }, + { + id: 109, + iid: 1, + sha: '6511e58faafaa7ad2228990ec57f19d66f7db7c2', + commitUrl: + 'http://test.host/frontend-fixtures/environments-project/commit/6511e58faafaa7ad2228990ec57f19d66f7db7c2', + ref: { + name: 'update2-readme', + }, + created_at: '2019-07-16T12:14:25.589Z', + tag: false, + tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false', + 'last?': false, + }, +]; + +export const metricsNewGroupsAPIResponse = [ + { + group: 'System metrics (Kubernetes)', + priority: 5, + panels: [ + { + title: 'Memory Usage (Pod average)', + type: 'area-chart', + y_label: 'Memory Used per Pod', + weight: 2, + metrics: [ + { + id: 'system_metrics_kubernetes_container_memory_average', + query_range: + 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) / count(avg(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}) without (job)) /1024/1024', + label: 'Pod average', + unit: 'MB', + metric_id: 17, + prometheus_endpoint_path: + '/root/autodevops-deploy/environments/32/prometheus/api/v1/query_range?query=avg%28sum%28container_memory_usage_bytes%7Bcontainer_name%21%3D%22POD%22%2Cpod_name%3D~%22%5E%25%7Bci_environment_slug%7D-%28%5B%5Ec%5D.%2A%7Cc%28%5B%5Ea%5D%7Ca%28%5B%5En%5D%7Cn%28%5B%5Ea%5D%7Ca%28%5B%5Er%5D%7Cr%5B%5Ey%5D%29%29%29%29.%2A%7C%29-%28.%2A%29%22%2Cnamespace%3D%22%25%7Bkube_namespace%7D%22%7D%29+by+%28job%29%29+without+%28job%29+%2F+count%28avg%28container_memory_usage_bytes%7Bcontainer_name%21%3D%22POD%22%2Cpod_name%3D~%22%5E%25%7Bci_environment_slug%7D-%28%5B%5Ec%5D.%2A%7Cc%28%5B%5Ea%5D%7Ca%28%5B%5En%5D%7Cn%28%5B%5Ea%5D%7Ca%28%5B%5Er%5D%7Cr%5B%5Ey%5D%29%29%29%29.%2A%7C%29-%28.%2A%29%22%2Cnamespace%3D%22%25%7Bkube_namespace%7D%22%7D%29+without+%28job%29%29+%2F1024%2F1024', + appearance: { + line: { + width: 2, + }, + }, + }, + ], + }, + ], + }, +]; + +export const mockedQueryResultPayload = { + metricId: '17_system_metrics_kubernetes_container_memory_average', + result: [ + { + metric: {}, + values: [ + [1563272065.589, '10.396484375'], + [1563272125.589, '10.333984375'], + [1563272185.589, '10.333984375'], + [1563272245.589, '10.333984375'], + [1563272305.589, '10.333984375'], + [1563272365.589, '10.333984375'], + [1563272425.589, '10.38671875'], + [1563272485.589, '10.333984375'], + [1563272545.589, '10.333984375'], + [1563272605.589, '10.333984375'], + [1563272665.589, '10.333984375'], + [1563272725.589, '10.333984375'], + [1563272785.589, '10.396484375'], + [1563272845.589, '10.333984375'], + [1563272905.589, '10.333984375'], + [1563272965.589, '10.3984375'], + [1563273025.589, '10.337890625'], + [1563273085.589, '10.34765625'], + [1563273145.589, '10.337890625'], + [1563273205.589, '10.337890625'], + [1563273265.589, '10.337890625'], + [1563273325.589, '10.337890625'], + [1563273385.589, '10.337890625'], + [1563273445.589, '10.337890625'], + [1563273505.589, '10.337890625'], + [1563273565.589, '10.337890625'], + [1563273625.589, '10.337890625'], + [1563273685.589, '10.337890625'], + [1563273745.589, '10.337890625'], + [1563273805.589, '10.337890625'], + [1563273865.589, '10.390625'], + [1563273925.589, '10.390625'], + ], + }, + ], +}; + +export const metricsGroupsAPIResponse = [ + { + group: 'System metrics (Kubernetes)', + priority: 5, + panels: [ + { + title: 'Memory Usage (Pod average)', + type: 'area-chart', + y_label: 'Memory Used per Pod', + weight: 2, + metrics: [ + { + id: 'system_metrics_kubernetes_container_memory_average', + query_range: + 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) / count(avg(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}) without (job)) /1024/1024', + label: 'Pod average', + unit: 'MB', + metric_id: 17, + prometheus_endpoint_path: + '/root/autodevops-deploy/environments/32/prometheus/api/v1/query_range?query=avg%28sum%28container_memory_usage_bytes%7Bcontainer_name%21%3D%22POD%22%2Cpod_name%3D~%22%5E%25%7Bci_environment_slug%7D-%28%5B%5Ec%5D.%2A%7Cc%28%5B%5Ea%5D%7Ca%28%5B%5En%5D%7Cn%28%5B%5Ea%5D%7Ca%28%5B%5Er%5D%7Cr%5B%5Ey%5D%29%29%29%29.%2A%7C%29-%28.%2A%29%22%2Cnamespace%3D%22%25%7Bkube_namespace%7D%22%7D%29+by+%28job%29%29+without+%28job%29+%2F+count%28avg%28container_memory_usage_bytes%7Bcontainer_name%21%3D%22POD%22%2Cpod_name%3D~%22%5E%25%7Bci_environment_slug%7D-%28%5B%5Ec%5D.%2A%7Cc%28%5B%5Ea%5D%7Ca%28%5B%5En%5D%7Cn%28%5B%5Ea%5D%7Ca%28%5B%5Er%5D%7Cr%5B%5Ey%5D%29%29%29%29.%2A%7C%29-%28.%2A%29%22%2Cnamespace%3D%22%25%7Bkube_namespace%7D%22%7D%29+without+%28job%29%29+%2F1024%2F1024', + appearance: { + line: { + width: 2, + }, + }, + }, + ], + }, + { + title: 'Core Usage (Total)', + type: 'area-chart', + y_label: 'Total Cores', + weight: 3, + metrics: [ + { + id: 'system_metrics_kubernetes_container_cores_total', + query_range: + 'avg(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}[15m])) by (job)) without (job)', + label: 'Total', + unit: 'cores', + metric_id: 13, + }, + ], + }, + ], + }, +]; diff --git a/spec/frontend/monitoring/panel_type_spec.js b/spec/frontend/monitoring/panel_type_spec.js index 1cf302c25da..54a63e7f61f 100644 --- a/spec/frontend/monitoring/panel_type_spec.js +++ b/spec/frontend/monitoring/panel_type_spec.js @@ -1,5 +1,6 @@ import { shallowMount } from '@vue/test-utils'; import AxiosMockAdapter from 'axios-mock-adapter'; +import { setTestTimeout } from 'helpers/timeout'; import axios from '~/lib/utils/axios_utils'; import PanelType from '~/monitoring/components/panel_type.vue'; import EmptyChart from '~/monitoring/components/charts/empty_chart.vue'; @@ -10,15 +11,17 @@ import { anomalyMockGraphData } from '../../frontend/monitoring/mock_data'; import { createStore } from '~/monitoring/stores'; global.IS_EE = true; -global.URL.createObjectURL = jest.fn(() => {}); +global.URL.createObjectURL = jest.fn(); describe('Panel Type component', () => { let axiosMock; let store; let panelType; const dashboardWidth = 100; + const exampleText = 'example_text'; beforeEach(() => { + setTestTimeout(1000); axiosMock = new AxiosMockAdapter(axios); }); @@ -40,6 +43,7 @@ describe('Panel Type component', () => { graphData: graphDataNoResult, }, sync: false, + attachToDocument: true, }); }); @@ -65,14 +69,13 @@ describe('Panel Type component', () => { }); describe('when Graph data is available', () => { - const exampleText = 'example_text'; const propsData = { clipboardText: exampleText, dashboardWidth, graphData: graphDataPrometheusQueryRange, }; - beforeEach(() => { + beforeEach(done => { store = createStore(); panelType = shallowMount(PanelType, { propsData, @@ -80,6 +83,11 @@ describe('Panel Type component', () => { sync: false, attachToDocument: true, }); + panelType.vm.$nextTick(done); + }); + + afterEach(() => { + panelType.destroy(); }); describe('Time Series Chart panel type', () => { @@ -110,4 +118,49 @@ describe('Panel Type component', () => { }); }); }); + + describe('when downloading metrics data as CSV', () => { + beforeEach(done => { + graphDataPrometheusQueryRange.y_label = 'metric'; + store = createStore(); + panelType = shallowMount(PanelType, { + propsData: { + clipboardText: exampleText, + dashboardWidth, + graphData: graphDataPrometheusQueryRange, + }, + store, + sync: false, + attachToDocument: true, + }); + panelType.vm.$nextTick(done); + }); + + afterEach(() => { + panelType.destroy(); + }); + + describe('csvText', () => { + it('converts metrics data from json to csv', () => { + const header = `timestamp,${graphDataPrometheusQueryRange.y_label}`; + const data = graphDataPrometheusQueryRange.queries[0].result[0].values; + const firstRow = `${data[0][0]},${data[0][1]}`; + const secondRow = `${data[1][0]},${data[1][1]}`; + + expect(panelType.vm.csvText).toBe(`${header}\r\n${firstRow}\r\n${secondRow}\r\n`); + }); + }); + + describe('downloadCsv', () => { + it('produces a link with a Blob', () => { + expect(global.URL.createObjectURL).toHaveBeenLastCalledWith(expect.any(Blob)); + expect(global.URL.createObjectURL).toHaveBeenLastCalledWith( + expect.objectContaining({ + size: panelType.vm.csvText.length, + type: 'text/plain', + }), + ); + }); + }); + }); }); diff --git a/spec/javascripts/monitoring/components/dashboard_spec.js b/spec/javascripts/monitoring/components/dashboard_spec.js index 35a7d8e2f2d..674db7c7afb 100644 --- a/spec/javascripts/monitoring/components/dashboard_spec.js +++ b/spec/javascripts/monitoring/components/dashboard_spec.js @@ -7,11 +7,12 @@ import Dashboard from '~/monitoring/components/dashboard.vue'; import * as types from '~/monitoring/stores/mutation_types'; import { createStore } from '~/monitoring/stores'; import axios from '~/lib/utils/axios_utils'; -import MonitoringMock, { +import { metricsGroupsAPIResponse, + mockedQueryResultPayload, + mockedQueryResultPayloadCoresTotal, mockApiEndpoint, environmentData, - singleGroupResponse, dashboardGitResponse, } from '../mock_data'; @@ -44,12 +45,33 @@ const resetSpy = spy => { export default propsData; +function setupComponentStore(component) { + component.$store.commit( + `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, + metricsGroupsAPIResponse, + ); + + // Load 2 panels to the dashboard + component.$store.commit( + `monitoringDashboard/${types.SET_QUERY_RESULT}`, + mockedQueryResultPayload, + ); + component.$store.commit( + `monitoringDashboard/${types.SET_QUERY_RESULT}`, + mockedQueryResultPayloadCoresTotal, + ); + + component.$store.commit( + `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, + environmentData, + ); +} + describe('Dashboard', () => { let DashboardComponent; let mock; let store; let component; - let mockGraphData; beforeEach(() => { setFixtures(` @@ -123,25 +145,6 @@ describe('Dashboard', () => { }); }); - it('hides the legend when showLegend is false', done => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { - ...propsData, - hasMetrics: true, - showLegend: false, - }, - store, - }); - - setTimeout(() => { - expect(component.showEmptyState).toEqual(false); - expect(component.$el.querySelector('.legend-group')).toEqual(null); - expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy(); - done(); - }); - }); - it('hides the group panels when showPanels is false', done => { component = new DashboardComponent({ el: document.querySelector('.prometheus-graphs'), @@ -153,69 +156,85 @@ describe('Dashboard', () => { store, }); - setTimeout(() => { - expect(component.showEmptyState).toEqual(false); - expect(component.$el.querySelector('.prometheus-panel')).toEqual(null); - expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy(); - done(); - }); - }); - - it('renders the environments dropdown with a number of environments', done => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { - ...propsData, - hasMetrics: true, - showPanels: false, - }, - store, - }); - - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, - environmentData, - ); - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, - singleGroupResponse, - ); + setupComponentStore(component); Vue.nextTick() .then(() => { - const dropdownMenuEnvironments = component.$el.querySelectorAll( - '.js-environments-dropdown .dropdown-item', - ); - - expect(component.environments.length).toEqual(environmentData.length); - expect(dropdownMenuEnvironments.length).toEqual(component.environments.length); - - Array.from(dropdownMenuEnvironments).forEach((value, index) => { - if (environmentData[index].metrics_path) { - expect(value).toHaveAttr('href', environmentData[index].metrics_path); - } - }); + expect(component.showEmptyState).toEqual(false); + expect(component.$el.querySelector('.prometheus-panel')).toEqual(null); + expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy(); done(); }) .catch(done.fail); }); + describe('when all the requests have been commited by the store', () => { + beforeEach(() => { + component = new DashboardComponent({ + el: document.querySelector('.prometheus-graphs'), + propsData: { + ...propsData, + hasMetrics: true, + }, + store, + }); + + setupComponentStore(component); + }); + + it('renders the environments dropdown with a number of environments', done => { + Vue.nextTick() + .then(() => { + const dropdownMenuEnvironments = component.$el.querySelectorAll( + '.js-environments-dropdown .dropdown-item', + ); + + expect(component.environments.length).toEqual(environmentData.length); + expect(dropdownMenuEnvironments.length).toEqual(component.environments.length); + + Array.from(dropdownMenuEnvironments).forEach((value, index) => { + if (environmentData[index].metrics_path) { + expect(value).toHaveAttr('href', environmentData[index].metrics_path); + } + }); + + done(); + }) + .catch(done.fail); + }); + + it('renders the environments dropdown with a single active element', done => { + Vue.nextTick() + .then(() => { + const dropdownItems = component.$el.querySelectorAll( + '.js-environments-dropdown .dropdown-item.active', + ); + + expect(dropdownItems.length).toEqual(1); + done(); + }) + .catch(done.fail); + }); + }); + it('hides the environments dropdown list when there is no environments', done => { component = new DashboardComponent({ el: document.querySelector('.prometheus-graphs'), propsData: { ...propsData, hasMetrics: true, - showPanels: false, }, store, }); - component.$store.commit(`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, []); component.$store.commit( `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, - singleGroupResponse, + metricsGroupsAPIResponse, + ); + component.$store.commit( + `monitoringDashboard/${types.SET_QUERY_RESULT}`, + mockedQueryResultPayload, ); Vue.nextTick() @@ -230,58 +249,6 @@ describe('Dashboard', () => { .catch(done.fail); }); - it('renders the environments dropdown with a single active element', done => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { - ...propsData, - hasMetrics: true, - showPanels: false, - }, - store, - }); - - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, - environmentData, - ); - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, - singleGroupResponse, - ); - - Vue.nextTick() - .then(() => { - const dropdownItems = component.$el.querySelectorAll( - '.js-environments-dropdown .dropdown-item.active', - ); - - expect(dropdownItems.length).toEqual(1); - done(); - }) - .catch(done.fail); - }); - - it('hides the dropdown', done => { - component = new DashboardComponent({ - el: document.querySelector('.prometheus-graphs'), - propsData: { - ...propsData, - hasMetrics: true, - showPanels: false, - environmentsEndpoint: '', - }, - store, - }); - - Vue.nextTick(() => { - const dropdownIsActiveElement = component.$el.querySelectorAll('.environments'); - - expect(dropdownIsActiveElement.length).toEqual(0); - done(); - }); - }); - it('renders the datetimepicker dropdown', done => { component = new DashboardComponent({ el: document.querySelector('.prometheus-graphs'), @@ -293,10 +260,14 @@ describe('Dashboard', () => { store, }); - setTimeout(() => { - expect(component.$el.querySelector('.js-time-window-dropdown')).not.toBeNull(); - done(); - }); + setupComponentStore(component); + + Vue.nextTick() + .then(() => { + expect(component.$el.querySelector('.js-time-window-dropdown')).not.toBeNull(); + done(); + }) + .catch(done.fail); }); it('fetches the metrics data with proper time window', done => { @@ -347,14 +318,21 @@ describe('Dashboard', () => { el: document.querySelector('.prometheus-graphs'), propsData: { ...propsData, hasMetrics: true }, store, + sync: false, }); - setTimeout(() => { - const selectedTimeWindow = component.$el.querySelector('.js-time-window-dropdown .active'); + setupComponentStore(component); - expect(selectedTimeWindow.textContent.trim()).toEqual('30 minutes'); - done(); - }); + Vue.nextTick() + .then(() => { + const selectedTimeWindow = component.$el.querySelector( + '.js-time-window-dropdown .active', + ); + + expect(selectedTimeWindow.textContent.trim()).toEqual('30 minutes'); + done(); + }) + .catch(done.fail); }); it('shows an error message if invalid url parameters are passed', done => { @@ -381,29 +359,36 @@ describe('Dashboard', () => { describe('drag and drop function', () => { let wrapper; let expectedPanelCount; // also called metrics, naming to be improved: https://gitlab.com/gitlab-org/gitlab/issues/31565 + const findDraggables = () => wrapper.findAll(VueDraggable); const findEnabledDraggables = () => findDraggables().filter(f => !f.attributes('disabled')); const findDraggablePanels = () => wrapper.findAll('.js-draggable-panel'); const findRearrangeButton = () => wrapper.find('.js-rearrange-button'); - beforeEach(done => { + beforeEach(() => { mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse); - expectedPanelCount = metricsGroupsAPIResponse.data.reduce( - (acc, d) => d.metrics.length + acc, + expectedPanelCount = metricsGroupsAPIResponse.reduce( + (acc, group) => group.panels.length + acc, 0, ); - store.dispatch('monitoringDashboard/setFeatureFlags', { additionalPanelTypesEnabled: true }); + }); + beforeEach(done => { wrapper = shallowMount(DashboardComponent, { localVue, sync: false, propsData: { ...propsData, hasMetrics: true }, store, + attachToDocument: true, }); - // not using $nextTicket becuase we must wait for the dashboard - // to be populated with the mock data results. - setTimeout(done); + setupComponentStore(wrapper.vm); + + wrapper.vm.$nextTick(done); + }); + + afterEach(() => { + wrapper.destroy(); }); it('wraps vuedraggable', () => { @@ -444,7 +429,7 @@ describe('Dashboard', () => { it('metrics can be swapped', done => { const firstDraggable = findDraggables().at(0); - const mockMetrics = [...metricsGroupsAPIResponse.data[0].metrics]; + const mockMetrics = [...metricsGroupsAPIResponse[0].panels]; const value = () => firstDraggable.props('value'); expect(value().length).toBe(mockMetrics.length); @@ -486,10 +471,6 @@ describe('Dashboard', () => { }); }); }); - - afterEach(() => { - wrapper.destroy(); - }); }); // https://gitlab.com/gitlab-org/gitlab-ce/issues/66922 @@ -577,24 +558,7 @@ describe('Dashboard', () => { store, }); - component.$store.dispatch('monitoringDashboard/setFeatureFlags', { - prometheusEndpoint: false, - }); - - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, - environmentData, - ); - - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, - singleGroupResponse, - ); - - component.$store.commit( - `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, - dashboardGitResponse, - ); + setupComponentStore(component); return Vue.nextTick().then(() => { promPanel = component.$el.querySelector('.prometheus-panel'); @@ -707,20 +671,6 @@ describe('Dashboard', () => { store, }); - component.$store.dispatch('monitoringDashboard/setFeatureFlags', { - prometheusEndpoint: false, - }); - - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, - environmentData, - ); - - component.$store.commit( - `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, - singleGroupResponse, - ); - component.$store.commit( `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, dashboardGitResponse, @@ -736,38 +686,4 @@ describe('Dashboard', () => { }); }); }); - - describe('when downloading metrics data as CSV', () => { - beforeEach(() => { - component = new DashboardComponent({ - propsData: { - ...propsData, - }, - store, - }); - store.commit( - `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, - MonitoringMock.data, - ); - [ - mockGraphData, - ] = component.$store.state.monitoringDashboard.dashboard.panel_groups[0].metrics; - }); - - describe('csvText', () => { - it('converts metrics data from json to csv', () => { - const header = `timestamp,${mockGraphData.y_label}`; - const data = mockGraphData.queries[0].result[0].values; - const firstRow = `${data[0][0]},${data[0][1]}`; - - expect(component.csvText(mockGraphData)).toMatch(`^${header}\r\n${firstRow}`); - }); - }); - - describe('downloadCsv', () => { - it('produces a link with a Blob', () => { - expect(component.downloadCsv(mockGraphData)).toContain(`blob:`); - }); - }); - }); }); diff --git a/spec/javascripts/monitoring/mock_data.js b/spec/javascripts/monitoring/mock_data.js index 9e20eb34ee6..b1cd27f49be 100644 --- a/spec/javascripts/monitoring/mock_data.js +++ b/spec/javascripts/monitoring/mock_data.js @@ -1,823 +1,106 @@ -import { anomalyMockGraphData as importedAnomalyMockGraphData } from '../../frontend/monitoring/mock_data'; +import { + anomalyMockGraphData as importedAnomalyMockGraphData, + deploymentData as importedDeploymentData, + metricsNewGroupsAPIResponse as importedMetricsNewGroupsAPIResponse, + metricsGroupsAPIResponse as importedMetricsGroupsAPIResponse, +} from '../../frontend/monitoring/mock_data'; +// TODO Check if these exports are still needed export const anomalyMockGraphData = importedAnomalyMockGraphData; +export const deploymentData = importedDeploymentData; +export const metricsNewGroupsAPIResponse = importedMetricsNewGroupsAPIResponse; +export const metricsGroupsAPIResponse = importedMetricsGroupsAPIResponse; export const mockApiEndpoint = `${gl.TEST_HOST}/monitoring/mock`; export const mockProjectPath = '/frontend-fixtures/environments-project'; -export const metricsGroupsAPIResponse = { - success: true, - data: [ +export const mockedQueryResultPayload = { + metricId: '17_system_metrics_kubernetes_container_memory_average', + result: [ { - group: 'Kubernetes', - priority: 1, - metrics: [ - { - id: 5, - title: 'Memory usage', - weight: 1, - queries: [ - { - query_range: 'avg(container_memory_usage_bytes{%{environment_filter}}) / 2^20', - label: 'Memory', - unit: 'MiB', - result: [ - { - metric: {}, - values: [ - [1495700554.925, '8.0390625'], - [1495700614.925, '8.0390625'], - [1495700674.925, '8.0390625'], - [1495700734.925, '8.0390625'], - [1495700794.925, '8.0390625'], - [1495700854.925, '8.0390625'], - [1495700914.925, '8.0390625'], - [1495700974.925, '8.0390625'], - [1495701034.925, '8.0390625'], - [1495701094.925, '8.0390625'], - [1495701154.925, '8.0390625'], - [1495701214.925, '8.0390625'], - [1495701274.925, '8.0390625'], - [1495701334.925, '8.0390625'], - [1495701394.925, '8.0390625'], - [1495701454.925, '8.0390625'], - [1495701514.925, '8.0390625'], - [1495701574.925, '8.0390625'], - [1495701634.925, '8.0390625'], - [1495701694.925, '8.0390625'], - [1495701754.925, '8.0390625'], - [1495701814.925, '8.0390625'], - [1495701874.925, '8.0390625'], - [1495701934.925, '8.0390625'], - [1495701994.925, '8.0390625'], - [1495702054.925, '8.0390625'], - [1495702114.925, '8.0390625'], - [1495702174.925, '8.0390625'], - [1495702234.925, '8.0390625'], - [1495702294.925, '8.0390625'], - [1495702354.925, '8.0390625'], - [1495702414.925, '8.0390625'], - [1495702474.925, '8.0390625'], - [1495702534.925, '8.0390625'], - [1495702594.925, '8.0390625'], - [1495702654.925, '8.0390625'], - [1495702714.925, '8.0390625'], - [1495702774.925, '8.0390625'], - [1495702834.925, '8.0390625'], - [1495702894.925, '8.0390625'], - [1495702954.925, '8.0390625'], - [1495703014.925, '8.0390625'], - [1495703074.925, '8.0390625'], - [1495703134.925, '8.0390625'], - [1495703194.925, '8.0390625'], - [1495703254.925, '8.03515625'], - [1495703314.925, '8.03515625'], - [1495703374.925, '8.03515625'], - [1495703434.925, '8.03515625'], - [1495703494.925, '8.03515625'], - [1495703554.925, '8.03515625'], - [1495703614.925, '8.03515625'], - [1495703674.925, '8.03515625'], - [1495703734.925, '8.03515625'], - [1495703794.925, '8.03515625'], - [1495703854.925, '8.03515625'], - [1495703914.925, '8.03515625'], - [1495703974.925, '8.03515625'], - [1495704034.925, '8.03515625'], - [1495704094.925, '8.03515625'], - [1495704154.925, '8.03515625'], - [1495704214.925, '7.9296875'], - [1495704274.925, '7.9296875'], - [1495704334.925, '7.9296875'], - [1495704394.925, '7.9296875'], - [1495704454.925, '7.9296875'], - [1495704514.925, '7.9296875'], - [1495704574.925, '7.9296875'], - [1495704634.925, '7.9296875'], - [1495704694.925, '7.9296875'], - [1495704754.925, '7.9296875'], - [1495704814.925, '7.9296875'], - [1495704874.925, '7.9296875'], - [1495704934.925, '7.9296875'], - [1495704994.925, '7.9296875'], - [1495705054.925, '7.9296875'], - [1495705114.925, '7.9296875'], - [1495705174.925, '7.9296875'], - [1495705234.925, '7.9296875'], - [1495705294.925, '7.9296875'], - [1495705354.925, '7.9296875'], - [1495705414.925, '7.9296875'], - [1495705474.925, '7.9296875'], - [1495705534.925, '7.9296875'], - [1495705594.925, '7.9296875'], - [1495705654.925, '7.9296875'], - [1495705714.925, '7.9296875'], - [1495705774.925, '7.9296875'], - [1495705834.925, '7.9296875'], - [1495705894.925, '7.9296875'], - [1495705954.925, '7.9296875'], - [1495706014.925, '7.9296875'], - [1495706074.925, '7.9296875'], - [1495706134.925, '7.9296875'], - [1495706194.925, '7.9296875'], - [1495706254.925, '7.9296875'], - [1495706314.925, '7.9296875'], - [1495706374.925, '7.9296875'], - [1495706434.925, '7.9296875'], - [1495706494.925, '7.9296875'], - [1495706554.925, '7.9296875'], - [1495706614.925, '7.9296875'], - [1495706674.925, '7.9296875'], - [1495706734.925, '7.9296875'], - [1495706794.925, '7.9296875'], - [1495706854.925, '7.9296875'], - [1495706914.925, '7.9296875'], - [1495706974.925, '7.9296875'], - [1495707034.925, '7.9296875'], - [1495707094.925, '7.9296875'], - [1495707154.925, '7.9296875'], - [1495707214.925, '7.9296875'], - [1495707274.925, '7.9296875'], - [1495707334.925, '7.9296875'], - [1495707394.925, '7.9296875'], - [1495707454.925, '7.9296875'], - [1495707514.925, '7.9296875'], - [1495707574.925, '7.9296875'], - [1495707634.925, '7.9296875'], - [1495707694.925, '7.9296875'], - [1495707754.925, '7.9296875'], - [1495707814.925, '7.9296875'], - [1495707874.925, '7.9296875'], - [1495707934.925, '7.9296875'], - [1495707994.925, '7.9296875'], - [1495708054.925, '7.9296875'], - [1495708114.925, '7.9296875'], - [1495708174.925, '7.9296875'], - [1495708234.925, '7.9296875'], - [1495708294.925, '7.9296875'], - [1495708354.925, '7.9296875'], - [1495708414.925, '7.9296875'], - [1495708474.925, '7.9296875'], - [1495708534.925, '7.9296875'], - [1495708594.925, '7.9296875'], - [1495708654.925, '7.9296875'], - [1495708714.925, '7.9296875'], - [1495708774.925, '7.9296875'], - [1495708834.925, '7.9296875'], - [1495708894.925, '7.9296875'], - [1495708954.925, '7.8984375'], - [1495709014.925, '7.8984375'], - [1495709074.925, '7.8984375'], - [1495709134.925, '7.8984375'], - [1495709194.925, '7.8984375'], - [1495709254.925, '7.89453125'], - [1495709314.925, '7.89453125'], - [1495709374.925, '7.89453125'], - [1495709434.925, '7.89453125'], - [1495709494.925, '7.89453125'], - [1495709554.925, '7.89453125'], - [1495709614.925, '7.89453125'], - [1495709674.925, '7.89453125'], - [1495709734.925, '7.89453125'], - [1495709794.925, '7.89453125'], - [1495709854.925, '7.89453125'], - [1495709914.925, '7.89453125'], - [1495709974.925, '7.89453125'], - [1495710034.925, '7.89453125'], - [1495710094.925, '7.89453125'], - [1495710154.925, '7.89453125'], - [1495710214.925, '7.89453125'], - [1495710274.925, '7.89453125'], - [1495710334.925, '7.89453125'], - [1495710394.925, '7.89453125'], - [1495710454.925, '7.89453125'], - [1495710514.925, '7.89453125'], - [1495710574.925, '7.89453125'], - [1495710634.925, '7.89453125'], - [1495710694.925, '7.89453125'], - [1495710754.925, '7.89453125'], - [1495710814.925, '7.89453125'], - [1495710874.925, '7.89453125'], - [1495710934.925, '7.89453125'], - [1495710994.925, '7.89453125'], - [1495711054.925, '7.89453125'], - [1495711114.925, '7.89453125'], - [1495711174.925, '7.8515625'], - [1495711234.925, '7.8515625'], - [1495711294.925, '7.8515625'], - [1495711354.925, '7.8515625'], - [1495711414.925, '7.8515625'], - [1495711474.925, '7.8515625'], - [1495711534.925, '7.8515625'], - [1495711594.925, '7.8515625'], - [1495711654.925, '7.8515625'], - [1495711714.925, '7.8515625'], - [1495711774.925, '7.8515625'], - [1495711834.925, '7.8515625'], - [1495711894.925, '7.8515625'], - [1495711954.925, '7.8515625'], - [1495712014.925, '7.8515625'], - [1495712074.925, '7.8515625'], - [1495712134.925, '7.8515625'], - [1495712194.925, '7.8515625'], - [1495712254.925, '7.8515625'], - [1495712314.925, '7.8515625'], - [1495712374.925, '7.8515625'], - [1495712434.925, '7.83203125'], - [1495712494.925, '7.83203125'], - [1495712554.925, '7.83203125'], - [1495712614.925, '7.83203125'], - [1495712674.925, '7.83203125'], - [1495712734.925, '7.83203125'], - [1495712794.925, '7.83203125'], - [1495712854.925, '7.83203125'], - [1495712914.925, '7.83203125'], - [1495712974.925, '7.83203125'], - [1495713034.925, '7.83203125'], - [1495713094.925, '7.83203125'], - [1495713154.925, '7.83203125'], - [1495713214.925, '7.83203125'], - [1495713274.925, '7.83203125'], - [1495713334.925, '7.83203125'], - [1495713394.925, '7.8125'], - [1495713454.925, '7.8125'], - [1495713514.925, '7.8125'], - [1495713574.925, '7.8125'], - [1495713634.925, '7.8125'], - [1495713694.925, '7.8125'], - [1495713754.925, '7.8125'], - [1495713814.925, '7.8125'], - [1495713874.925, '7.8125'], - [1495713934.925, '7.8125'], - [1495713994.925, '7.8125'], - [1495714054.925, '7.8125'], - [1495714114.925, '7.8125'], - [1495714174.925, '7.8125'], - [1495714234.925, '7.8125'], - [1495714294.925, '7.8125'], - [1495714354.925, '7.80859375'], - [1495714414.925, '7.80859375'], - [1495714474.925, '7.80859375'], - [1495714534.925, '7.80859375'], - [1495714594.925, '7.80859375'], - [1495714654.925, '7.80859375'], - [1495714714.925, '7.80859375'], - [1495714774.925, '7.80859375'], - [1495714834.925, '7.80859375'], - [1495714894.925, '7.80859375'], - [1495714954.925, '7.80859375'], - [1495715014.925, '7.80859375'], - [1495715074.925, '7.80859375'], - [1495715134.925, '7.80859375'], - [1495715194.925, '7.80859375'], - [1495715254.925, '7.80859375'], - [1495715314.925, '7.80859375'], - [1495715374.925, '7.80859375'], - [1495715434.925, '7.80859375'], - [1495715494.925, '7.80859375'], - [1495715554.925, '7.80859375'], - [1495715614.925, '7.80859375'], - [1495715674.925, '7.80859375'], - [1495715734.925, '7.80859375'], - [1495715794.925, '7.80859375'], - [1495715854.925, '7.80859375'], - [1495715914.925, '7.80078125'], - [1495715974.925, '7.80078125'], - [1495716034.925, '7.80078125'], - [1495716094.925, '7.80078125'], - [1495716154.925, '7.80078125'], - [1495716214.925, '7.796875'], - [1495716274.925, '7.796875'], - [1495716334.925, '7.796875'], - [1495716394.925, '7.796875'], - [1495716454.925, '7.796875'], - [1495716514.925, '7.796875'], - [1495716574.925, '7.796875'], - [1495716634.925, '7.796875'], - [1495716694.925, '7.796875'], - [1495716754.925, '7.796875'], - [1495716814.925, '7.796875'], - [1495716874.925, '7.79296875'], - [1495716934.925, '7.79296875'], - [1495716994.925, '7.79296875'], - [1495717054.925, '7.79296875'], - [1495717114.925, '7.79296875'], - [1495717174.925, '7.7890625'], - [1495717234.925, '7.7890625'], - [1495717294.925, '7.7890625'], - [1495717354.925, '7.7890625'], - [1495717414.925, '7.7890625'], - [1495717474.925, '7.7890625'], - [1495717534.925, '7.7890625'], - [1495717594.925, '7.7890625'], - [1495717654.925, '7.7890625'], - [1495717714.925, '7.7890625'], - [1495717774.925, '7.7890625'], - [1495717834.925, '7.77734375'], - [1495717894.925, '7.77734375'], - [1495717954.925, '7.77734375'], - [1495718014.925, '7.77734375'], - [1495718074.925, '7.77734375'], - [1495718134.925, '7.7421875'], - [1495718194.925, '7.7421875'], - [1495718254.925, '7.7421875'], - [1495718314.925, '7.7421875'], - ], - }, - ], - }, - ], - }, - { - id: 6, - title: 'CPU usage', - y_label: 'CPU', - weight: 1, - queries: [ - { - appearance: { - line: { - width: 2, - }, - }, - query_range: - 'avg(rate(container_cpu_usage_seconds_total{%{environment_filter}}[2m])) * 100', - label: 'Core Usage', - unit: 'Cores', - result: [ - { - metric: {}, - values: [ - [1495700554.925, '0.0010794445585559514'], - [1495700614.925, '0.003927214935433527'], - [1495700674.925, '0.0053045219047619975'], - [1495700734.925, '0.0048892095238097155'], - [1495700794.925, '0.005827140952381137'], - [1495700854.925, '0.00569846906219937'], - [1495700914.925, '0.004972616802849382'], - [1495700974.925, '0.005117509523809902'], - [1495701034.925, '0.00512389061919564'], - [1495701094.925, '0.005199100501890691'], - [1495701154.925, '0.005415746394885837'], - [1495701214.925, '0.005607682788146286'], - [1495701274.925, '0.005641300000000118'], - [1495701334.925, '0.0071166279368766495'], - [1495701394.925, '0.0063242138095234044'], - [1495701454.925, '0.005793314698235304'], - [1495701514.925, '0.00703934942237556'], - [1495701574.925, '0.006357007076123191'], - [1495701634.925, '0.003753167300126738'], - [1495701694.925, '0.005018469678430698'], - [1495701754.925, '0.0045217153371887'], - [1495701814.925, '0.006140104285714119'], - [1495701874.925, '0.004818684285714102'], - [1495701934.925, '0.005079509718955242'], - [1495701994.925, '0.005059981142498263'], - [1495702054.925, '0.005269098389538773'], - [1495702114.925, '0.005269954285714175'], - [1495702174.925, '0.014199241435795856'], - [1495702234.925, '0.01511936843111017'], - [1495702294.925, '0.0060933692920682875'], - [1495702354.925, '0.004945682380952493'], - [1495702414.925, '0.005641266666666565'], - [1495702474.925, '0.005223752857142996'], - [1495702534.925, '0.005743098505699831'], - [1495702594.925, '0.00538493380952391'], - [1495702654.925, '0.005507793883751339'], - [1495702714.925, '0.005666705714285466'], - [1495702774.925, '0.006231530000000112'], - [1495702834.925, '0.006570768635394899'], - [1495702894.925, '0.005551146666666895'], - [1495702954.925, '0.005602604737098058'], - [1495703014.925, '0.00613993580402159'], - [1495703074.925, '0.004770258764368832'], - [1495703134.925, '0.005512376671364914'], - [1495703194.925, '0.005254436666666674'], - [1495703254.925, '0.0050109839141320505'], - [1495703314.925, '0.0049478019256960016'], - [1495703374.925, '0.0037666860965123463'], - [1495703434.925, '0.004813526061656314'], - [1495703494.925, '0.005047748095238278'], - [1495703554.925, '0.00386494081008772'], - [1495703614.925, '0.004304037408111405'], - [1495703674.925, '0.004999466661587168'], - [1495703734.925, '0.004689140476190834'], - [1495703794.925, '0.004746126153582475'], - [1495703854.925, '0.004482706382572302'], - [1495703914.925, '0.004032808931864524'], - [1495703974.925, '0.005728319047618988'], - [1495704034.925, '0.004436139179627006'], - [1495704094.925, '0.004553455714285617'], - [1495704154.925, '0.003455244285714341'], - [1495704214.925, '0.004742244761904621'], - [1495704274.925, '0.005366978571428422'], - [1495704334.925, '0.004257954837665058'], - [1495704394.925, '0.005431603259831257'], - [1495704454.925, '0.0052009214498621986'], - [1495704514.925, '0.004317201904761618'], - [1495704574.925, '0.004307384285714157'], - [1495704634.925, '0.004789801146644822'], - [1495704694.925, '0.0051429795906706485'], - [1495704754.925, '0.005322495714285479'], - [1495704814.925, '0.004512809333244233'], - [1495704874.925, '0.004953843582568726'], - [1495704934.925, '0.005812690120858119'], - [1495704994.925, '0.004997024285714838'], - [1495705054.925, '0.005246216154439592'], - [1495705114.925, '0.0063494966618726795'], - [1495705174.925, '0.005306004342898225'], - [1495705234.925, '0.005081412857142978'], - [1495705294.925, '0.00511409523809522'], - [1495705354.925, '0.0047861001481192'], - [1495705414.925, '0.005107688228042962'], - [1495705474.925, '0.005271929582294012'], - [1495705534.925, '0.004453254502681249'], - [1495705594.925, '0.005799134293959226'], - [1495705654.925, '0.005340865929502478'], - [1495705714.925, '0.004911654761904942'], - [1495705774.925, '0.005888234873953261'], - [1495705834.925, '0.005565283333332954'], - [1495705894.925, '0.005522869047618869'], - [1495705954.925, '0.005177549737621646'], - [1495706014.925, '0.0053145810232096465'], - [1495706074.925, '0.004751095238095275'], - [1495706134.925, '0.006242077142856976'], - [1495706194.925, '0.00621034406957871'], - [1495706254.925, '0.006887592738978596'], - [1495706314.925, '0.006328128779726213'], - [1495706374.925, '0.007488363809523927'], - [1495706434.925, '0.006193758571428157'], - [1495706494.925, '0.0068798371839706935'], - [1495706554.925, '0.005757034340423128'], - [1495706614.925, '0.004571388497294698'], - [1495706674.925, '0.00620283044923395'], - [1495706734.925, '0.005607562380952455'], - [1495706794.925, '0.005506969933620308'], - [1495706854.925, '0.005621118095238131'], - [1495706914.925, '0.004876606098698849'], - [1495706974.925, '0.0047871205988517206'], - [1495707034.925, '0.00526405939458784'], - [1495707094.925, '0.005716323800605852'], - [1495707154.925, '0.005301459523809575'], - [1495707214.925, '0.0051613042857144905'], - [1495707274.925, '0.005384792857142714'], - [1495707334.925, '0.005259719047619222'], - [1495707394.925, '0.00584101142857182'], - [1495707454.925, '0.0060066121920326326'], - [1495707514.925, '0.006359978571428453'], - [1495707574.925, '0.006315876322151109'], - [1495707634.925, '0.005590012517198831'], - [1495707694.925, '0.005517419877137072'], - [1495707754.925, '0.006089813430348506'], - [1495707814.925, '0.00466754476190479'], - [1495707874.925, '0.006059954380517721'], - [1495707934.925, '0.005085657142856972'], - [1495707994.925, '0.005897665238095296'], - [1495708054.925, '0.0062282023199555885'], - [1495708114.925, '0.00526214553236979'], - [1495708174.925, '0.0044803300000000644'], - [1495708234.925, '0.005421443333333592'], - [1495708294.925, '0.005694326244512144'], - [1495708354.925, '0.005527721904761457'], - [1495708414.925, '0.005988819523809819'], - [1495708474.925, '0.005484704285714448'], - [1495708534.925, '0.005041123649230085'], - [1495708594.925, '0.005717767639612059'], - [1495708654.925, '0.005412954417342863'], - [1495708714.925, '0.005833343333333254'], - [1495708774.925, '0.005448135238094969'], - [1495708834.925, '0.005117341428571432'], - [1495708894.925, '0.005888345825277833'], - [1495708954.925, '0.005398543809524135'], - [1495709014.925, '0.005325611428571416'], - [1495709074.925, '0.005848668571428527'], - [1495709134.925, '0.005135003105145044'], - [1495709194.925, '0.0054551400000003'], - [1495709254.925, '0.005319472937322171'], - [1495709314.925, '0.00585677857142792'], - [1495709374.925, '0.0062146261904759215'], - [1495709434.925, '0.0067105060904182265'], - [1495709494.925, '0.005829691904762108'], - [1495709554.925, '0.005719280952381261'], - [1495709614.925, '0.005682603793416407'], - [1495709674.925, '0.0055272846277326934'], - [1495709734.925, '0.0057123680952386735'], - [1495709794.925, '0.00520597958075818'], - [1495709854.925, '0.005584358957263837'], - [1495709914.925, '0.005601104275197466'], - [1495709974.925, '0.005991657142857066'], - [1495710034.925, '0.00553722238095218'], - [1495710094.925, '0.005127883122696293'], - [1495710154.925, '0.005498111927534584'], - [1495710214.925, '0.005609934069084202'], - [1495710274.925, '0.00459206285714307'], - [1495710334.925, '0.0047910828571428084'], - [1495710394.925, '0.0056014671288845685'], - [1495710454.925, '0.005686936791078528'], - [1495710514.925, '0.00444480476190448'], - [1495710574.925, '0.005780394696738921'], - [1495710634.925, '0.0053107227550210365'], - [1495710694.925, '0.005096031495761817'], - [1495710754.925, '0.005451377979091524'], - [1495710814.925, '0.005328136666667083'], - [1495710874.925, '0.006020612857143043'], - [1495710934.925, '0.0061063585714285365'], - [1495710994.925, '0.006018346015752312'], - [1495711054.925, '0.005069130952381193'], - [1495711114.925, '0.005458406190476052'], - [1495711174.925, '0.00577219190476179'], - [1495711234.925, '0.005760814645658314'], - [1495711294.925, '0.005371875716579101'], - [1495711354.925, '0.0064232666666665834'], - [1495711414.925, '0.009369806836906667'], - [1495711474.925, '0.008956864761904692'], - [1495711534.925, '0.005266849368559271'], - [1495711594.925, '0.005335111364934262'], - [1495711654.925, '0.006461778319586945'], - [1495711714.925, '0.004687939890762393'], - [1495711774.925, '0.004438831245760684'], - [1495711834.925, '0.005142786666666613'], - [1495711894.925, '0.007257734212054963'], - [1495711954.925, '0.005621991904761494'], - [1495712014.925, '0.007868689999999862'], - [1495712074.925, '0.00910970215275738'], - [1495712134.925, '0.006151004285714278'], - [1495712194.925, '0.005447120924961522'], - [1495712254.925, '0.005150705153929503'], - [1495712314.925, '0.006358108714969314'], - [1495712374.925, '0.0057725354795696475'], - [1495712434.925, '0.005232139047619015'], - [1495712494.925, '0.004932809617949037'], - [1495712554.925, '0.004511607508499662'], - [1495712614.925, '0.00440487701522666'], - [1495712674.925, '0.005479113333333174'], - [1495712734.925, '0.004726317619047547'], - [1495712794.925, '0.005582041102958029'], - [1495712854.925, '0.006381481216082099'], - [1495712914.925, '0.005474260014095208'], - [1495712974.925, '0.00567597142857188'], - [1495713034.925, '0.0064741233333332985'], - [1495713094.925, '0.005467475714285271'], - [1495713154.925, '0.004868648393824457'], - [1495713214.925, '0.005254923286444893'], - [1495713274.925, '0.005599217150312865'], - [1495713334.925, '0.005105413720618919'], - [1495713394.925, '0.007246073333333279'], - [1495713454.925, '0.005990312380952272'], - [1495713514.925, '0.005594601853351101'], - [1495713574.925, '0.004739258673727054'], - [1495713634.925, '0.003932121428571783'], - [1495713694.925, '0.005018188268459395'], - [1495713754.925, '0.004538238095237985'], - [1495713814.925, '0.00561816643265435'], - [1495713874.925, '0.0063132584495033586'], - [1495713934.925, '0.00442385238095213'], - [1495713994.925, '0.004181795887658453'], - [1495714054.925, '0.004437759047619037'], - [1495714114.925, '0.006421748157178241'], - [1495714174.925, '0.006525143809523842'], - [1495714234.925, '0.004715904935144247'], - [1495714294.925, '0.005966040152763461'], - [1495714354.925, '0.005614535466921674'], - [1495714414.925, '0.004934375119415906'], - [1495714474.925, '0.0054122933333327385'], - [1495714534.925, '0.004926540699612279'], - [1495714594.925, '0.006124649517134237'], - [1495714654.925, '0.004629427092013995'], - [1495714714.925, '0.005117951257607005'], - [1495714774.925, '0.004868774512685422'], - [1495714834.925, '0.005310093333333399'], - [1495714894.925, '0.0054907752286127345'], - [1495714954.925, '0.004597678117351089'], - [1495715014.925, '0.0059622552380952'], - [1495715074.925, '0.005352457072655368'], - [1495715134.925, '0.005491630952381143'], - [1495715194.925, '0.006391770078379791'], - [1495715254.925, '0.005933472857142518'], - [1495715314.925, '0.005301314285714163'], - [1495715374.925, '0.0058352959724814165'], - [1495715434.925, '0.006154755147867044'], - [1495715494.925, '0.009391935637482038'], - [1495715554.925, '0.007846462857142592'], - [1495715614.925, '0.00477608215316353'], - [1495715674.925, '0.006132865238094998'], - [1495715734.925, '0.006159762457649516'], - [1495715794.925, '0.005957307073265968'], - [1495715854.925, '0.006652319091792501'], - [1495715914.925, '0.005493557402895287'], - [1495715974.925, '0.0058652434829145166'], - [1495716034.925, '0.005627400430468021'], - [1495716094.925, '0.006240656190475609'], - [1495716154.925, '0.006305997676168624'], - [1495716214.925, '0.005388057732783248'], - [1495716274.925, '0.0052814916048421244'], - [1495716334.925, '0.00699498614272497'], - [1495716394.925, '0.00627768693035141'], - [1495716454.925, '0.0042411487048161145'], - [1495716514.925, '0.005348647473627653'], - [1495716574.925, '0.0047176657142853975'], - [1495716634.925, '0.004437898571428686'], - [1495716694.925, '0.004923527366927261'], - [1495716754.925, '0.005131935066048421'], - [1495716814.925, '0.005046949523809611'], - [1495716874.925, '0.00547184095238092'], - [1495716934.925, '0.005224140016380444'], - [1495716994.925, '0.005297991171665292'], - [1495717054.925, '0.005492965995623498'], - [1495717114.925, '0.005754660000000403'], - [1495717174.925, '0.005949557138639285'], - [1495717234.925, '0.006091816112534666'], - [1495717294.925, '0.005554210080192063'], - [1495717354.925, '0.006411504395279871'], - [1495717414.925, '0.006319643996609606'], - [1495717474.925, '0.005539174405717675'], - [1495717534.925, '0.0053157078842772255'], - [1495717594.925, '0.005247480952381066'], - [1495717654.925, '0.004820141620396252'], - [1495717714.925, '0.005906173868322844'], - [1495717774.925, '0.006173117219570961'], - [1495717834.925, '0.005963340952380661'], - [1495717894.925, '0.005698976627681527'], - [1495717954.925, '0.004751279096346378'], - [1495718014.925, '0.005733142379359711'], - [1495718074.925, '0.004831689010348035'], - [1495718134.925, '0.005188370476191092'], - [1495718194.925, '0.004793227554547938'], - [1495718254.925, '0.003997442857142731'], - [1495718314.925, '0.004386040132951264'], - ], - }, - ], - }, - ], - }, - ], - }, - { - group: 'NGINX', - priority: 2, - metrics: [ - { - id: 100, - title: 'Http Error Rate', - weight: 100, - queries: [ - { - query_range: - 'sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"nginx-test-8691397-production-.*"}[2m])) / sum(rate(nginx_upstream_responses_total{upstream=~"nginx-test-8691397-production-.*"}[2m])) * 100', - label: '5xx errors', - unit: '%', - result: [ - { - metric: {}, - values: [ - [1495700554.925, NaN], - [1495700614.925, NaN], - [1495700674.925, NaN], - [1495700734.925, NaN], - [1495700794.925, NaN], - [1495700854.925, NaN], - [1495700914.925, NaN], - ], - }, - ], - }, - ], - }, + metric: {}, + values: [ + [1563272065.589, '10.396484375'], + [1563272125.589, '10.333984375'], + [1563272185.589, '10.333984375'], + [1563272245.589, '10.333984375'], + [1563272305.589, '10.333984375'], + [1563272365.589, '10.333984375'], + [1563272425.589, '10.38671875'], + [1563272485.589, '10.333984375'], + [1563272545.589, '10.333984375'], + [1563272605.589, '10.333984375'], + [1563272665.589, '10.333984375'], + [1563272725.589, '10.333984375'], + [1563272785.589, '10.396484375'], + [1563272845.589, '10.333984375'], + [1563272905.589, '10.333984375'], + [1563272965.589, '10.3984375'], + [1563273025.589, '10.337890625'], + [1563273085.589, '10.34765625'], + [1563273145.589, '10.337890625'], + [1563273205.589, '10.337890625'], + [1563273265.589, '10.337890625'], + [1563273325.589, '10.337890625'], + [1563273385.589, '10.337890625'], + [1563273445.589, '10.337890625'], + [1563273505.589, '10.337890625'], + [1563273565.589, '10.337890625'], + [1563273625.589, '10.337890625'], + [1563273685.589, '10.337890625'], + [1563273745.589, '10.337890625'], + [1563273805.589, '10.337890625'], + [1563273865.589, '10.390625'], + [1563273925.589, '10.390625'], ], }, ], - last_update: '2017-05-25T13:18:34.949Z', }; -export const singleGroupResponse = [ - { - group: 'System metrics (Kubernetes)', - priority: 5, - metrics: [ - { - title: 'Memory Usage (Total)', - weight: 0, - y_label: 'Total Memory Used', - queries: [ - { - query_range: - 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^production-(.*)",namespace="autodevops-deploy-33"}) by (job)) without (job) /1024/1024/1024', - unit: 'GB', - label: 'Total', - result: [ - { - metric: {}, - values: [ - [1558453960.079, '0.0357666015625'], - [1558454020.079, '0.035675048828125'], - [1558454080.079, '0.035152435302734375'], - [1558454140.079, '0.035221099853515625'], - [1558454200.079, '0.0352325439453125'], - [1558454260.079, '0.03479766845703125'], - [1558454320.079, '0.034793853759765625'], - [1558454380.079, '0.034931182861328125'], - [1558454440.079, '0.034816741943359375'], - [1558454500.079, '0.034816741943359375'], - [1558454560.079, '0.034816741943359375'], - ], - }, - ], - }, - ], - id: 15, - }, - ], - }, -]; - -export default metricsGroupsAPIResponse; - -export const deploymentData = [ - { - id: 111, - iid: 3, - sha: 'f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187', - commitUrl: - 'http://test.host/frontend-fixtures/environments-project/commit/f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187', - ref: { - name: 'master', - }, - created_at: '2017-05-31T21:23:37.881Z', - tag: false, - tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false', - 'last?': true, - }, - { - id: 110, - iid: 2, - sha: 'f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187', - commitUrl: - 'http://test.host/frontend-fixtures/environments-project/commit/f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187', - ref: { - name: 'master', - }, - created_at: '2017-05-30T20:08:04.629Z', - tag: false, - tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false', - 'last?': false, - }, - { - id: 109, - iid: 1, - sha: '6511e58faafaa7ad2228990ec57f19d66f7db7c2', - commitUrl: - 'http://test.host/frontend-fixtures/environments-project/commit/6511e58faafaa7ad2228990ec57f19d66f7db7c2', - ref: { - name: 'update2-readme', - }, - created_at: '2017-05-30T17:42:38.409Z', - tag: false, - tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false', - 'last?': false, - }, -]; - -export const statePaths = { - settingsPath: '/root/hello-prometheus/services/prometheus/edit', - clustersPath: '/root/hello-prometheus/clusters', - documentationPath: '/help/administration/monitoring/prometheus/index.md', -}; - -export const queryWithoutData = { - title: 'HTTP Error rate', - weight: 10, - y_label: 'Http Error Rate', - queries: [ +export const mockedQueryResultPayloadCoresTotal = { + metricId: '13_system_metrics_kubernetes_container_cores_total', + result: [ { - query_range: - 'sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"nginx-test-8691397-production-.*"}[2m])) / sum(rate(nginx_upstream_responses_total{upstream=~"nginx-test-8691397-production-.*"}[2m])) * 100', - label: '5xx errors', - unit: '%', - result: [], + metric: {}, + values: [ + [1563272065.589, '9.396484375'], + [1563272125.589, '9.333984375'], + [1563272185.589, '9.333984375'], + [1563272245.589, '9.333984375'], + [1563272305.589, '9.333984375'], + [1563272365.589, '9.333984375'], + [1563272425.589, '9.38671875'], + [1563272485.589, '9.333984375'], + [1563272545.589, '9.333984375'], + [1563272605.589, '9.333984375'], + [1563272665.589, '9.333984375'], + [1563272725.589, '9.333984375'], + [1563272785.589, '9.396484375'], + [1563272845.589, '9.333984375'], + [1563272905.589, '9.333984375'], + [1563272965.589, '9.3984375'], + [1563273025.589, '9.337890625'], + [1563273085.589, '9.34765625'], + [1563273145.589, '9.337890625'], + [1563273205.589, '9.337890625'], + [1563273265.589, '9.337890625'], + [1563273325.589, '9.337890625'], + [1563273385.589, '9.337890625'], + [1563273445.589, '9.337890625'], + [1563273505.589, '9.337890625'], + [1563273565.589, '9.337890625'], + [1563273625.589, '9.337890625'], + [1563273685.589, '9.337890625'], + [1563273745.589, '9.337890625'], + [1563273805.589, '9.337890625'], + [1563273865.589, '9.390625'], + [1563273925.589, '9.390625'], + ], }, ], }; -export function convertDatesMultipleSeries(multipleSeries) { - const convertedMultiple = multipleSeries; - multipleSeries.forEach((column, index) => { - let convertedResult = []; - convertedResult = column.queries[0].result.map(resultObj => { - const convertedMetrics = {}; - convertedMetrics.values = resultObj.values.map(val => ({ - time: new Date(val.time), - value: val.value, - })); - convertedMetrics.metric = resultObj.metric; - return convertedMetrics; - }); - convertedMultiple[index].queries[0].result = convertedResult; - }); - return convertedMultiple; -} - export const environmentData = [ { id: 34, @@ -1006,7 +289,7 @@ export const graphDataPrometheusQueryRange = { ], queries: [ { - metricId: null, + metricId: '10', id: 'metric_a1', metric_id: 2, query_range: diff --git a/spec/javascripts/monitoring/store/actions_spec.js b/spec/javascripts/monitoring/store/actions_spec.js index 4d602e3b0a2..684e26641c7 100644 --- a/spec/javascripts/monitoring/store/actions_spec.js +++ b/spec/javascripts/monitoring/store/actions_spec.js @@ -317,7 +317,7 @@ describe('Monitoring store actions', () => { const metric = metricsDashboardResponse.dashboard.panel_groups[0].panels[0].metrics[0]; const state = storeState(); - const data = metricsGroupsAPIResponse.data[0].metrics[0].queries[0]; + const data = metricsGroupsAPIResponse[0].panels[0].metrics[0]; const response = { data }; mock.onGet('http://test').reply(200, response); diff --git a/spec/javascripts/monitoring/store/mutations_spec.js b/spec/javascripts/monitoring/store/mutations_spec.js index 49aed1b85e6..498c1a9e9b1 100644 --- a/spec/javascripts/monitoring/store/mutations_spec.js +++ b/spec/javascripts/monitoring/store/mutations_spec.js @@ -21,79 +21,46 @@ describe('Monitoring mutations', () => { beforeEach(() => { stateCopy.dashboard.panel_groups = []; - groups = metricsGroupsAPIResponse.data; + groups = metricsGroupsAPIResponse; }); it('adds a key to the group', () => { mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, groups); - expect(stateCopy.dashboard.panel_groups[0].key).toBe('kubernetes-0'); - expect(stateCopy.dashboard.panel_groups[1].key).toBe('nginx-1'); + expect(stateCopy.dashboard.panel_groups[0].key).toBe('system-metrics-kubernetes--0'); }); it('normalizes values', () => { mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, groups); - const expectedTimestamp = '2017-05-25T08:22:34.925Z'; - const expectedValue = 8.0390625; - const [ - timestamp, - value, - ] = stateCopy.dashboard.panel_groups[0].metrics[0].queries[0].result[0].values[0]; + const expectedLabel = 'Pod average'; + const { label, query_range } = stateCopy.dashboard.panel_groups[0].metrics[0].metrics[0]; - expect(timestamp).toEqual(expectedTimestamp); - expect(value).toEqual(expectedValue); + expect(label).toEqual(expectedLabel); + expect(query_range.length).toBeGreaterThan(0); }); - it('contains two groups that contains, one of which has two queries sorted by priority', () => { + it('contains one group, which it has two panels and one metrics property', () => { mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, groups); expect(stateCopy.dashboard.panel_groups).toBeDefined(); - expect(stateCopy.dashboard.panel_groups.length).toEqual(2); - expect(stateCopy.dashboard.panel_groups[0].metrics.length).toEqual(2); + expect(stateCopy.dashboard.panel_groups.length).toEqual(1); + expect(stateCopy.dashboard.panel_groups[0].panels.length).toEqual(2); + expect(stateCopy.dashboard.panel_groups[0].panels[0].metrics.length).toEqual(1); + expect(stateCopy.dashboard.panel_groups[0].panels[1].metrics.length).toEqual(1); }); it('assigns queries a metric id', () => { mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, groups); - expect(stateCopy.dashboard.panel_groups[1].metrics[0].queries[0].metricId).toEqual('100'); + expect(stateCopy.dashboard.panel_groups[0].metrics[0].queries[0].metricId).toEqual( + '17_system_metrics_kubernetes_container_memory_average', + ); }); - it('removes the data if all the values from a query are not defined', () => { - mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, groups); - - expect(stateCopy.dashboard.panel_groups[1].metrics[0].queries[0].result.length).toEqual(0); - }); - - it('assigns metric id of null if metric has no id', () => { - stateCopy.dashboard.panel_groups = []; - const noId = groups.map(group => ({ - ...group, - ...{ - metrics: group.metrics.map(metric => { - const { id, ...metricWithoutId } = metric; - - return metricWithoutId; - }), - }, - })); - - mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, noId); - - stateCopy.dashboard.panel_groups.forEach(group => { - group.metrics.forEach(metric => { - expect(metric.queries.every(query => query.metricId === null)).toBe(true); - }); - }); - }); - - describe('dashboard endpoint enabled', () => { + describe('dashboard endpoint', () => { const dashboardGroups = metricsDashboardResponse.dashboard.panel_groups; - beforeEach(() => { - stateCopy.useDashboardEndpoint = true; - }); - it('aliases group panels to metrics for backwards compatibility', () => { mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, dashboardGroups); @@ -143,7 +110,6 @@ describe('Monitoring mutations', () => { const result = [{ values: [[0, 1], [1, 1], [1, 3]] }]; beforeEach(() => { - stateCopy.useDashboardEndpoint = true; const dashboardGroups = metricsDashboardResponse.dashboard.panel_groups; mutations[types.RECEIVE_METRICS_DATA_SUCCESS](stateCopy, dashboardGroups); }); diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index f01d89b35e2..7a5c2a29d89 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -48,6 +48,10 @@ describe Gitlab::UsageData do create(:clusters_applications_knative, :installed, cluster: gcp_cluster) create(:clusters_applications_elastic_stack, :installed, cluster: gcp_cluster) + create(:grafana_integration, project: projects[0], enabled: true) + create(:grafana_integration, project: projects[1], enabled: true) + create(:grafana_integration, project: projects[2], enabled: false) + ProjectFeature.first.update_attribute('repository_access_level', 0) end @@ -140,6 +144,7 @@ describe Gitlab::UsageData do clusters_applications_knative clusters_applications_elastic_stack in_review_folder + grafana_integrated_projects groups issues issues_with_associated_zoom_link @@ -221,6 +226,7 @@ describe Gitlab::UsageData do expect(count_data[:clusters_applications_runner]).to eq(1) expect(count_data[:clusters_applications_knative]).to eq(1) expect(count_data[:clusters_applications_elastic_stack]).to eq(1) + expect(count_data[:grafana_integrated_projects]).to eq(2) end it 'works when queries time out' do