diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-20 12:26:25 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-07-20 12:26:25 +0000 |
commit | a09983ae35713f5a2bbb100981116d31ce99826e (patch) | |
tree | 2ee2af7bd104d57086db360a7e6d8c9d5d43667a /app/assets/javascripts/ci_variable_list | |
parent | 18c5ab32b738c0b6ecb4d0df3994000482f34bd8 (diff) | |
download | gitlab-ce-a09983ae35713f5a2bbb100981116d31ce99826e.tar.gz |
Add latest changes from gitlab-org/gitlab@13-2-stable-ee
Diffstat (limited to 'app/assets/javascripts/ci_variable_list')
8 files changed, 102 insertions, 239 deletions
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_key_field.vue b/app/assets/javascripts/ci_variable_list/components/ci_key_field.vue deleted file mode 100644 index c15d638d92b..00000000000 --- a/app/assets/javascripts/ci_variable_list/components/ci_key_field.vue +++ /dev/null @@ -1,169 +0,0 @@ -<script> -import { uniqueId } from 'lodash'; -import { GlButton, GlFormGroup, GlFormInput } from '@gitlab/ui'; - -export default { - name: 'CiKeyField', - components: { - GlButton, - GlFormGroup, - GlFormInput, - }, - model: { - prop: 'value', - event: 'input', - }, - props: { - tokenList: { - type: Array, - required: true, - }, - value: { - type: String, - required: true, - }, - }, - data() { - return { - results: [], - arrowCounter: -1, - userDismissedResults: false, - suggestionsId: uniqueId('token-suggestions-'), - }; - }, - computed: { - showAutocomplete() { - return this.showSuggestions ? 'off' : 'on'; - }, - showSuggestions() { - return this.results.length > 0; - }, - }, - mounted() { - document.addEventListener('click', this.handleClickOutside); - }, - destroyed() { - document.removeEventListener('click', this.handleClickOutside); - }, - methods: { - closeSuggestions() { - this.results = []; - this.arrowCounter = -1; - }, - handleClickOutside(event) { - if (!this.$el.contains(event.target)) { - this.closeSuggestions(); - } - }, - onArrowDown() { - const newCount = this.arrowCounter + 1; - - if (newCount >= this.results.length) { - this.arrowCounter = 0; - return; - } - - this.arrowCounter = newCount; - }, - onArrowUp() { - const newCount = this.arrowCounter - 1; - - if (newCount < 0) { - this.arrowCounter = this.results.length - 1; - return; - } - - this.arrowCounter = newCount; - }, - onEnter() { - const currentToken = this.results[this.arrowCounter] || this.value; - this.selectToken(currentToken); - }, - onEsc() { - if (!this.showSuggestions) { - this.$emit('input', ''); - } - this.closeSuggestions(); - this.userDismissedResults = true; - }, - onEntry(value) { - this.$emit('input', value); - this.userDismissedResults = false; - - // short circuit so that we don't false match on empty string - if (value.length < 1) { - this.closeSuggestions(); - return; - } - - const filteredTokens = this.tokenList.filter(token => - token.toLowerCase().includes(value.toLowerCase()), - ); - - if (filteredTokens.length) { - this.openSuggestions(filteredTokens); - } else { - this.closeSuggestions(); - } - }, - openSuggestions(filteredResults) { - this.results = filteredResults; - }, - selectToken(value) { - this.$emit('input', value); - this.closeSuggestions(); - this.$emit('key-selected'); - }, - }, -}; -</script> -<template> - <div> - <div class="dropdown position-relative" role="combobox" aria-owns="token-suggestions"> - <gl-form-group :label="__('Key')" label-for="ci-variable-key"> - <gl-form-input - id="ci-variable-key" - :value="value" - type="text" - role="searchbox" - class="form-control pl-2 js-env-input" - :autocomplete="showAutocomplete" - aria-autocomplete="list" - aria-controls="token-suggestions" - aria-haspopup="listbox" - :aria-expanded="showSuggestions" - data-qa-selector="ci_variable_key_field" - @input="onEntry" - @keydown.down="onArrowDown" - @keydown.up="onArrowUp" - @keydown.enter.prevent="onEnter" - @keydown.esc.stop="onEsc" - @keydown.tab="closeSuggestions" - /> - </gl-form-group> - - <div - v-show="showSuggestions && !userDismissedResults" - id="ci-variable-dropdown" - class="dropdown-menu dropdown-menu-selectable dropdown-menu-full-width" - :class="{ 'd-block': showSuggestions }" - > - <div class="dropdown-content"> - <ul :id="suggestionsId"> - <li - v-for="(result, i) in results" - :key="i" - role="option" - :class="{ 'gl-bg-gray-50': i === arrowCounter }" - :aria-selected="i === arrowCounter" - > - <gl-button tabindex="-1" class="btn-transparent pl-2" @click="selectToken(result)">{{ - result - }}</gl-button> - </li> - </ul> - </div> - </div> - </div> - </div> -</template> diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_autocomplete_tokens.js b/app/assets/javascripts/ci_variable_list/components/ci_variable_autocomplete_tokens.js index 9022bf51514..3f25e3df305 100644 --- a/app/assets/javascripts/ci_variable_list/components/ci_variable_autocomplete_tokens.js +++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_autocomplete_tokens.js @@ -1,28 +1,14 @@ -import { __ } from '~/locale'; - import { AWS_ACCESS_KEY_ID, AWS_DEFAULT_REGION, AWS_SECRET_ACCESS_KEY } from '../constants'; export const awsTokens = { [AWS_ACCESS_KEY_ID]: { name: AWS_ACCESS_KEY_ID, - /* Checks for exactly twenty characters that match key. - Based on greps suggested by Amazon at: - https://aws.amazon.com/blogs/security/a-safer-way-to-distribute-aws-credentials-to-ec2/ - */ - validation: val => /^[A-Za-z0-9]{20}$/.test(val), - invalidMessage: __('This variable does not match the expected pattern.'), }, [AWS_DEFAULT_REGION]: { name: AWS_DEFAULT_REGION, }, [AWS_SECRET_ACCESS_KEY]: { name: AWS_SECRET_ACCESS_KEY, - /* Checks for exactly forty characters that match secret. - Based on greps suggested by Amazon at: - https://aws.amazon.com/blogs/security/a-safer-way-to-distribute-aws-credentials-to-ec2/ - */ - validation: val => /^[A-Za-z0-9/+=]{40}$/.test(val), - invalidMessage: __('This variable does not match the expected pattern.'), }, }; diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue index 6531b945212..0ba58430de1 100644 --- a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue +++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue @@ -5,6 +5,7 @@ import { GlCollapse, GlDeprecatedButton, GlFormCheckbox, + GlFormCombobox, GlFormGroup, GlFormInput, GlFormSelect, @@ -16,6 +17,7 @@ import { } from '@gitlab/ui'; import Cookies from 'js-cookie'; import { mapActions, mapState } from 'vuex'; +import { mapComputed } from '~/vuex_shared/bindings'; import { __ } from '~/locale'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { @@ -25,19 +27,21 @@ import { AWS_TIP_MESSAGE, } from '../constants'; import { awsTokens, awsTokenList } from './ci_variable_autocomplete_tokens'; -import CiKeyField from './ci_key_field.vue'; import CiEnvironmentsDropdown from './ci_environments_dropdown.vue'; export default { modalId: ADD_CI_VARIABLE_MODAL_ID, + tokens: awsTokens, + tokenList: awsTokenList, + awsTipMessage: AWS_TIP_MESSAGE, components: { CiEnvironmentsDropdown, - CiKeyField, GlAlert, GlButton, GlCollapse, GlDeprecatedButton, GlFormCheckbox, + GlFormCombobox, GlFormGroup, GlFormInput, GlFormSelect, @@ -48,9 +52,6 @@ export default { GlSprintf, }, mixins: [glFeatureFlagsMixin()], - tokens: awsTokens, - tokenList: awsTokenList, - awsTipMessage: AWS_TIP_MESSAGE, data() { return { isTipDismissed: Cookies.get(AWS_TIP_DISMISSED_COOKIE_NAME) === 'true', @@ -74,22 +75,34 @@ export default { 'protectedEnvironmentVariablesLink', 'maskedEnvironmentVariablesLink', ]), + ...mapComputed( + [ + { key: 'key', updateFn: 'updateVariableKey' }, + { key: 'secret_value', updateFn: 'updateVariableValue' }, + { key: 'variable_type', updateFn: 'updateVariableType' }, + { key: 'environment_scope', updateFn: 'setEnvironmentScope' }, + { key: 'protected_variable', updateFn: 'updateVariableProtected' }, + { key: 'masked', updateFn: 'updateVariableMasked' }, + ], + false, + 'variable', + ), isTipVisible() { - return !this.isTipDismissed && AWS_TOKEN_CONSTANTS.includes(this.variableData.key); + return !this.isTipDismissed && AWS_TOKEN_CONSTANTS.includes(this.variable.key); }, canSubmit() { return ( this.variableValidationState && - this.variableData.key !== '' && - this.variableData.secret_value !== '' + this.variable.key !== '' && + this.variable.secret_value !== '' ); }, canMask() { const regex = RegExp(this.maskableRegex); - return regex.test(this.variableData.secret_value); + return regex.test(this.variable.secret_value); }, displayMaskedError() { - return !this.canMask && this.variableData.masked; + return !this.canMask && this.variable.masked; }, maskedState() { if (this.displayMaskedError) { @@ -97,9 +110,6 @@ export default { } return true; }, - variableData() { - return this.variableBeingEdited || this.variable; - }, modalActionText() { return this.variableBeingEdited ? __('Update variable') : __('Add variable'); }, @@ -107,7 +117,7 @@ export default { return this.displayMaskedError ? __('This variable can not be masked.') : ''; }, tokenValidationFeedback() { - const tokenSpecificFeedback = this.$options.tokens?.[this.variableData.key]?.invalidMessage; + const tokenSpecificFeedback = this.$options.tokens?.[this.variable.key]?.invalidMessage; if (!this.tokenValidationState && tokenSpecificFeedback) { return tokenSpecificFeedback; } @@ -119,10 +129,10 @@ export default { return true; } - const validator = this.$options.tokens?.[this.variableData.key]?.validation; + const validator = this.$options.tokens?.[this.variable.key]?.validation; if (validator) { - return validator(this.variableData.secret_value); + return validator(this.variable.secret_value); } return true; @@ -131,14 +141,7 @@ export default { return `${this.tokenValidationFeedback} ${this.maskedFeedback}`; }, variableValidationState() { - if ( - this.variableData.secret_value === '' || - (this.tokenValidationState && this.maskedState) - ) { - return true; - } - - return false; + return this.variable.secret_value === '' || (this.tokenValidationState && this.maskedState); }, }, methods: { @@ -160,7 +163,7 @@ export default { this.isTipDismissed = true; }, deleteVarAndClose() { - this.deleteVariable(this.variableBeingEdited); + this.deleteVariable(); this.hideModal(); }, hideModal() { @@ -169,14 +172,14 @@ export default { resetModalHandler() { if (this.variableBeingEdited) { this.resetEditing(); - } else { - this.clearModal(); } + + this.clearModal(); this.resetSelectedEnvironment(); }, updateOrAddVariable() { if (this.variableBeingEdited) { - this.updateVariable(this.variableBeingEdited); + this.updateVariable(); } else { this.addVariable(); } @@ -202,16 +205,17 @@ export default { @shown="setVariableProtectedByDefault" > <form> - <ci-key-field + <gl-form-combobox v-if="glFeatures.ciKeyAutocomplete" - v-model="variableData.key" + v-model="key" :token-list="$options.tokenList" + :label-text="__('Key')" /> <gl-form-group v-else :label="__('Key')" label-for="ci-variable-key"> <gl-form-input id="ci-variable-key" - v-model="variableData.key" + v-model="key" data-qa-selector="ci_variable_key_field" /> </gl-form-group> @@ -225,11 +229,12 @@ export default { <gl-form-textarea id="ci-variable-value" ref="valueField" - v-model="variableData.secret_value" + v-model="secret_value" :state="variableValidationState" rows="3" max-rows="6" data-qa-selector="ci_variable_value_field" + class="gl-font-monospace!" /> </gl-form-group> @@ -240,11 +245,7 @@ export default { class="w-50 append-right-15" :class="{ 'w-100': isGroup }" > - <gl-form-select - id="ci-variable-type" - v-model="variableData.variable_type" - :options="typeOptions" - /> + <gl-form-select id="ci-variable-type" v-model="variable_type" :options="typeOptions" /> </gl-form-group> <gl-form-group @@ -255,7 +256,7 @@ export default { > <ci-environments-dropdown class="w-100" - :value="variableData.environment_scope" + :value="environment_scope" @selectEnvironment="setEnvironmentScope" @createClicked="addWildCardScope" /> @@ -263,7 +264,7 @@ export default { </div> <gl-form-group :label="__('Flags')" label-for="ci-variable-flags"> - <gl-form-checkbox v-model="variableData.protected" class="mb-0"> + <gl-form-checkbox v-model="protected_variable" class="mb-0"> {{ __('Protect variable') }} <gl-link target="_blank" :href="protectedEnvironmentVariablesLink"> <gl-icon name="question" :size="12" /> @@ -275,7 +276,7 @@ export default { <gl-form-checkbox ref="masked-ci-variable" - v-model="variableData.masked" + v-model="masked" data-qa-selector="ci_variable_masked_checkbox" > {{ __('Mask variable') }} diff --git a/app/assets/javascripts/ci_variable_list/store/actions.js b/app/assets/javascripts/ci_variable_list/store/actions.js index d9129c919f8..60c7a480769 100644 --- a/app/assets/javascripts/ci_variable_list/store/actions.js +++ b/app/assets/javascripts/ci_variable_list/store/actions.js @@ -65,10 +65,10 @@ export const receiveUpdateVariableError = ({ commit }, error) => { commit(types.RECEIVE_UPDATE_VARIABLE_ERROR, error); }; -export const updateVariable = ({ state, dispatch }, variable) => { +export const updateVariable = ({ state, dispatch }) => { dispatch('requestUpdateVariable'); - const updatedVariable = prepareDataForApi(variable); + const updatedVariable = prepareDataForApi(state.variable); updatedVariable.secrect_value = updateVariable.value; return axios @@ -121,13 +121,13 @@ export const receiveDeleteVariableError = ({ commit }, error) => { commit(types.RECEIVE_DELETE_VARIABLE_ERROR, error); }; -export const deleteVariable = ({ dispatch, state }, variable) => { +export const deleteVariable = ({ dispatch, state }) => { dispatch('requestDeleteVariable'); const destroy = true; return axios - .patch(state.endpoint, { variables_attributes: [prepareDataForApi(variable, destroy)] }) + .patch(state.endpoint, { variables_attributes: [prepareDataForApi(state.variable, destroy)] }) .then(() => { dispatch('receiveDeleteVariableSuccess'); dispatch('fetchVariables'); @@ -176,3 +176,23 @@ export const resetSelectedEnvironment = ({ commit }) => { export const setSelectedEnvironment = ({ commit }, environment) => { commit(types.SET_SELECTED_ENVIRONMENT, environment); }; + +export const updateVariableKey = ({ commit }, { key }) => { + commit(types.UPDATE_VARIABLE_KEY, key); +}; + +export const updateVariableValue = ({ commit }, { secret_value }) => { + commit(types.UPDATE_VARIABLE_VALUE, secret_value); +}; + +export const updateVariableType = ({ commit }, { variable_type }) => { + commit(types.UPDATE_VARIABLE_TYPE, variable_type); +}; + +export const updateVariableProtected = ({ commit }, { protected_variable }) => { + commit(types.UPDATE_VARIABLE_PROTECTED, protected_variable); +}; + +export const updateVariableMasked = ({ commit }, { masked }) => { + commit(types.UPDATE_VARIABLE_MASKED, masked); +}; diff --git a/app/assets/javascripts/ci_variable_list/store/mutation_types.js b/app/assets/javascripts/ci_variable_list/store/mutation_types.js index ccf8fbd3cb5..5db8f610192 100644 --- a/app/assets/javascripts/ci_variable_list/store/mutation_types.js +++ b/app/assets/javascripts/ci_variable_list/store/mutation_types.js @@ -25,3 +25,9 @@ export const SET_ENVIRONMENT_SCOPE = 'SET_ENVIRONMENT_SCOPE'; export const ADD_WILD_CARD_SCOPE = 'ADD_WILD_CARD_SCOPE'; export const RESET_SELECTED_ENVIRONMENT = 'RESET_SELECTED_ENVIRONMENT'; export const SET_SELECTED_ENVIRONMENT = 'SET_SELECTED_ENVIRONMENT'; + +export const UPDATE_VARIABLE_KEY = 'UPDATE_VARIABLE_KEY'; +export const UPDATE_VARIABLE_VALUE = 'UPDATE_VARIABLE_VALUE'; +export const UPDATE_VARIABLE_TYPE = 'UPDATE_VARIABLE_TYPE'; +export const UPDATE_VARIABLE_PROTECTED = 'UPDATE_VARIABLE_PROTECTED'; +export const UPDATE_VARIABLE_MASKED = 'UPDATE_VARIABLE_MASKED'; diff --git a/app/assets/javascripts/ci_variable_list/store/mutations.js b/app/assets/javascripts/ci_variable_list/store/mutations.js index 7d9cd0dd727..961cecee298 100644 --- a/app/assets/javascripts/ci_variable_list/store/mutations.js +++ b/app/assets/javascripts/ci_variable_list/store/mutations.js @@ -65,7 +65,8 @@ export default { }, [types.VARIABLE_BEING_EDITED](state, variable) { - state.variableBeingEdited = variable; + state.variableBeingEdited = true; + state.variable = variable; }, [types.CLEAR_MODAL](state) { @@ -73,23 +74,19 @@ export default { variable_type: displayText.variableText, key: '', secret_value: '', - protected: false, + protected_variable: false, masked: false, environment_scope: displayText.allEnvironmentsText, }; }, [types.RESET_EDITING](state) { - state.variableBeingEdited = null; + state.variableBeingEdited = false; state.showInputValue = false; }, [types.SET_ENVIRONMENT_SCOPE](state, environment) { - if (state.variableBeingEdited) { - state.variableBeingEdited.environment_scope = environment; - } else { - state.variable.environment_scope = environment; - } + state.variable.environment_scope = environment; }, [types.ADD_WILD_CARD_SCOPE](state, environment) { @@ -106,6 +103,26 @@ export default { }, [types.SET_VARIABLE_PROTECTED](state) { - state.variable.protected = true; + state.variable.protected_variable = true; + }, + + [types.UPDATE_VARIABLE_KEY](state, key) { + state.variable.key = key; + }, + + [types.UPDATE_VARIABLE_VALUE](state, value) { + state.variable.secret_value = value; + }, + + [types.UPDATE_VARIABLE_TYPE](state, type) { + state.variable.variable_type = type; + }, + + [types.UPDATE_VARIABLE_PROTECTED](state, bool) { + state.variable.protected_variable = bool; + }, + + [types.UPDATE_VARIABLE_MASKED](state, bool) { + state.variable.masked = bool; }, }; diff --git a/app/assets/javascripts/ci_variable_list/store/state.js b/app/assets/javascripts/ci_variable_list/store/state.js index 2fffd115589..96b27792664 100644 --- a/app/assets/javascripts/ci_variable_list/store/state.js +++ b/app/assets/javascripts/ci_variable_list/store/state.js @@ -12,7 +12,7 @@ export default () => ({ variable_type: displayText.variableText, key: '', secret_value: '', - protected: false, + protected_variable: false, masked: false, environment_scope: displayText.allEnvironmentsText, }, @@ -21,6 +21,6 @@ export default () => ({ error: null, environments: [], typeOptions: [displayText.variableText, displayText.fileText], - variableBeingEdited: null, + variableBeingEdited: false, selectedEnvironment: '', }); diff --git a/app/assets/javascripts/ci_variable_list/store/utils.js b/app/assets/javascripts/ci_variable_list/store/utils.js index 3cd8c85024b..f04530359e7 100644 --- a/app/assets/javascripts/ci_variable_list/store/utils.js +++ b/app/assets/javascripts/ci_variable_list/store/utils.js @@ -18,6 +18,7 @@ export const prepareDataForDisplay = variables => { if (variableCopy.environment_scope === types.allEnvironmentsType) { variableCopy.environment_scope = displayText.allEnvironmentsText; } + variableCopy.protected_variable = variableCopy.protected; variablesToDisplay.push(variableCopy); }); return variablesToDisplay; @@ -25,7 +26,8 @@ export const prepareDataForDisplay = variables => { export const prepareDataForApi = (variable, destroy = false) => { const variableCopy = cloneDeep(variable); - variableCopy.protected = variableCopy.protected.toString(); + variableCopy.protected = variableCopy.protected_variable.toString(); + delete variableCopy.protected_variable; variableCopy.masked = variableCopy.masked.toString(); variableCopy.variable_type = variableTypeHandler(variableCopy.variable_type); if (variableCopy.environment_scope === displayText.allEnvironmentsText) { |