2021-01-03 14:25:43 +05:30
< script >
2021-09-30 23:02:18 +05:30
import { GlButton } from '@gitlab/ui' ;
import { memoize , cloneDeep , isNumber , uniqueId } from 'lodash' ;
2021-03-11 19:13:27 +05:30
import Vue from 'vue' ;
2021-01-03 14:25:43 +05:30
import { s _ _ } from '~/locale' ;
2021-03-11 19:13:27 +05:30
import RelatedIssuesRoot from '~/related_issues/components/related_issues_root.vue' ;
2021-01-03 14:25:43 +05:30
import featureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin' ;
import {
ROLLOUT _STRATEGY _ALL _USERS ,
ROLLOUT _STRATEGY _PERCENT _ROLLOUT ,
ROLLOUT _STRATEGY _USER _ID ,
ALL _ENVIRONMENTS _NAME ,
NEW _VERSION _FLAG ,
} from '../constants' ;
2021-03-11 19:13:27 +05:30
import Strategy from './strategy.vue' ;
2021-01-03 14:25:43 +05:30
export default {
2021-04-29 21:17:54 +05:30
i18n : {
removeLabel : s _ _ ( 'FeatureFlags|Remove' ) ,
statusLabel : s _ _ ( 'FeatureFlags|Status' ) ,
} ,
2021-01-03 14:25:43 +05:30
components : {
GlButton ,
Strategy ,
RelatedIssuesRoot ,
} ,
mixins : [ featureFlagsMixin ( ) ] ,
2021-03-08 18:12:59 +05:30
inject : {
featureFlagIssuesEndpoint : {
default : '' ,
} ,
} ,
2021-01-03 14:25:43 +05:30
props : {
active : {
type : Boolean ,
required : false ,
default : true ,
} ,
name : {
type : String ,
required : false ,
default : '' ,
} ,
description : {
type : String ,
required : false ,
default : '' ,
} ,
cancelPath : {
type : String ,
required : true ,
} ,
submitText : {
type : String ,
required : true ,
} ,
strategies : {
type : Array ,
required : false ,
default : ( ) => [ ] ,
} ,
} ,
translations : {
allEnvironmentsText : s _ _ ( 'FeatureFlags|* (All Environments)' ) ,
helpText : s _ _ (
'FeatureFlags|Feature Flag behavior is built up by creating a set of rules to define the status of target environments. A default wildcard rule %{codeStart}*%{codeEnd} for %{boldStart}All Environments%{boldEnd} is set, and you are able to add as many rules as you need by choosing environment specs below. You can toggle the behavior for each of your rules to set them %{boldStart}Active%{boldEnd} or %{boldStart}Inactive%{boldEnd}.' ,
) ,
newHelpText : s _ _ (
'FeatureFlags|Enable features for specific users and environments by configuring feature flag strategies.' ,
) ,
noStrategiesText : s _ _ ( 'FeatureFlags|Feature Flag has no strategies' ) ,
} ,
ROLLOUT _STRATEGY _ALL _USERS ,
ROLLOUT _STRATEGY _PERCENT _ROLLOUT ,
ROLLOUT _STRATEGY _USER _ID ,
// Matches numbers 0 through 100
rolloutPercentageRegex : /^[0-9]$|^[1-9][0-9]$|^100$/ ,
data ( ) {
return {
formName : this . name ,
formDescription : this . description ,
formStrategies : cloneDeep ( this . strategies ) ,
newScope : '' ,
} ;
} ,
computed : {
filteredStrategies ( ) {
2021-03-08 18:12:59 +05:30
return this . formStrategies . filter ( ( s ) => ! s . shouldBeDestroyed ) ;
2021-01-03 14:25:43 +05:30
} ,
showRelatedIssues ( ) {
2021-11-18 22:05:49 +05:30
return Boolean ( this . featureFlagIssuesEndpoint ) ;
2021-01-03 14:25:43 +05:30
} ,
} ,
methods : {
keyFor ( strategy ) {
if ( strategy . id ) {
return strategy . id ;
}
return uniqueId ( 'strategy_' ) ;
} ,
addStrategy ( ) {
this . formStrategies . push ( { name : ROLLOUT _STRATEGY _ALL _USERS , parameters : { } , scopes : [ ] } ) ;
} ,
deleteStrategy ( s ) {
if ( isNumber ( s . id ) ) {
Vue . set ( s , 'shouldBeDestroyed' , true ) ;
} else {
2021-03-08 18:12:59 +05:30
this . formStrategies = this . formStrategies . filter ( ( strategy ) => strategy !== s ) ;
2021-01-03 14:25:43 +05:30
}
} ,
isAllEnvironment ( name ) {
return name === ALL _ENVIRONMENTS _NAME ;
} ,
/ * *
* When the user clicks the submit button
* it triggers an event with the form data
* /
handleSubmit ( ) {
const flag = {
name : this . formName ,
description : this . formDescription ,
active : this . active ,
2021-09-30 23:02:18 +05:30
version : NEW _VERSION _FLAG ,
strategies : this . formStrategies ,
2021-01-03 14:25:43 +05:30
} ;
this . $emit ( 'handleSubmit' , flag ) ;
} ,
isRolloutPercentageInvalid : memoize ( function isRolloutPercentageInvalid ( percentage ) {
return ! this . $options . rolloutPercentageRegex . test ( percentage ) ;
} ) ,
onFormStrategyChange ( strategy , index ) {
2022-01-26 12:08:38 +05:30
const currentUserListId = this . filteredStrategies [ index ] ? . userList ? . id ;
const newUserListId = strategy ? . userList ? . id ;
2021-01-03 14:25:43 +05:30
Object . assign ( this . filteredStrategies [ index ] , strategy ) ;
2022-01-26 12:08:38 +05:30
if ( currentUserListId !== newUserListId ) {
this . formStrategies = [ ... this . formStrategies ] ;
}
2021-01-03 14:25:43 +05:30
} ,
} ,
} ;
< / script >
< template >
< form class = "feature-flags-form" >
< fieldset >
< div class = "row" >
< div class = "form-group col-md-4" >
< label for = "feature-flag-name" class = "label-bold" > { { s _ _ ( 'FeatureFlags|Name' ) } } * < / label >
2021-09-30 23:02:18 +05:30
< input id = "feature-flag-name" v-model = "formName" class="form-control" / >
2021-01-03 14:25:43 +05:30
< / div >
< / div >
< div class = "row" >
< div class = "form-group col-md-4" >
< label for = "feature-flag-description" class = "label-bold" >
{ { s _ _ ( 'FeatureFlags|Description' ) } }
< / label >
< textarea
id = "feature-flag-description"
v - model = "formDescription"
class = "form-control"
rows = "4"
> < / textarea >
< / div >
< / div >
< related-issues-root
v - if = "showRelatedIssues"
: endpoint = "featureFlagIssuesEndpoint"
: can - admin = "true"
: show - categorized - issues = "false"
/ >
2021-09-30 23:02:18 +05:30
< div class = "row" >
< div class = "col-md-12" >
< h4 > { { s _ _ ( 'FeatureFlags|Strategies' ) } } < / h4 >
< div class = "flex align-items-baseline justify-content-between" >
< p class = "mr-3" > { { $options . translations . newHelpText } } < / p >
< gl-button variant = "confirm" category = "secondary" @click ="addStrategy" >
{ { s _ _ ( 'FeatureFlags|Add strategy' ) } }
< / gl-button >
2021-01-03 14:25:43 +05:30
< / div >
< / div >
< / div >
2021-09-30 23:02:18 +05:30
< div v-if = "filteredStrategies.length > 0" data-testid="feature-flag-strategies" >
< strategy
v - for = "(strategy, index) in filteredStrategies"
: key = "keyFor(strategy)"
: strategy = "strategy"
: index = "index"
@ change = "onFormStrategyChange($event, index)"
@ delete = "deleteStrategy(strategy)"
/ >
< / div >
< div v -else class = "flex justify-content-center border-top py-4 w-100" >
< span > { { $options . translations . noStrategiesText } } < / span >
< / div >
2021-01-03 14:25:43 +05:30
< / fieldset >
< div class = "form-actions" >
< gl-button
ref = "submitButton"
type = "button"
2021-04-29 21:17:54 +05:30
variant = "confirm"
2021-01-03 14:25:43 +05:30
class = "js-ff-submit col-xs-12"
@ click = "handleSubmit"
> { { submitText } } < / g l - b u t t o n
>
< gl-button :href = "cancelPath" class = "js-ff-cancel col-xs-12 float-right" >
{ { _ _ ( 'Cancel' ) } }
< / gl-button >
< / div >
< / form >
< / template >