diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-18 13:34:06 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-18 13:34:06 +0300 |
commit | 859a6fb938bb9ee2a317c46dfa4fcc1af49608f0 (patch) | |
tree | d7f2700abe6b4ffcb2dcfc80631b2d87d0609239 /app/assets/javascripts/integrations | |
parent | 446d496a6d000c73a304be52587cd9bbc7493136 (diff) |
Add latest changes from gitlab-org/gitlab@13-9-stable-eev13.9.0-rc42
Diffstat (limited to 'app/assets/javascripts/integrations')
15 files changed, 253 insertions, 99 deletions
diff --git a/app/assets/javascripts/integrations/edit/components/active_checkbox.vue b/app/assets/javascripts/integrations/edit/components/active_checkbox.vue index 6698984d02f..8677f139723 100644 --- a/app/assets/javascripts/integrations/edit/components/active_checkbox.vue +++ b/app/assets/javascripts/integrations/edit/components/active_checkbox.vue @@ -1,6 +1,6 @@ <script> -import { mapGetters } from 'vuex'; import { GlFormGroup, GlFormCheckbox } from '@gitlab/ui'; +import { mapGetters } from 'vuex'; import eventHub from '../event_hub'; export default { diff --git a/app/assets/javascripts/integrations/edit/components/confirmation_modal.vue b/app/assets/javascripts/integrations/edit/components/confirmation_modal.vue index 93ea1f4f636..bcf4b036fd2 100644 --- a/app/assets/javascripts/integrations/edit/components/confirmation_modal.vue +++ b/app/assets/javascripts/integrations/edit/components/confirmation_modal.vue @@ -1,6 +1,6 @@ <script> -import { mapGetters } from 'vuex'; import { GlModal } from '@gitlab/ui'; +import { mapGetters } from 'vuex'; import { __ } from '~/locale'; export default { diff --git a/app/assets/javascripts/integrations/edit/components/dynamic_field.vue b/app/assets/javascripts/integrations/edit/components/dynamic_field.vue index f568f7e6d3d..a4baca20ac9 100644 --- a/app/assets/javascripts/integrations/edit/components/dynamic_field.vue +++ b/app/assets/javascripts/integrations/edit/components/dynamic_field.vue @@ -1,10 +1,10 @@ <script> /* eslint-disable vue/no-v-html */ -import { mapGetters } from 'vuex'; -import { capitalize, lowerCase, isEmpty } from 'lodash'; import { GlFormGroup, GlFormCheckbox, GlFormInput, GlFormSelect, GlFormTextarea } from '@gitlab/ui'; -import eventHub from '../event_hub'; +import { capitalize, lowerCase, isEmpty } from 'lodash'; +import { mapGetters } from 'vuex'; import { __, sprintf } from '~/locale'; +import eventHub from '../event_hub'; export default { name: 'DynamicField', diff --git a/app/assets/javascripts/integrations/edit/components/integration_form.vue b/app/assets/javascripts/integrations/edit/components/integration_form.vue index ac8a64d5f3b..3ec0c23e55d 100644 --- a/app/assets/javascripts/integrations/edit/components/integration_form.vue +++ b/app/assets/javascripts/integrations/edit/components/integration_form.vue @@ -1,18 +1,18 @@ <script> +import { GlButton, GlModalDirective, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; import { mapState, mapActions, mapGetters } from 'vuex'; -import { GlButton, GlModalDirective } from '@gitlab/ui'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import eventHub from '../event_hub'; import { integrationLevels } from '../constants'; +import eventHub from '../event_hub'; -import OverrideDropdown from './override_dropdown.vue'; import ActiveCheckbox from './active_checkbox.vue'; -import JiraTriggerFields from './jira_trigger_fields.vue'; -import JiraIssuesFields from './jira_issues_fields.vue'; -import TriggerFields from './trigger_fields.vue'; -import DynamicField from './dynamic_field.vue'; import ConfirmationModal from './confirmation_modal.vue'; +import DynamicField from './dynamic_field.vue'; +import JiraIssuesFields from './jira_issues_fields.vue'; +import JiraTriggerFields from './jira_trigger_fields.vue'; +import OverrideDropdown from './override_dropdown.vue'; import ResetConfirmationModal from './reset_confirmation_modal.vue'; +import TriggerFields from './trigger_fields.vue'; export default { name: 'IntegrationForm', @@ -28,9 +28,17 @@ export default { GlButton, }, directives: { - 'gl-modal': GlModalDirective, + GlModal: GlModalDirective, + SafeHtml, }, mixins: [glFeatureFlagsMixin()], + props: { + helpHtml: { + type: String, + required: false, + default: '', + }, + }, computed: { ...mapGetters(['currentKey', 'propsSource', 'isDisabled']), ...mapState([ @@ -80,11 +88,14 @@ export default { this.fetchResetIntegration(); }, }, + helpHtmlConfig: { + ADD_TAGS: ['use'], // to support icon SVGs + }, }; </script> <template> - <div> + <div class="gl-mb-3"> <override-dropdown v-if="defaultState !== null" :inherit-from-id="defaultState.id" @@ -92,80 +103,92 @@ export default { :learn-more-path="propsSource.learnMorePath" @change="setOverride" /> - <active-checkbox v-if="propsSource.showActive" :key="`${currentKey}-active-checkbox`" /> - <jira-trigger-fields - v-if="isJira" - :key="`${currentKey}-jira-trigger-fields`" - v-bind="propsSource.triggerFieldsProps" - /> - <trigger-fields - v-else-if="propsSource.triggerEvents.length" - :key="`${currentKey}-trigger-fields`" - :events="propsSource.triggerEvents" - :type="propsSource.type" - /> - <dynamic-field - v-for="field in propsSource.fields" - :key="`${currentKey}-${field.name}`" - v-bind="field" - /> - <jira-issues-fields - v-if="showJiraIssuesFields" - :key="`${currentKey}-jira-issues-fields`" - v-bind="propsSource.jiraIssuesProps" - /> - <div v-if="isEditable" class="footer-block row-content-block"> - <template v-if="isInstanceOrGroupLevel"> - <gl-button - v-gl-modal.confirmSaveIntegration - category="primary" - variant="success" - :loading="isSaving" - :disabled="isDisabled" - data-qa-selector="save_changes_button" - > - {{ __('Save changes') }} - </gl-button> - <confirmation-modal @submit="onSaveClick" /> - </template> - <gl-button - v-else - category="primary" - variant="success" - type="submit" - :loading="isSaving" - :disabled="isDisabled" - data-qa-selector="save_changes_button" - @click.prevent="onSaveClick" - > - {{ __('Save changes') }} - </gl-button> - <gl-button - v-if="propsSource.canTest" - :loading="isTesting" - :disabled="isDisabled" - :href="propsSource.testPath" - @click.prevent="onTestClick" - > - {{ __('Test settings') }} - </gl-button> + <div class="row"> + <div class="col-lg-4"></div> + + <div class="col-lg-8"> + <!-- helpHtml is trusted input --> + <div v-if="helpHtml" v-safe-html:[$options.helpHtmlConfig]="helpHtml"></div> + + <active-checkbox v-if="propsSource.showActive" :key="`${currentKey}-active-checkbox`" /> + <jira-trigger-fields + v-if="isJira" + :key="`${currentKey}-jira-trigger-fields`" + v-bind="propsSource.triggerFieldsProps" + /> + <trigger-fields + v-else-if="propsSource.triggerEvents.length" + :key="`${currentKey}-trigger-fields`" + :events="propsSource.triggerEvents" + :type="propsSource.type" + /> + <dynamic-field + v-for="field in propsSource.fields" + :key="`${currentKey}-${field.name}`" + v-bind="field" + /> + <jira-issues-fields + v-if="showJiraIssuesFields" + :key="`${currentKey}-jira-issues-fields`" + v-bind="propsSource.jiraIssuesProps" + /> + <div v-if="isEditable" class="footer-block row-content-block"> + <template v-if="isInstanceOrGroupLevel"> + <gl-button + v-gl-modal.confirmSaveIntegration + category="primary" + variant="success" + :loading="isSaving" + :disabled="isDisabled" + data-qa-selector="save_changes_button" + > + {{ __('Save changes') }} + </gl-button> + <confirmation-modal @submit="onSaveClick" /> + </template> + <gl-button + v-else + category="primary" + variant="success" + type="submit" + :loading="isSaving" + :disabled="isDisabled" + data-qa-selector="save_changes_button" + @click.prevent="onSaveClick" + > + {{ __('Save changes') }} + </gl-button> + + <gl-button + v-if="propsSource.canTest" + :loading="isTesting" + :disabled="isDisabled" + :href="propsSource.testPath" + @click.prevent="onTestClick" + > + {{ __('Test settings') }} + </gl-button> - <template v-if="showReset"> - <gl-button - v-gl-modal.confirmResetIntegration - category="secondary" - variant="default" - :loading="isResetting" - :disabled="isDisabled" - data-testid="reset-button" - > - {{ __('Reset') }} - </gl-button> - <reset-confirmation-modal @reset="onResetClick" /> - </template> + <template v-if="showReset"> + <gl-button + v-gl-modal.confirmResetIntegration + category="secondary" + variant="default" + :loading="isResetting" + :disabled="isDisabled" + data-testid="reset-button" + > + {{ __('Reset') }} + </gl-button> + <reset-confirmation-modal @reset="onResetClick" /> + </template> - <gl-button class="btn-cancel" :href="propsSource.cancelPath">{{ __('Cancel') }}</gl-button> + <gl-button class="btn-cancel" :href="propsSource.cancelPath">{{ + __('Cancel') + }}</gl-button> + </div> + </div> </div> </div> </template> diff --git a/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue b/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue index 1baa2b440b0..d3d1fd8ddc3 100644 --- a/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue +++ b/app/assets/javascripts/integrations/edit/components/jira_issues_fields.vue @@ -8,6 +8,7 @@ import { GlButton, GlCard, } from '@gitlab/ui'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import eventHub from '../event_hub'; export default { @@ -20,18 +21,36 @@ export default { GlLink, GlButton, GlCard, + JiraIssueCreationVulnerabilities: () => + import('ee_component/integrations/edit/components/jira_issue_creation_vulnerabilities.vue'), }, + mixins: [glFeatureFlagsMixin()], props: { showJiraIssuesIntegration: { type: Boolean, required: false, default: false, }, + showJiraVulnerabilitiesIntegration: { + type: Boolean, + required: false, + default: false, + }, initialEnableJiraIssues: { type: Boolean, required: false, default: null, }, + initialEnableJiraVulnerabilities: { + type: Boolean, + required: false, + default: false, + }, + initialVulnerabilitiesIssuetype: { + type: String, + required: false, + default: undefined, + }, initialProjectKey: { type: String, required: false, @@ -45,12 +64,12 @@ export default { upgradePlanPath: { type: String, required: false, - default: null, + default: '', }, editProjectPath: { type: String, required: false, - default: null, + default: '', }, }, data() { @@ -64,6 +83,13 @@ export default { validProjectKey() { return !this.enableJiraIssues || Boolean(this.projectKey) || !this.validated; }, + showJiraVulnerabilitiesOptions() { + return ( + this.enableJiraIssues && + this.showJiraVulnerabilitiesIntegration && + this.glFeatures.jiraForVulnerabilities + ); + }, }, created() { eventHub.$on('validateForm', this.validateForm); @@ -75,6 +101,9 @@ export default { validateForm() { this.validated = true; }, + getJiraIssueTypes() { + eventHub.$emit('getJiraIssueTypes'); + }, }, }; </script> @@ -105,6 +134,14 @@ export default { }} </template> </gl-form-checkbox> + <jira-issue-creation-vulnerabilities + v-if="showJiraVulnerabilitiesOptions" + :project-key="projectKey" + :initial-is-enabled="initialEnableJiraVulnerabilities" + :initial-issue-type-id="initialVulnerabilitiesIssuetype" + data-testid="jira-for-vulnerabilities" + @request-get-issue-types="getJiraIssueTypes" + /> </template> <gl-card v-else class="gl-mt-7"> <strong>{{ __('This is a Premium feature') }}</strong> diff --git a/app/assets/javascripts/integrations/edit/components/jira_trigger_fields.vue b/app/assets/javascripts/integrations/edit/components/jira_trigger_fields.vue index bc005aa16e9..af4e9acf4ba 100644 --- a/app/assets/javascripts/integrations/edit/components/jira_trigger_fields.vue +++ b/app/assets/javascripts/integrations/edit/components/jira_trigger_fields.vue @@ -1,6 +1,6 @@ <script> -import { mapGetters } from 'vuex'; import { GlFormGroup, GlFormCheckbox, GlFormRadio } from '@gitlab/ui'; +import { mapGetters } from 'vuex'; import { s__ } from '~/locale'; const commentDetailOptions = [ diff --git a/app/assets/javascripts/integrations/edit/components/override_dropdown.vue b/app/assets/javascripts/integrations/edit/components/override_dropdown.vue index 4e2c37ac7f3..7b3a067b186 100644 --- a/app/assets/javascripts/integrations/edit/components/override_dropdown.vue +++ b/app/assets/javascripts/integrations/edit/components/override_dropdown.vue @@ -1,6 +1,6 @@ <script> -import { mapState } from 'vuex'; import { GlDropdown, GlDropdownItem, GlLink } from '@gitlab/ui'; +import { mapState } from 'vuex'; import { s__ } from '~/locale'; import { defaultIntegrationLevel, overrideDropdownDescriptions } from '../constants'; diff --git a/app/assets/javascripts/integrations/edit/components/reset_confirmation_modal.vue b/app/assets/javascripts/integrations/edit/components/reset_confirmation_modal.vue index d8503910566..9472a3eeafe 100644 --- a/app/assets/javascripts/integrations/edit/components/reset_confirmation_modal.vue +++ b/app/assets/javascripts/integrations/edit/components/reset_confirmation_modal.vue @@ -1,6 +1,6 @@ <script> -import { mapGetters } from 'vuex'; import { GlModal } from '@gitlab/ui'; +import { mapGetters } from 'vuex'; import { __ } from '~/locale'; diff --git a/app/assets/javascripts/integrations/edit/components/trigger_fields.vue b/app/assets/javascripts/integrations/edit/components/trigger_fields.vue index 32878c6afa4..1bbecea05ad 100644 --- a/app/assets/javascripts/integrations/edit/components/trigger_fields.vue +++ b/app/assets/javascripts/integrations/edit/components/trigger_fields.vue @@ -1,7 +1,7 @@ <script> -import { mapGetters } from 'vuex'; -import { startCase } from 'lodash'; import { GlFormGroup, GlFormCheckbox, GlFormInput } from '@gitlab/ui'; +import { startCase } from 'lodash'; +import { mapGetters } from 'vuex'; import { __ } from '~/locale'; const typeWithPlaceholder = { diff --git a/app/assets/javascripts/integrations/edit/index.js b/app/assets/javascripts/integrations/edit/index.js index 95a53f1beab..ab9bdd9ca2e 100644 --- a/app/assets/javascripts/integrations/edit/index.js +++ b/app/assets/javascripts/integrations/edit/index.js @@ -1,7 +1,7 @@ import Vue from 'vue'; -import { createStore } from './store'; import { parseBoolean } from '~/lib/utils/common_utils'; import IntegrationForm from './components/integration_form.vue'; +import { createStore } from './store'; function parseBooleanInData(data) { const result = {}; @@ -27,6 +27,7 @@ function parseDatasetToProps(data) { cancelPath, testPath, resetPath, + vulnerabilitiesIssuetype, ...booleanAttributes } = data; const { @@ -38,7 +39,9 @@ function parseDatasetToProps(data) { mergeRequestEvents, enableComments, showJiraIssuesIntegration, + showJiraVulnerabilitiesIntegration, enableJiraIssues, + enableJiraVulnerabilities, gitlabIssuesEnabled, } = parseBooleanInData(booleanAttributes); @@ -59,7 +62,10 @@ function parseDatasetToProps(data) { }, jiraIssuesProps: { showJiraIssuesIntegration, + showJiraVulnerabilitiesIntegration, initialEnableJiraIssues: enableJiraIssues, + initialEnableJiraVulnerabilities: enableJiraVulnerabilities, + initialVulnerabilitiesIssuetype: vulnerabilitiesIssuetype, initialProjectKey: projectKey, gitlabIssuesEnabled, upgradePlanPath, @@ -80,21 +86,29 @@ export default (el, defaultEl) => { } const props = parseDatasetToProps(el.dataset); - const initialState = { defaultState: null, customState: props, }; - if (defaultEl) { initialState.defaultState = Object.freeze(parseDatasetToProps(defaultEl.dataset)); } + // Here, we capture the "helpHtml", so we can pass it to the Vue component + // to position it where ever it wants. + // Because this node is a _child_ of `el`, it will be removed when the Vue component is mounted, + // so we don't need to manually remove it. + const helpHtml = el.querySelector('.js-integration-help-html')?.innerHTML; + return new Vue({ el, store: createStore(initialState), render(createElement) { - return createElement(IntegrationForm); + return createElement(IntegrationForm, { + props: { + helpHtml, + }, + }); }, }); }; diff --git a/app/assets/javascripts/integrations/edit/store/actions.js b/app/assets/javascripts/integrations/edit/store/actions.js index 421917b720a..400397c050c 100644 --- a/app/assets/javascripts/integrations/edit/store/actions.js +++ b/app/assets/javascripts/integrations/edit/store/actions.js @@ -26,3 +26,18 @@ export const fetchResetIntegration = ({ dispatch, getters }) => { .then(() => dispatch('receiveResetIntegrationSuccess')) .catch(() => dispatch('receiveResetIntegrationError')); }; + +export const requestJiraIssueTypes = ({ commit }) => { + commit(types.SET_JIRA_ISSUE_TYPES_ERROR_MESSAGE, ''); + commit(types.SET_IS_LOADING_JIRA_ISSUE_TYPES, true); +}; +export const receiveJiraIssueTypesSuccess = ({ commit }, issueTypes = []) => { + commit(types.SET_IS_LOADING_JIRA_ISSUE_TYPES, false); + commit(types.SET_JIRA_ISSUE_TYPES, issueTypes); +}; + +export const receiveJiraIssueTypesError = ({ commit }, errorMessage) => { + commit(types.SET_IS_LOADING_JIRA_ISSUE_TYPES, false); + commit(types.SET_JIRA_ISSUE_TYPES, []); + commit(types.SET_JIRA_ISSUE_TYPES_ERROR_MESSAGE, errorMessage); +}; diff --git a/app/assets/javascripts/integrations/edit/store/mutation_types.js b/app/assets/javascripts/integrations/edit/store/mutation_types.js index 54928148b22..c681056a515 100644 --- a/app/assets/javascripts/integrations/edit/store/mutation_types.js +++ b/app/assets/javascripts/integrations/edit/store/mutation_types.js @@ -3,5 +3,9 @@ export const SET_IS_SAVING = 'SET_IS_SAVING'; export const SET_IS_TESTING = 'SET_IS_TESTING'; export const SET_IS_RESETTING = 'SET_IS_RESETTING'; +export const SET_IS_LOADING_JIRA_ISSUE_TYPES = 'SET_IS_LOADING_JIRA_ISSUE_TYPES'; +export const SET_JIRA_ISSUE_TYPES_ERROR_MESSAGE = 'SET_JIRA_ISSUE_TYPES_ERROR_MESSAGE'; +export const SET_JIRA_ISSUE_TYPES = 'SET_JIRA_ISSUE_TYPES'; + export const REQUEST_RESET_INTEGRATION = 'REQUEST_RESET_INTEGRATION'; export const RECEIVE_RESET_INTEGRATION_ERROR = 'RECEIVE_RESET_INTEGRATION_ERROR'; diff --git a/app/assets/javascripts/integrations/edit/store/mutations.js b/app/assets/javascripts/integrations/edit/store/mutations.js index 826757e665b..279df1b9266 100644 --- a/app/assets/javascripts/integrations/edit/store/mutations.js +++ b/app/assets/javascripts/integrations/edit/store/mutations.js @@ -19,4 +19,13 @@ export default { [types.RECEIVE_RESET_INTEGRATION_ERROR](state) { state.isResetting = false; }, + [types.SET_JIRA_ISSUE_TYPES](state, jiraIssueTypes) { + state.jiraIssueTypes = jiraIssueTypes; + }, + [types.SET_IS_LOADING_JIRA_ISSUE_TYPES](state, isLoadingJiraIssueTypes) { + state.isLoadingJiraIssueTypes = isLoadingJiraIssueTypes; + }, + [types.SET_JIRA_ISSUE_TYPES_ERROR_MESSAGE](state, errorMessage) { + state.loadingJiraIssueTypesErrorMessage = errorMessage; + }, }; diff --git a/app/assets/javascripts/integrations/edit/store/state.js b/app/assets/javascripts/integrations/edit/store/state.js index aae3db1583f..1c0b274e4ef 100644 --- a/app/assets/javascripts/integrations/edit/store/state.js +++ b/app/assets/javascripts/integrations/edit/store/state.js @@ -8,5 +8,8 @@ export default ({ defaultState = null, customState = {} } = {}) => { isSaving: false, isTesting: false, isResetting: false, + isLoadingJiraIssueTypes: false, + loadingJiraIssueTypesErrorMessage: '', + jiraIssueTypes: [], }; }; diff --git a/app/assets/javascripts/integrations/integration_settings_form.js b/app/assets/javascripts/integrations/integration_settings_form.js index 861655a6a64..801cf3ed27e 100644 --- a/app/assets/javascripts/integrations/integration_settings_form.js +++ b/app/assets/javascripts/integrations/integration_settings_form.js @@ -1,8 +1,8 @@ import $ from 'jquery'; import { delay } from 'lodash'; -import axios from '../lib/utils/axios_utils'; import { __, s__ } from '~/locale'; import toast from '~/vue_shared/plugins/global_toast'; +import axios from '../lib/utils/axios_utils'; import initForm from './edit'; import eventHub from './edit/event_hub'; @@ -33,6 +33,12 @@ export default class IntegrationSettingsForm { eventHub.$on('saveIntegration', () => { this.saveIntegration(); }); + eventHub.$on('getJiraIssueTypes', () => { + // eslint-disable-next-line no-jquery/no-serialize + this.getJiraIssueTypes(this.$form.serialize()); + }); + + eventHub.$emit('formInitialized'); } saveIntegration() { @@ -80,15 +86,58 @@ export default class IntegrationSettingsForm { } /** + * Get a list of Jira issue types for the currently configured project + * + * @param {string} formData - URL encoded string containing the form data + * + * @return {Promise} + */ + getJiraIssueTypes(formData) { + const { + $store: { dispatch }, + } = this.vue; + + dispatch('requestJiraIssueTypes'); + + return this.fetchTestSettings(formData) + .then( + ({ + data: { + issuetypes, + error, + message = s__('Integrations|Connection failed. Please check your settings.'), + }, + }) => { + if (error || !issuetypes?.length) { + eventHub.$emit('validateForm'); + throw new Error(message); + } + + dispatch('receiveJiraIssueTypesSuccess', issuetypes); + }, + ) + .catch(({ message = __('Something went wrong on our end.') }) => { + dispatch('receiveJiraIssueTypesError', message); + }); + } + + /** + * Send request to the test endpoint which checks if the current config is valid + */ + fetchTestSettings(formData) { + return axios.put(this.testEndPoint, formData); + } + + /** * Test Integration config */ testSettings(formData) { - return axios - .put(this.testEndPoint, formData) + return this.fetchTestSettings(formData) .then(({ data }) => { if (data.error) { toast(`${data.message} ${data.service_response}`); } else { + this.vue.$store.dispatch('receiveJiraIssueTypesSuccess', data.issuetypes); toast(s__('Integrations|Connection successful.')); } }) |