diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-10-30 03:07:52 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-10-30 03:07:52 +0300 |
commit | 4c016ad02422709d3a341215952a9b1cdb4a8451 (patch) | |
tree | 599e58df9e1f8987a9f9400b0abf61612e4e125a /app | |
parent | cb3e6b9c1b020378b5f94b4c38319a2dc961de01 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
16 files changed, 206 insertions, 6 deletions
diff --git a/app/assets/javascripts/grafana_integration/components/grafana_integration.vue b/app/assets/javascripts/grafana_integration/components/grafana_integration.vue new file mode 100644 index 00000000000..2d3212429db --- /dev/null +++ b/app/assets/javascripts/grafana_integration/components/grafana_integration.vue @@ -0,0 +1,82 @@ +<script> +import { GlButton, GlFormGroup, GlFormInput, GlLink } from '@gitlab/ui'; +import Icon from '~/vue_shared/components/icon.vue'; +import { mapState, mapActions } from 'vuex'; + +export default { + components: { + GlButton, + GlFormGroup, + GlFormInput, + GlLink, + Icon, + }, + data() { + return { placeholderUrl: 'https://my-url.grafana.net/my-dashboard' }; + }, + computed: { + ...mapState(['operationsSettingsEndpoint', 'grafanaToken', 'grafanaUrl']), + localGrafanaToken: { + get() { + return this.grafanaToken; + }, + set(token) { + this.setGrafanaToken(token); + }, + }, + localGrafanaUrl: { + get() { + return this.grafanaUrl; + }, + set(url) { + this.setGrafanaUrl(url); + }, + }, + }, + methods: { + ...mapActions(['setGrafanaUrl', 'setGrafanaToken', 'updateGrafanaIntegration']), + }, +}; +</script> + +<template> + <section id="grafana" class="settings no-animate js-grafana-integration"> + <div class="settings-header"> + <h4 class="js-section-header"> + {{ s__('GrafanaIntegration|Grafana Authentication') }} + </h4> + <gl-button class="js-settings-toggle">{{ __('Expand') }}</gl-button> + <p class="js-section-sub-header"> + {{ s__('GrafanaIntegration|Embed Grafana charts in GitLab issues.') }} + </p> + </div> + <div class="settings-content"> + <form> + <gl-form-group + :label="s__('GrafanaIntegration|Grafana URL')" + label-for="grafana-url" + :description="s__('GrafanaIntegration|Enter the base URL of the Grafana instance.')" + > + <gl-form-input id="grafana-url" v-model="localGrafanaUrl" :placeholder="placeholderUrl" /> + </gl-form-group> + <gl-form-group :label="s__('GrafanaIntegration|API Token')" label-for="grafana-token"> + <gl-form-input id="grafana-token" v-model="localGrafanaToken" /> + <p class="form-text text-muted"> + {{ s__('GrafanaIntegration|Enter the Grafana API Token.') }} + <a + href="https://grafana.com/docs/http_api/auth/#create-api-token" + target="_blank" + rel="noopener noreferrer" + > + {{ __('More information') }} + <icon name="external-link" class="vertical-align-middle" /> + </a> + </p> + </gl-form-group> + <gl-button variant="success" @click="updateGrafanaIntegration"> + {{ __('Save Changes') }} + </gl-button> + </form> + </div> + </section> +</template> diff --git a/app/assets/javascripts/grafana_integration/index.js b/app/assets/javascripts/grafana_integration/index.js new file mode 100644 index 00000000000..58c28e09f80 --- /dev/null +++ b/app/assets/javascripts/grafana_integration/index.js @@ -0,0 +1,15 @@ +import Vue from 'vue'; +import store from './store'; +import GrafanaIntegration from './components/grafana_integration.vue'; + +export default () => { + const el = document.querySelector('.js-grafana-integration'); + + return new Vue({ + el, + store: store(el.dataset), + render(createElement) { + return createElement(GrafanaIntegration); + }, + }); +}; diff --git a/app/assets/javascripts/grafana_integration/store/actions.js b/app/assets/javascripts/grafana_integration/store/actions.js new file mode 100644 index 00000000000..98085fdcb2d --- /dev/null +++ b/app/assets/javascripts/grafana_integration/store/actions.js @@ -0,0 +1,38 @@ +import axios from '~/lib/utils/axios_utils'; +import { __ } from '~/locale'; +import createFlash from '~/flash'; +import { refreshCurrentPage } from '~/lib/utils/url_utility'; +import * as mutationTypes from './mutation_types'; + +export const setGrafanaUrl = ({ commit }, url) => commit(mutationTypes.SET_GRAFANA_URL, url); + +export const setGrafanaToken = ({ commit }, token) => + commit(mutationTypes.SET_GRAFANA_TOKEN, token); + +export const updateGrafanaIntegration = ({ state, dispatch }) => + axios + .patch(state.operationsSettingsEndpoint, { + project: { + grafana_integration_attributes: { + grafana_url: state.grafanaUrl, + token: state.grafanaToken, + }, + }, + }) + .then(() => dispatch('receiveGrafanaIntegrationUpdateSuccess')) + .catch(error => dispatch('receiveGrafanaIntegrationUpdateError', error)); + +export const receiveGrafanaIntegrationUpdateSuccess = () => { + /** + * The operations_controller currently handles successful requests + * by creating a flash banner messsage to notify the user. + */ + refreshCurrentPage(); +}; + +export const receiveGrafanaIntegrationUpdateError = (_, error) => { + const { response } = error; + const message = response.data && response.data.message ? response.data.message : ''; + + createFlash(`${__('There was an error saving your changes.')} ${message}`, 'alert'); +}; diff --git a/app/assets/javascripts/grafana_integration/store/index.js b/app/assets/javascripts/grafana_integration/store/index.js new file mode 100644 index 00000000000..e96bb1e8aad --- /dev/null +++ b/app/assets/javascripts/grafana_integration/store/index.js @@ -0,0 +1,16 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import createState from './state'; +import * as actions from './actions'; +import mutations from './mutations'; + +Vue.use(Vuex); + +export const createStore = initialState => + new Vuex.Store({ + state: createState(initialState), + actions, + mutations, + }); + +export default createStore; diff --git a/app/assets/javascripts/grafana_integration/store/mutation_types.js b/app/assets/javascripts/grafana_integration/store/mutation_types.js new file mode 100644 index 00000000000..33ce3228823 --- /dev/null +++ b/app/assets/javascripts/grafana_integration/store/mutation_types.js @@ -0,0 +1,2 @@ +export const SET_GRAFANA_URL = 'SET_GRAFANA_URL'; +export const SET_GRAFANA_TOKEN = 'SET_GRAFANA_TOKEN'; diff --git a/app/assets/javascripts/grafana_integration/store/mutations.js b/app/assets/javascripts/grafana_integration/store/mutations.js new file mode 100644 index 00000000000..e8d63a9a732 --- /dev/null +++ b/app/assets/javascripts/grafana_integration/store/mutations.js @@ -0,0 +1,10 @@ +import * as types from './mutation_types'; + +export default { + [types.SET_GRAFANA_URL](state, url) { + state.grafanaUrl = url; + }, + [types.SET_GRAFANA_TOKEN](state, token) { + state.grafanaToken = token; + }, +}; diff --git a/app/assets/javascripts/grafana_integration/store/state.js b/app/assets/javascripts/grafana_integration/store/state.js new file mode 100644 index 00000000000..c25742c82bc --- /dev/null +++ b/app/assets/javascripts/grafana_integration/store/state.js @@ -0,0 +1,5 @@ +export default (initialState = {}) => ({ + operationsSettingsEndpoint: initialState.operationsSettingsEndpoint, + grafanaToken: initialState.grafanaIntegrationToken || '', + grafanaUrl: initialState.grafanaIntegrationUrl || '', +}); diff --git a/app/assets/javascripts/pages/projects/settings/operations/show/index.js b/app/assets/javascripts/pages/projects/settings/operations/show/index.js index 98e19705976..7037933bc5a 100644 --- a/app/assets/javascripts/pages/projects/settings/operations/show/index.js +++ b/app/assets/javascripts/pages/projects/settings/operations/show/index.js @@ -1,9 +1,13 @@ import mountErrorTrackingForm from '~/error_tracking_settings'; import mountOperationSettings from '~/operation_settings'; +import mountGrafanaIntegration from '~/grafana_integration'; import initSettingsPanels from '~/settings_panels'; document.addEventListener('DOMContentLoaded', () => { mountErrorTrackingForm(); mountOperationSettings(); + if (gon.features.gfmGrafanaIntegration) { + mountGrafanaIntegration(); + } initSettingsPanels(); }); diff --git a/app/assets/javascripts/pages/sessions/new/index.js b/app/assets/javascripts/pages/sessions/new/index.js index c0e798e004f..66ee2d9303f 100644 --- a/app/assets/javascripts/pages/sessions/new/index.js +++ b/app/assets/javascripts/pages/sessions/new/index.js @@ -20,12 +20,17 @@ document.addEventListener('DOMContentLoaded', () => { // Save the URL fragment from the current window location. This will be present if the user was // redirected to sign-in after attempting to access a protected URL that included a fragment. preserveUrlFragment(window.location.hash); +}); +export default function trackData() { if (gon.tracking_data) { const tab = document.querySelector(".new-session-tabs a[href='#register-pane']"); const { category, action, ...data } = gon.tracking_data; + tab.addEventListener('click', () => { Tracking.event(category, action, data); }); } -}); +} + +trackData(); diff --git a/app/models/analytics/cycle_analytics/project_stage.rb b/app/models/analytics/cycle_analytics/project_stage.rb index 23f0db0829b..0cdf199a146 100644 --- a/app/models/analytics/cycle_analytics/project_stage.rb +++ b/app/models/analytics/cycle_analytics/project_stage.rb @@ -10,6 +10,14 @@ module Analytics alias_attribute :parent, :project alias_attribute :parent_id, :project_id + + def self.relative_positioning_query_base(stage) + where(project_id: stage.project_id) + end + + def self.relative_positioning_parent_column + :project_id + end end end end diff --git a/app/models/concerns/analytics/cycle_analytics/stage.rb b/app/models/concerns/analytics/cycle_analytics/stage.rb index 54e9a13d1ea..1376dd97a49 100644 --- a/app/models/concerns/analytics/cycle_analytics/stage.rb +++ b/app/models/concerns/analytics/cycle_analytics/stage.rb @@ -4,6 +4,7 @@ module Analytics module CycleAnalytics module Stage extend ActiveSupport::Concern + include RelativePositioning included do validates :name, presence: true @@ -17,6 +18,7 @@ module Analytics alias_attribute :custom_stage?, :custom scope :default_stages, -> { where(custom: false) } + scope :ordered, -> { order(:relative_position, :id) } end def parent=(_) @@ -58,6 +60,10 @@ module Analytics end_event_identifier.to_s.eql?(stage_params[:end_event_identifier].to_s) end + def find_with_same_parent!(id) + parent.cycle_analytics_stages.find(id) + end + private def validate_stage_event_pairs diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml index 4930c6cf5f7..a6d2c894185 100644 --- a/app/views/layouts/nav/sidebar/_group.html.haml +++ b/app/views/layouts/nav/sidebar/_group.html.haml @@ -16,13 +16,19 @@ .nav-icon-container = sprite_icon('home') %span.nav-item-name - = _('Overview') + - if @group.subgroup? + = _('Subgroup overview') + - else + = _('Group overview') %ul.sidebar-sub-level-items = nav_link(path: ['groups#show', 'groups#details', 'groups#activity', 'groups#subgroups'], html_options: { class: "fly-out-top-item" } ) do = link_to group_path(@group) do %strong.fly-out-top-item-name - = _('Overview') + - if @group.subgroup? + = _('Subgroup overview') + - else + = _('Group overview') %li.divider.fly-out-top-item = nav_link(path: ['groups#show', 'groups#details', 'groups#subgroups'], html_options: { class: 'home' }) do diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml index 247fbfefde9..fdad2a64a80 100644 --- a/app/views/layouts/nav/sidebar/_project.html.haml +++ b/app/views/layouts/nav/sidebar/_project.html.haml @@ -13,13 +13,13 @@ .nav-icon-container = sprite_icon('home') %span.nav-item-name - = _('Project') + = _('Project overview') %ul.sidebar-sub-level-items = nav_link(path: 'projects#show', html_options: { class: "fly-out-top-item" } ) do = link_to project_path(@project) do %strong.fly-out-top-item-name - = _('Project') + = _('Project overview') %li.divider.fly-out-top-item = nav_link(path: 'projects#show') do = link_to project_path(@project), title: _('Project details'), class: 'shortcuts-project' do diff --git a/app/views/projects/deployments/_confirm_rollback_modal.html.haml b/app/views/projects/deployments/_confirm_rollback_modal.html.haml index ff40e404e5f..9162827b501 100644 --- a/app/views/projects/deployments/_confirm_rollback_modal.html.haml +++ b/app/views/projects/deployments/_confirm_rollback_modal.html.haml @@ -13,7 +13,7 @@ %p= s_('Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?').html_safe % {commit_id: commit_sha} - else %p - = s_('Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?').html_safe % {commit_id: commit_sha} + = s_('Environments|This action will run the job defined by %{environment_name} for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?').html_safe % {commit_id: commit_sha, environment_name: @environment.name} .modal-footer = button_tag _('Cancel'), type: 'button', class: 'btn btn-cancel', data: { dismiss: 'modal' } = link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-danger' do diff --git a/app/views/projects/settings/operations/_grafana_integration.html.haml b/app/views/projects/settings/operations/_grafana_integration.html.haml new file mode 100644 index 00000000000..ae90b6dd5aa --- /dev/null +++ b/app/views/projects/settings/operations/_grafana_integration.html.haml @@ -0,0 +1,2 @@ +.js-grafana-integration{ data: { operations_settings_endpoint: project_settings_operations_path(@project), + grafana_integration: { url: grafana_integration_url, token: grafana_integration_token } } } diff --git a/app/views/projects/settings/operations/show.html.haml b/app/views/projects/settings/operations/show.html.haml index 0a7a155bc12..3c955e5f558 100644 --- a/app/views/projects/settings/operations/show.html.haml +++ b/app/views/projects/settings/operations/show.html.haml @@ -5,4 +5,5 @@ = render_if_exists 'projects/settings/operations/incidents' = render 'projects/settings/operations/error_tracking' = render 'projects/settings/operations/external_dashboard' += render 'projects/settings/operations/grafana_integration' = render_if_exists 'projects/settings/operations/tracing' |