summaryrefslogtreecommitdiff
path: root/app/assets
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-04-07 18:09:19 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-04-07 18:09:19 +0000
commit3290d46655f07d7ae3dca788d6df9f326972ffd8 (patch)
tree0d24713e1592cdd3583257f14a52d46a22539ed1 /app/assets
parentc6b3ec3f56fa32a0e0ed3de0d0878d25f1adaddf (diff)
downloadgitlab-ce-3290d46655f07d7ae3dca788d6df9f326972ffd8.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/javascripts/monitoring/stores/actions.js186
-rw-r--r--app/assets/javascripts/monitoring/stores/mutation_types.js11
-rw-r--r--app/assets/javascripts/monitoring/stores/mutations.js6
-rw-r--r--app/assets/javascripts/notes/components/sort_discussion.vue13
-rw-r--r--app/assets/javascripts/snippets/components/snippet_blob_view.vue15
-rw-r--r--app/assets/javascripts/snippets/fragments/snippetBase.fragment.graphql4
-rw-r--r--app/assets/javascripts/vue_shared/components/local_storage_sync.vue39
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss8
8 files changed, 194 insertions, 88 deletions
diff --git a/app/assets/javascripts/monitoring/stores/actions.js b/app/assets/javascripts/monitoring/stores/actions.js
index 2e4987b7349..acc09fa6305 100644
--- a/app/assets/javascripts/monitoring/stores/actions.js
+++ b/app/assets/javascripts/monitoring/stores/actions.js
@@ -12,6 +12,20 @@ import { s__, sprintf } from '../../locale';
import { PROMETHEUS_TIMEOUT } from '../constants';
+function prometheusMetricQueryParams(timeRange) {
+ const { start, end } = convertToFixedRange(timeRange);
+
+ const timeDiff = (new Date(end) - new Date(start)) / 1000;
+ const minStep = 60;
+ const queryDataPoints = 600;
+
+ return {
+ start_time: start,
+ end_time: end,
+ step: Math.max(minStep, Math.ceil(timeDiff / queryDataPoints)),
+ };
+}
+
function backOffRequest(makeRequestCallback) {
return backOff((next, stop) => {
makeRequestCallback()
@@ -26,6 +40,20 @@ function backOffRequest(makeRequestCallback) {
}, PROMETHEUS_TIMEOUT);
}
+function getPrometheusMetricResult(prometheusEndpoint, params) {
+ return backOffRequest(() => axios.get(prometheusEndpoint, { params }))
+ .then(res => res.data)
+ .then(response => {
+ if (response.status === 'error') {
+ throw new Error(response.error);
+ }
+
+ return response.data.result;
+ });
+}
+
+// Setup
+
export const setGettingStartedEmptyState = ({ commit }) => {
commit(types.SET_GETTING_STARTED_EMPTY_STATE);
};
@@ -47,56 +75,26 @@ export const setShowErrorBanner = ({ commit }, enabled) => {
commit(types.SET_SHOW_ERROR_BANNER, enabled);
};
-export const requestMetricsDashboard = ({ commit }) => {
- commit(types.REQUEST_METRICS_DATA);
-};
-export const receiveMetricsDashboardSuccess = ({ commit, dispatch }, { response, params }) => {
- const { all_dashboards, dashboard, metrics_data } = response;
-
- commit(types.SET_ALL_DASHBOARDS, all_dashboards);
- commit(types.RECEIVE_METRICS_DATA_SUCCESS, dashboard);
- commit(types.SET_ENDPOINTS, convertObjectPropsToCamelCase(metrics_data));
-
- return dispatch('fetchPrometheusMetrics', params);
-};
-export const receiveMetricsDashboardFailure = ({ commit }, error) => {
- commit(types.RECEIVE_METRICS_DATA_FAILURE, error);
-};
-
-export const receiveDeploymentsDataSuccess = ({ commit }, data) =>
- commit(types.RECEIVE_DEPLOYMENTS_DATA_SUCCESS, data);
-export const receiveDeploymentsDataFailure = ({ commit }) =>
- commit(types.RECEIVE_DEPLOYMENTS_DATA_FAILURE);
-export const requestEnvironmentsData = ({ commit }) => commit(types.REQUEST_ENVIRONMENTS_DATA);
-export const receiveEnvironmentsDataSuccess = ({ commit }, data) =>
- commit(types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS, data);
-export const receiveEnvironmentsDataFailure = ({ commit }) =>
- commit(types.RECEIVE_ENVIRONMENTS_DATA_FAILURE);
+// All Data
export const fetchData = ({ dispatch }) => {
- dispatch('fetchDashboard');
- dispatch('fetchDeploymentsData');
dispatch('fetchEnvironmentsData');
+ dispatch('fetchDashboard');
};
+// Metrics dashboard
+
export const fetchDashboard = ({ state, commit, dispatch }) => {
dispatch('requestMetricsDashboard');
const params = {};
-
- if (state.timeRange) {
- const { start, end } = convertToFixedRange(state.timeRange);
- params.start_time = start;
- params.end_time = end;
- }
-
if (state.currentDashboard) {
params.dashboard = state.currentDashboard;
}
return backOffRequest(() => axios.get(state.dashboardEndpoint, { params }))
.then(resp => resp.data)
- .then(response => dispatch('receiveMetricsDashboardSuccess', { response, params }))
+ .then(response => dispatch('receiveMetricsDashboardSuccess', { response }))
.catch(error => {
Sentry.captureException(error);
@@ -120,61 +118,43 @@ export const fetchDashboard = ({ state, commit, dispatch }) => {
});
};
-function fetchPrometheusResult(prometheusEndpoint, params) {
- return backOffRequest(() => axios.get(prometheusEndpoint, { params }))
- .then(res => res.data)
- .then(response => {
- if (response.status === 'error') {
- throw new Error(response.error);
- }
-
- return response.data.result;
- });
-}
-
-/**
- * Returns list of metrics in data.result
- * {"status":"success", "data":{"resultType":"matrix","result":[]}}
- *
- * @param {metric} metric
- */
-export const fetchPrometheusMetric = ({ commit }, { metric, params }) => {
- const { start_time, end_time } = params;
- const timeDiff = (new Date(end_time) - new Date(start_time)) / 1000;
+export const requestMetricsDashboard = ({ commit }) => {
+ commit(types.REQUEST_METRICS_DASHBOARD);
+};
+export const receiveMetricsDashboardSuccess = ({ commit, dispatch }, { response }) => {
+ const { all_dashboards, dashboard, metrics_data } = response;
- const minStep = 60;
- const queryDataPoints = 600;
- const step = metric.step ? metric.step : Math.max(minStep, Math.ceil(timeDiff / queryDataPoints));
+ commit(types.SET_ALL_DASHBOARDS, all_dashboards);
+ commit(types.RECEIVE_METRICS_DASHBOARD_SUCCESS, dashboard);
+ commit(types.SET_ENDPOINTS, convertObjectPropsToCamelCase(metrics_data));
- const queryParams = {
- start_time,
- end_time,
- step,
- };
+ return dispatch('fetchPrometheusMetrics');
+};
+export const receiveMetricsDashboardFailure = ({ commit }, error) => {
+ commit(types.RECEIVE_METRICS_DASHBOARD_FAILURE, error);
+};
- commit(types.REQUEST_METRIC_RESULT, { metricId: metric.metricId });
+// Metrics
- return fetchPrometheusResult(metric.prometheusEndpointPath, queryParams)
- .then(result => {
- commit(types.RECEIVE_METRIC_RESULT_SUCCESS, { metricId: metric.metricId, result });
- })
- .catch(error => {
- Sentry.captureException(error);
+/**
+ * Loads timeseries data: Prometheus data points and deployment data from the project
+ * @param {Object} Vuex store
+ */
+export const fetchPrometheusMetrics = ({ state, dispatch, getters }) => {
+ dispatch('fetchDeploymentsData');
- commit(types.RECEIVE_METRIC_RESULT_FAILURE, { metricId: metric.metricId, error });
- // Continue to throw error so the dashboard can notify using createFlash
- throw error;
- });
-};
+ if (!state.timeRange) {
+ createFlash(s__(`Metrics|Invalid time range, please verify.`), 'warning');
+ return Promise.reject();
+ }
-export const fetchPrometheusMetrics = ({ state, commit, dispatch, getters }, params) => {
- commit(types.REQUEST_METRICS_DATA);
+ const defaultQueryParams = prometheusMetricQueryParams(state.timeRange);
const promises = [];
state.dashboard.panelGroups.forEach(group => {
group.panels.forEach(panel => {
panel.metrics.forEach(metric => {
- promises.push(dispatch('fetchPrometheusMetric', { metric, params }));
+ promises.push(dispatch('fetchPrometheusMetric', { metric, defaultQueryParams }));
});
});
});
@@ -192,6 +172,35 @@ export const fetchPrometheusMetrics = ({ state, commit, dispatch, getters }, par
});
};
+/**
+ * Returns list of metrics in data.result
+ * {"status":"success", "data":{"resultType":"matrix","result":[]}}
+ *
+ * @param {metric} metric
+ */
+export const fetchPrometheusMetric = ({ commit }, { metric, defaultQueryParams }) => {
+ const queryParams = { ...defaultQueryParams };
+ if (metric.step) {
+ queryParams.step = metric.step;
+ }
+
+ commit(types.REQUEST_METRIC_RESULT, { metricId: metric.metricId });
+
+ return getPrometheusMetricResult(metric.prometheusEndpointPath, queryParams)
+ .then(result => {
+ commit(types.RECEIVE_METRIC_RESULT_SUCCESS, { metricId: metric.metricId, result });
+ })
+ .catch(error => {
+ Sentry.captureException(error);
+
+ commit(types.RECEIVE_METRIC_RESULT_FAILURE, { metricId: metric.metricId, error });
+ // Continue to throw error so the dashboard can notify using createFlash
+ throw error;
+ });
+};
+
+// Deployments
+
export const fetchDeploymentsData = ({ state, dispatch }) => {
if (!state.deploymentsEndpoint) {
return Promise.resolve([]);
@@ -212,6 +221,14 @@ export const fetchDeploymentsData = ({ state, dispatch }) => {
createFlash(s__('Metrics|There was an error getting deployment information.'));
});
};
+export const receiveDeploymentsDataSuccess = ({ commit }, data) => {
+ commit(types.RECEIVE_DEPLOYMENTS_DATA_SUCCESS, data);
+};
+export const receiveDeploymentsDataFailure = ({ commit }) => {
+ commit(types.RECEIVE_DEPLOYMENTS_DATA_FAILURE);
+};
+
+// Environments
export const fetchEnvironmentsData = ({ state, dispatch }) => {
dispatch('requestEnvironmentsData');
@@ -241,6 +258,17 @@ export const fetchEnvironmentsData = ({ state, dispatch }) => {
createFlash(s__('Metrics|There was an error getting environments information.'));
});
};
+export const requestEnvironmentsData = ({ commit }) => {
+ commit(types.REQUEST_ENVIRONMENTS_DATA);
+};
+export const receiveEnvironmentsDataSuccess = ({ commit }, data) => {
+ commit(types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS, data);
+};
+export const receiveEnvironmentsDataFailure = ({ commit }) => {
+ commit(types.RECEIVE_ENVIRONMENTS_DATA_FAILURE);
+};
+
+// Dashboard manipulation
/**
* Set a new array of metrics to a panel group
diff --git a/app/assets/javascripts/monitoring/stores/mutation_types.js b/app/assets/javascripts/monitoring/stores/mutation_types.js
index 09eb7dc1673..9a3489d53d7 100644
--- a/app/assets/javascripts/monitoring/stores/mutation_types.js
+++ b/app/assets/javascripts/monitoring/stores/mutation_types.js
@@ -1,19 +1,24 @@
-export const REQUEST_METRICS_DATA = 'REQUEST_METRICS_DATA';
-export const RECEIVE_METRICS_DATA_SUCCESS = 'RECEIVE_METRICS_DATA_SUCCESS';
-export const RECEIVE_METRICS_DATA_FAILURE = 'RECEIVE_METRICS_DATA_FAILURE';
+// Dashboard "skeleton", groups, panels and metrics
+export const REQUEST_METRICS_DASHBOARD = 'REQUEST_METRICS_DASHBOARD';
+export const RECEIVE_METRICS_DASHBOARD_SUCCESS = 'RECEIVE_METRICS_DASHBOARD_SUCCESS';
+export const RECEIVE_METRICS_DASHBOARD_FAILURE = 'RECEIVE_METRICS_DASHBOARD_FAILURE';
+// Git project deployments
export const REQUEST_DEPLOYMENTS_DATA = 'REQUEST_DEPLOYMENTS_DATA';
export const RECEIVE_DEPLOYMENTS_DATA_SUCCESS = 'RECEIVE_DEPLOYMENTS_DATA_SUCCESS';
export const RECEIVE_DEPLOYMENTS_DATA_FAILURE = 'RECEIVE_DEPLOYMENTS_DATA_FAILURE';
+// Environments
export const REQUEST_ENVIRONMENTS_DATA = 'REQUEST_ENVIRONMENTS_DATA';
export const RECEIVE_ENVIRONMENTS_DATA_SUCCESS = 'RECEIVE_ENVIRONMENTS_DATA_SUCCESS';
export const RECEIVE_ENVIRONMENTS_DATA_FAILURE = 'RECEIVE_ENVIRONMENTS_DATA_FAILURE';
+// Metric data points
export const REQUEST_METRIC_RESULT = 'REQUEST_METRIC_RESULT';
export const RECEIVE_METRIC_RESULT_SUCCESS = 'RECEIVE_METRIC_RESULT_SUCCESS';
export const RECEIVE_METRIC_RESULT_FAILURE = 'RECEIVE_METRIC_RESULT_FAILURE';
+// Parameters and other information
export const SET_TIME_RANGE = 'SET_TIME_RANGE';
export const SET_ALL_DASHBOARDS = 'SET_ALL_DASHBOARDS';
export const SET_ENDPOINTS = 'SET_ENDPOINTS';
diff --git a/app/assets/javascripts/monitoring/stores/mutations.js b/app/assets/javascripts/monitoring/stores/mutations.js
index 2e10d189087..0a7bb47d533 100644
--- a/app/assets/javascripts/monitoring/stores/mutations.js
+++ b/app/assets/javascripts/monitoring/stores/mutations.js
@@ -74,18 +74,18 @@ export default {
/**
* Dashboard panels structure and global state
*/
- [types.REQUEST_METRICS_DATA](state) {
+ [types.REQUEST_METRICS_DASHBOARD](state) {
state.emptyState = 'loading';
state.showEmptyState = true;
},
- [types.RECEIVE_METRICS_DATA_SUCCESS](state, dashboard) {
+ [types.RECEIVE_METRICS_DASHBOARD_SUCCESS](state, dashboard) {
state.dashboard = mapToDashboardViewModel(dashboard);
if (!state.dashboard.panelGroups.length) {
state.emptyState = 'noData';
}
},
- [types.RECEIVE_METRICS_DATA_FAILURE](state, error) {
+ [types.RECEIVE_METRICS_DASHBOARD_FAILURE](state, error) {
state.emptyState = error ? 'unableToConnect' : 'noData';
state.showEmptyState = true;
},
diff --git a/app/assets/javascripts/notes/components/sort_discussion.vue b/app/assets/javascripts/notes/components/sort_discussion.vue
index 3f82ddde3ef..4a7543819eb 100644
--- a/app/assets/javascripts/notes/components/sort_discussion.vue
+++ b/app/assets/javascripts/notes/components/sort_discussion.vue
@@ -1,7 +1,9 @@
+gs
<script>
import { GlIcon } from '@gitlab/ui';
import { mapActions, mapGetters } from 'vuex';
import { __ } from '~/locale';
+import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import Tracking from '~/tracking';
import { ASC, DESC } from '../constants';
@@ -14,16 +16,20 @@ export default {
SORT_OPTIONS,
components: {
GlIcon,
+ LocalStorageSync,
},
mixins: [Tracking.mixin()],
computed: {
- ...mapGetters(['sortDirection']),
+ ...mapGetters(['sortDirection', 'noteableType']),
selectedOption() {
return SORT_OPTIONS.find(({ key }) => this.sortDirection === key);
},
dropdownText() {
return this.selectedOption.text;
},
+ storageKey() {
+ return `sort_direction_${this.noteableType.toLowerCase()}`;
+ },
},
methods: {
...mapActions(['setDiscussionSortDirection']),
@@ -44,6 +50,11 @@ export default {
<template>
<div class="mr-2 d-inline-block align-bottom full-width-mobile">
+ <local-storage-sync
+ :value="sortDirection"
+ :storage-key="storageKey"
+ @input="setDiscussionSortDirection"
+ />
<button class="btn btn-sm js-dropdown-text" data-toggle="dropdown" aria-expanded="false">
{{ dropdownText }}
<gl-icon name="chevron-down" />
diff --git a/app/assets/javascripts/snippets/components/snippet_blob_view.vue b/app/assets/javascripts/snippets/components/snippet_blob_view.vue
index 4703a940e08..3e3dcab70c0 100644
--- a/app/assets/javascripts/snippets/components/snippet_blob_view.vue
+++ b/app/assets/javascripts/snippets/components/snippet_blob_view.vue
@@ -4,6 +4,7 @@ import { SNIPPET_VISIBILITY_PUBLIC } from '../constants';
import BlobHeader from '~/blob/components/blob_header.vue';
import BlobContent from '~/blob/components/blob_content.vue';
import { GlLoadingIcon } from '@gitlab/ui';
+import CloneDropdownButton from '~/vue_shared/components/clone_dropdown.vue';
import GetSnippetBlobQuery from '../queries/snippet.blob.query.graphql';
import GetBlobContent from '../queries/snippet.blob.content.query.graphql';
@@ -16,6 +17,7 @@ export default {
BlobHeader,
BlobContent,
GlLoadingIcon,
+ CloneDropdownButton,
},
apollo: {
blob: {
@@ -72,6 +74,9 @@ export default {
const { richViewer, simpleViewer } = this.blob;
return this.activeViewerType === RICH_BLOB_VIEWER ? richViewer : simpleViewer;
},
+ canBeCloned() {
+ return this.snippet.sshUrlToRepo || this.snippet.httpUrlToRepo;
+ },
},
methods: {
switchViewer(newViewer, respectHash = false) {
@@ -90,7 +95,15 @@ export default {
class="prepend-top-20 append-bottom-20"
/>
<article v-else class="file-holder snippet-file-content">
- <blob-header :blob="blob" :active-viewer-type="viewer.type" @viewer-changed="switchViewer" />
+ <blob-header :blob="blob" :active-viewer-type="viewer.type" @viewer-changed="switchViewer">
+ <template #actions>
+ <clone-dropdown-button
+ v-if="canBeCloned"
+ :ssh-link="snippet.sshUrlToRepo"
+ :http-link="snippet.httpUrlToRepo"
+ />
+ </template>
+ </blob-header>
<blob-content :loading="isContentLoading" :content="blobContent" :active-viewer="viewer" />
</article>
</div>
diff --git a/app/assets/javascripts/snippets/fragments/snippetBase.fragment.graphql b/app/assets/javascripts/snippets/fragments/snippetBase.fragment.graphql
index e0cc6cc2dda..22aab7c7795 100644
--- a/app/assets/javascripts/snippets/fragments/snippetBase.fragment.graphql
+++ b/app/assets/javascripts/snippets/fragments/snippetBase.fragment.graphql
@@ -7,8 +7,10 @@ fragment SnippetBase on Snippet {
updatedAt
visibilityLevel
webUrl
+ httpUrlToRepo
+ sshUrlToRepo
userPermissions {
adminSnippet
updateSnippet
}
-} \ No newline at end of file
+}
diff --git a/app/assets/javascripts/vue_shared/components/local_storage_sync.vue b/app/assets/javascripts/vue_shared/components/local_storage_sync.vue
new file mode 100644
index 00000000000..b5d6b872547
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/local_storage_sync.vue
@@ -0,0 +1,39 @@
+<script>
+export default {
+ props: {
+ storageKey: {
+ type: String,
+ required: true,
+ },
+ value: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ watch: {
+ value(newVal) {
+ this.saveValue(newVal);
+ },
+ },
+ mounted() {
+ // On mount, trigger update if we actually have a localStorageValue
+ const value = this.getValue();
+
+ if (value && this.value !== value) {
+ this.$emit('input', value);
+ }
+ },
+ methods: {
+ getValue() {
+ return localStorage.getItem(this.storageKey);
+ },
+ saveValue(val) {
+ localStorage.setItem(this.storageKey, val);
+ },
+ },
+ render() {
+ return this.$slots.default;
+ },
+};
+</script>
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index a56505ee6e2..b6edadb05a9 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -1,6 +1,14 @@
.dropdown {
position: relative;
+ // Once the new design (https://gitlab.com/gitlab-org/gitlab-foss/-/issues/63499/designs)
+ // for Snippets is introduced and Clone button is relocated, we won't
+ // need this style.
+ // Issue for the refactoring: https://gitlab.com/gitlab-org/gitlab/-/issues/213327
+ &.gl-new-dropdown button.dropdown-toggle {
+ @include gl-display-inline-flex;
+ }
+
.btn-link {
&:hover {
cursor: pointer;