diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-05 21:08:51 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-05 21:08:51 +0300 |
commit | ec18750aa8538712b952b8265581fc3b3e037923 (patch) | |
tree | 7eaf59fe40401415fde3c994bffae0031e2ffd23 /app/assets/javascripts/feature_flags | |
parent | 61265b9f01c7db3d4f6e1266d165b1c85be7b9e7 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/feature_flags')
12 files changed, 313 insertions, 165 deletions
diff --git a/app/assets/javascripts/feature_flags/components/feature_flags_table.vue b/app/assets/javascripts/feature_flags/components/feature_flags_table.vue index 7881ae523fc..c4e2be50d34 100644 --- a/app/assets/javascripts/feature_flags/components/feature_flags_table.vue +++ b/app/assets/javascripts/feature_flags/components/feature_flags_table.vue @@ -3,7 +3,7 @@ import { GlBadge, GlButton, GlTooltipDirective, GlModal, GlToggle, GlIcon } from import { sprintf, s__ } from '~/locale'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { ROLLOUT_STRATEGY_PERCENT_ROLLOUT, NEW_VERSION_FLAG, LEGACY_FLAG } from '../constants'; -import labelForStrategy from '../utils'; +import { labelForStrategy } from '../utils'; export default { components: { diff --git a/app/assets/javascripts/feature_flags/components/form.vue b/app/assets/javascripts/feature_flags/components/form.vue index 04bea2d80d4..8d065933316 100644 --- a/app/assets/javascripts/feature_flags/components/form.vue +++ b/app/assets/javascripts/feature_flags/components/form.vue @@ -477,7 +477,7 @@ export default { <label class="sr-only" :for="rolloutPercentageId(index)"> {{ s__('FeatureFlags|Rollout Percentage') }} </label> - <div class="w-3rem"> + <div class="gl-w-9"> <input :id="rolloutPercentageId(index)" v-model="scope.rolloutPercentage" diff --git a/app/assets/javascripts/feature_flags/components/strategies/default.vue b/app/assets/javascripts/feature_flags/components/strategies/default.vue new file mode 100644 index 00000000000..cb8ffbddfbd --- /dev/null +++ b/app/assets/javascripts/feature_flags/components/strategies/default.vue @@ -0,0 +1,10 @@ +<script> +export default { + mounted() { + this.$emit('change', { parameters: {} }); + }, + render() { + return this.$slots.default; + }, +}; +</script> diff --git a/app/assets/javascripts/feature_flags/components/strategies/gitlab_user_list.vue b/app/assets/javascripts/feature_flags/components/strategies/gitlab_user_list.vue new file mode 100644 index 00000000000..b13bd86e900 --- /dev/null +++ b/app/assets/javascripts/feature_flags/components/strategies/gitlab_user_list.vue @@ -0,0 +1,63 @@ +<script> +import { GlFormSelect } from '@gitlab/ui'; +import { s__ } from '~/locale'; +import ParameterFormGroup from './parameter_form_group.vue'; + +export default { + components: { + GlFormSelect, + ParameterFormGroup, + }, + props: { + strategy: { + required: true, + type: Object, + }, + userLists: { + required: false, + type: Array, + default: () => [], + }, + }, + translations: { + rolloutUserListLabel: s__('FeatureFlag|List'), + rolloutUserListDescription: s__('FeatureFlag|Select a user list'), + rolloutUserListNoListError: s__('FeatureFlag|There are no configured user lists'), + }, + computed: { + userListOptions() { + return this.userLists.map(({ name, id }) => ({ value: id, text: name })); + }, + hasUserLists() { + return this.userListOptions.length > 0; + }, + userListId() { + return this.strategy?.userListId ?? ''; + }, + }, + methods: { + onUserListChange(list) { + this.$emit('change', { + userListId: list, + }); + }, + }, +}; +</script> +<template> + <parameter-form-group + :state="hasUserLists" + :invalid-feedback="$options.translations.rolloutUserListNoListError" + :label="$options.translations.rolloutUserListLabel" + :description="$options.translations.rolloutUserListDescription" + > + <template #default="{ inputId }"> + <gl-form-select + :id="inputId" + :value="userListId" + :options="userListOptions" + @change="onUserListChange" + /> + </template> + </parameter-form-group> +</template> diff --git a/app/assets/javascripts/feature_flags/components/strategies/parameter_form_group.vue b/app/assets/javascripts/feature_flags/components/strategies/parameter_form_group.vue new file mode 100644 index 00000000000..7f2c6d55db8 --- /dev/null +++ b/app/assets/javascripts/feature_flags/components/strategies/parameter_form_group.vue @@ -0,0 +1,22 @@ +<script> +import { uniqueId } from 'lodash'; +import { GlFormGroup } from '@gitlab/ui'; + +export default { + components: { + GlFormGroup, + }, + props: { + inputId: { + required: false, + type: String, + default: () => uniqueId('feature_flag_strategies_'), + }, + }, +}; +</script> +<template> + <gl-form-group :label-for="inputId" v-bind="$attrs"> + <slot v-bind="{ inputId }"></slot> + </gl-form-group> +</template> diff --git a/app/assets/javascripts/feature_flags/components/strategies/percent_rollout.vue b/app/assets/javascripts/feature_flags/components/strategies/percent_rollout.vue new file mode 100644 index 00000000000..9311589c364 --- /dev/null +++ b/app/assets/javascripts/feature_flags/components/strategies/percent_rollout.vue @@ -0,0 +1,68 @@ +<script> +import { GlFormInput } from '@gitlab/ui'; +import { s__, __ } from '~/locale'; +import { PERCENT_ROLLOUT_GROUP_ID } from '../../constants'; +import ParameterFormGroup from './parameter_form_group.vue'; + +export default { + components: { + GlFormInput, + ParameterFormGroup, + }, + props: { + strategy: { + required: true, + type: Object, + }, + }, + translations: { + rolloutPercentageDescription: __('Enter a whole number between 0 and 100'), + rolloutPercentageInvalid: s__( + 'FeatureFlags|Percent rollout must be a whole number between 0 and 100', + ), + rolloutPercentageLabel: s__('FeatureFlag|Percentage'), + }, + computed: { + isValid() { + return Number(this.percentage) >= 0 && Number(this.percentage) <= 100; + }, + percentage() { + return this.strategy?.parameters?.percentage ?? ''; + }, + }, + methods: { + onPercentageChange(value) { + this.$emit('change', { + parameters: { + percentage: value, + groupId: PERCENT_ROLLOUT_GROUP_ID, + }, + }); + }, + }, +}; +</script> +<template> + <parameter-form-group + :label="$options.translations.rolloutPercentageLabel" + :description="$options.translations.rolloutPercentageDescription" + :invalid-feedback="$options.translations.rolloutPercentageInvalid" + :state="isValid" + > + <template #default="{ inputId }"> + <div class="gl-display-flex gl-align-items-center"> + <gl-form-input + :id="inputId" + :value="percentage" + :state="isValid" + class="rollout-percentage gl-text-right gl-w-9" + type="number" + min="0" + max="100" + @input="onPercentageChange" + /> + <span class="gl-ml-2">%</span> + </div> + </template> + </parameter-form-group> +</template> diff --git a/app/assets/javascripts/feature_flags/components/strategies/users_with_id.vue b/app/assets/javascripts/feature_flags/components/strategies/users_with_id.vue new file mode 100644 index 00000000000..094127fb710 --- /dev/null +++ b/app/assets/javascripts/feature_flags/components/strategies/users_with_id.vue @@ -0,0 +1,47 @@ +<script> +import { GlFormTextarea } from '@gitlab/ui'; +import { __, s__ } from '~/locale'; + +import ParameterFormGroup from './parameter_form_group.vue'; + +export default { + components: { + ParameterFormGroup, + GlFormTextarea, + }, + props: { + strategy: { + required: true, + type: Object, + }, + }, + translations: { + rolloutUserIdsDescription: __('Enter one or more user ID separated by commas'), + rolloutUserIdsLabel: s__('FeatureFlag|User IDs'), + }, + computed: { + userIds() { + return this.strategy?.parameters?.userIds ?? ''; + }, + }, + methods: { + onUserIdsChange(value) { + this.$emit('change', { + parameters: { + userIds: value, + }, + }); + }, + }, +}; +</script> +<template> + <parameter-form-group + :label="$options.translations.rolloutUserIdsLabel" + :description="$options.translations.rolloutUserIdsDescription" + > + <template #default="{ inputId }"> + <gl-form-textarea :id="inputId" :value="userIds" @input="onUserIdsChange" /> + </template> + </parameter-form-group> +</template> diff --git a/app/assets/javascripts/feature_flags/components/strategy.vue b/app/assets/javascripts/feature_flags/components/strategy.vue index 3f10ec00aa5..c83e2c897e3 100644 --- a/app/assets/javascripts/feature_flags/components/strategy.vue +++ b/app/assets/javascripts/feature_flags/components/strategy.vue @@ -1,42 +1,23 @@ <script> import Vue from 'vue'; import { isNumber } from 'lodash'; -import { - GlButton, - GlFormSelect, - GlFormInput, - GlFormTextarea, - GlFormGroup, - GlIcon, - GlLink, - GlToken, -} from '@gitlab/ui'; +import { GlButton, GlFormSelect, GlFormGroup, GlIcon, GlLink, GlToken } from '@gitlab/ui'; import { s__, __ } from '~/locale'; -import { - PERCENT_ROLLOUT_GROUP_ID, - ROLLOUT_STRATEGY_ALL_USERS, - ROLLOUT_STRATEGY_PERCENT_ROLLOUT, - ROLLOUT_STRATEGY_USER_ID, - ROLLOUT_STRATEGY_GITLAB_USER_LIST, -} from '../constants'; +import { EMPTY_PARAMETERS, STRATEGY_SELECTIONS } from '../constants'; import NewEnvironmentsDropdown from './new_environments_dropdown.vue'; +import StrategyParameters from './strategy_parameters.vue'; export default { components: { GlButton, GlFormGroup, - GlFormInput, - GlFormTextarea, GlFormSelect, GlIcon, GlLink, GlToken, NewEnvironmentsDropdown, - }, - model: { - prop: 'strategy', - event: 'change', + StrategyParameters, }, inject: { strategyTypeDocsPagePath: { @@ -66,86 +47,35 @@ export default { default: () => [], }, }, - ROLLOUT_STRATEGY_ALL_USERS, - ROLLOUT_STRATEGY_PERCENT_ROLLOUT, - ROLLOUT_STRATEGY_USER_ID, - ROLLOUT_STRATEGY_GITLAB_USER_LIST, i18n: { allEnvironments: __('All environments'), environmentsLabel: __('Environments'), - environmentsSelectDescription: __('Select the environment scope for this feature flag.'), - rolloutPercentageDescription: __('Enter a whole number between 0 and 100'), - rolloutPercentageInvalid: s__( - 'FeatureFlags|Percent rollout must be a whole number between 0 and 100', - ), - rolloutPercentageLabel: s__('FeatureFlag|Percentage'), - rolloutUserIdsDescription: __('Enter one or more user ID separated by commas'), - rolloutUserIdsLabel: s__('FeatureFlag|User IDs'), rolloutUserListLabel: s__('FeatureFlag|List'), rolloutUserListDescription: s__('FeatureFlag|Select a user list'), rolloutUserListNoListError: s__('FeatureFlag|There are no configured user lists'), strategyTypeDescription: __('Select strategy activation method.'), strategyTypeLabel: s__('FeatureFlag|Type'), + environmentsSelectDescription: s__( + 'FeatureFlag|Select the environment scope for this feature flag.', + ), }, + strategies: STRATEGY_SELECTIONS, + data() { return { environments: this.strategy.scopes || [], formStrategy: { ...this.strategy }, - formPercentage: - this.strategy.name === ROLLOUT_STRATEGY_PERCENT_ROLLOUT - ? this.strategy.parameters.percentage - : '', - formUserIds: - this.strategy.name === ROLLOUT_STRATEGY_USER_ID ? this.strategy.parameters.userIds : '', - formUserListId: - this.strategy.name === ROLLOUT_STRATEGY_GITLAB_USER_LIST ? this.strategy.userListId : '', - strategies: [ - { - value: ROLLOUT_STRATEGY_ALL_USERS, - text: __('All users'), - }, - { - value: ROLLOUT_STRATEGY_PERCENT_ROLLOUT, - text: __('Percent of users'), - }, - { - value: ROLLOUT_STRATEGY_USER_ID, - text: __('User IDs'), - }, - { - value: ROLLOUT_STRATEGY_GITLAB_USER_LIST, - text: __('User List'), - }, - ], }; }, computed: { strategyTypeId() { return `strategy-type-${this.index}`; }, - strategyPercentageId() { - return `strategy-percentage-${this.index}`; - }, - strategyUserIdsId() { - return `strategy-user-ids-${this.index}`; - }, - strategyUserListId() { - return `strategy-user-list-${this.index}`; - }, environmentsDropdownId() { return `environments-dropdown-${this.index}`; }, - isPercentRollout() { - return this.isStrategyType(ROLLOUT_STRATEGY_PERCENT_ROLLOUT); - }, - isUserWithId() { - return this.isStrategyType(ROLLOUT_STRATEGY_USER_ID); - }, - isUserList() { - return this.isStrategyType(ROLLOUT_STRATEGY_GITLAB_USER_LIST); - }, appliesToAllEnvironments() { return ( this.filteredEnvironments.length === 1 && @@ -155,12 +85,6 @@ export default { filteredEnvironments() { return this.environments.filter(e => !e.shouldBeDestroyed); }, - userListOptions() { - return this.userLists.map(({ name, id }) => ({ value: id, text: name })); - }, - hasUserLists() { - return this.userListOptions.length > 0; - }, }, methods: { addEnvironment(environment) { @@ -169,33 +93,19 @@ export default { allEnvironmentsScope.shouldBeDestroyed = true; } this.environments.push({ environmentScope: environment }); - this.onStrategyChange(); + this.onStrategyChange({ ...this.formStrategy, scopes: this.environments }); }, - onStrategyChange() { - const parameters = {}; - const strategy = { + onStrategyTypeChange(name) { + this.onStrategyChange({ ...this.formStrategy, - scopes: this.environments, - }; - switch (this.formStrategy.name) { - case ROLLOUT_STRATEGY_PERCENT_ROLLOUT: - parameters.percentage = this.formPercentage; - parameters.groupId = PERCENT_ROLLOUT_GROUP_ID; - break; - case ROLLOUT_STRATEGY_USER_ID: - parameters.userIds = this.formUserIds; - break; - case ROLLOUT_STRATEGY_GITLAB_USER_LIST: - strategy.userListId = this.formUserListId; - break; - default: - break; - } - this.$emit('change', { - ...strategy, - parameters, + ...EMPTY_PARAMETERS, + name, }); }, + onStrategyChange(s) { + this.$emit('change', s); + this.formStrategy = s; + }, removeScope(environment) { if (isNumber(environment.id)) { Vue.set(environment, 'shouldBeDestroyed', true); @@ -205,10 +115,7 @@ export default { if (this.filteredEnvironments.length === 0) { this.environments.push({ environmentScope: '*' }); } - this.onStrategyChange(); - }, - isStrategyType(type) { - return this.formStrategy.name === type; + this.onStrategyChange({ ...this.formStrategy, scopes: this.environments }); }, }, }; @@ -224,60 +131,19 @@ export default { </gl-link> <gl-form-select :id="strategyTypeId" - v-model="formStrategy.name" - :options="strategies" - @change="onStrategyChange" + :value="formStrategy.name" + :options="$options.strategies" + @change="onStrategyTypeChange" /> </gl-form-group> </div> <div data-testid="strategy"> - <gl-form-group - v-if="isPercentRollout" - :label="$options.i18n.rolloutPercentageLabel" - :description="$options.i18n.rolloutPercentageDescription" - :label-for="strategyPercentageId" - :invalid-feedback="$options.i18n.rolloutPercentageInvalid" - > - <div class="gl-display-flex gl-align-items-center"> - <gl-form-input - :id="strategyPercentageId" - v-model="formPercentage" - class="rollout-percentage gl-text-right gl-w-9" - type="number" - @input="onStrategyChange" - /> - <span class="gl-ml-2">%</span> - </div> - </gl-form-group> - - <gl-form-group - v-if="isUserWithId" - :label="$options.i18n.rolloutUserIdsLabel" - :description="$options.i18n.rolloutUserIdsDescription" - :label-for="strategyUserIdsId" - > - <gl-form-textarea - :id="strategyUserIdsId" - v-model="formUserIds" - @input="onStrategyChange" - /> - </gl-form-group> - <gl-form-group - v-if="isUserList" - :state="hasUserLists" - :invalid-feedback="$options.i18n.rolloutUserListNoListError" - :label="$options.i18n.rolloutUserListLabel" - :description="$options.i18n.rolloutUserListDescription" - :label-for="strategyUserListId" - > - <gl-form-select - :id="strategyUserListId" - v-model="formUserListId" - :options="userListOptions" - @change="onStrategyChange" - /> - </gl-form-group> + <strategy-parameters + :strategy="strategy" + :user-lists="userLists" + @change="onStrategyChange" + /> </div> <div diff --git a/app/assets/javascripts/feature_flags/components/strategy_parameters.vue b/app/assets/javascripts/feature_flags/components/strategy_parameters.vue new file mode 100644 index 00000000000..6953095daff --- /dev/null +++ b/app/assets/javascripts/feature_flags/components/strategy_parameters.vue @@ -0,0 +1,51 @@ +<script> +import { + ROLLOUT_STRATEGY_ALL_USERS, + ROLLOUT_STRATEGY_PERCENT_ROLLOUT, + ROLLOUT_STRATEGY_USER_ID, + ROLLOUT_STRATEGY_GITLAB_USER_LIST, +} from '../constants'; + +import Default from './strategies/default.vue'; +import PercentRollout from './strategies/percent_rollout.vue'; +import UsersWithId from './strategies/users_with_id.vue'; +import GitlabUserList from './strategies/gitlab_user_list.vue'; + +const STRATEGIES = Object.freeze({ + [ROLLOUT_STRATEGY_ALL_USERS]: Default, + [ROLLOUT_STRATEGY_PERCENT_ROLLOUT]: PercentRollout, + [ROLLOUT_STRATEGY_USER_ID]: UsersWithId, + [ROLLOUT_STRATEGY_GITLAB_USER_LIST]: GitlabUserList, +}); + +export default { + props: { + strategy: { + type: Object, + required: true, + }, + }, + computed: { + strategyComponent() { + return STRATEGIES[(this.strategy?.name)]; + }, + }, + methods: { + onChange(value) { + this.$emit('change', { + ...this.strategy, + ...value, + }); + }, + }, +}; +</script> +<template> + <component + :is="strategyComponent" + v-if="strategyComponent" + :strategy="strategy" + v-bind="$attrs" + @change="onChange" + /> +</template> diff --git a/app/assets/javascripts/feature_flags/constants.js b/app/assets/javascripts/feature_flags/constants.js index f59414ab1a7..79bd6d8fe43 100644 --- a/app/assets/javascripts/feature_flags/constants.js +++ b/app/assets/javascripts/feature_flags/constants.js @@ -26,3 +26,24 @@ export const NEW_FLAG_ALERT = s__( export const FEATURE_FLAG_SCOPE = 'featureFlags'; export const USER_LIST_SCOPE = 'userLists'; + +export const EMPTY_PARAMETERS = { parameters: {}, userListId: undefined }; + +export const STRATEGY_SELECTIONS = [ + { + value: ROLLOUT_STRATEGY_ALL_USERS, + text: s__('FeatureFlags|All users'), + }, + { + value: ROLLOUT_STRATEGY_PERCENT_ROLLOUT, + text: s__('FeatureFlags|Percent of users'), + }, + { + value: ROLLOUT_STRATEGY_USER_ID, + text: s__('FeatureFlags|User IDs'), + }, + { + value: ROLLOUT_STRATEGY_GITLAB_USER_LIST, + text: s__('FeatureFlags|User List'), + }, +]; diff --git a/app/assets/javascripts/feature_flags/store/modules/helpers.js b/app/assets/javascripts/feature_flags/store/modules/helpers.js index 5a8d7bc6af3..bbde3cad7cd 100644 --- a/app/assets/javascripts/feature_flags/store/modules/helpers.js +++ b/app/assets/javascripts/feature_flags/store/modules/helpers.js @@ -182,7 +182,7 @@ export const mapStrategiesToViewModel = strategiesFromRails => const mapStrategiesParametersToRails = params => { if (params.userIds) { - return { ...params, userIds: params.userIds.split(', ').join(',') }; + return { ...params, userIds: params.userIds.replace(/\s*,\s*/g, ',') }; } return params; }; diff --git a/app/assets/javascripts/feature_flags/utils.js b/app/assets/javascripts/feature_flags/utils.js index 1017a3d0c2a..ccb6ac17792 100644 --- a/app/assets/javascripts/feature_flags/utils.js +++ b/app/assets/javascripts/feature_flags/utils.js @@ -30,7 +30,7 @@ const badgeTextByType = { const scopeName = ({ environment_scope: scope }) => scope === ALL_ENVIRONMENTS_NAME ? s__('FeatureFlags|All Environments') : scope; -export default strategy => { +export const labelForStrategy = strategy => { const { name, parameters } = badgeTextByType[strategy.name]; if (parameters) { |